diff --git a/HBC/META.XML b/HBC/META.XML index 69d9a586..b9fc1c84 100644 --- a/HBC/META.XML +++ b/HBC/META.XML @@ -2,8 +2,8 @@ USB Loader GX USB Loader GX Team - 1.0 r953 - 201009190727 + 1.0 r957 + 201009191357 Loads games from USB-devices USB Loader GX is a libwiigui based USB iso loader with a wii-like GUI. You can install games to your HDDs and boot them with shorter loading times. The interactive GUI is completely controllable with WiiMote, Classic Controller or GC Controller. diff --git a/Makefile b/Makefile index 625367c1..3a574042 100644 --- a/Makefile +++ b/Makefile @@ -41,15 +41,9 @@ SOURCES := source \ source/libfat \ source/memory \ source/libntfs \ - source/usbloader/wbfs \ -# source/libhfs+ \ -# source/libhfs+/hfscommon/BTree \ -# source/libhfs+/hfscommon/Catalog \ -# source/libhfs+/hfscommon/Misc \ -# source/libhfs+/hfscommon/Unicode + source/usbloader/wbfs DATA := data -INCLUDES := source -#source/libhfs+/hfscommon/headers +INCLUDES := source #--------------------------------------------------------------------------------- # options for code generation diff --git a/source/fatmounter.c b/source/fatmounter.c index f0858100..62e671ea 100644 --- a/source/fatmounter.c +++ b/source/fatmounter.c @@ -9,8 +9,8 @@ #include "usbloader/usbstorage2.h" #include "usbloader/sdhc.h" #include "usbloader/wbfs.h" -#include "libfat/fat.h" #include "libntfs/ntfs.h" +#include "libfat/fat.h" #include "gecko.h" //these are the only stable and speed is good @@ -215,7 +215,7 @@ s32 MountNTFS( u32 sector ) // } /* Mount device */ // if (!ntfsMount("NTFS", &__io_wiiums, sector, CACHE, SECTORS, NTFS_SHOW_HIDDEN_FILES | NTFS_RECOVER)) { - ret = ntfsMount( "NTFS", &__io_usbstorage2, sector, CACHE, SECTORS, NTFS_SHOW_HIDDEN_FILES | NTFS_READ_ONLY | NTFS_RECOVER ); + ret = ntfsMount( "NTFS", &__io_usbstorage2, sector, CACHE, SECTORS, NTFS_SHOW_HIDDEN_FILES | NTFS_RECOVER ); if ( !ret ) { return -2; @@ -226,11 +226,11 @@ s32 MountNTFS( u32 sector ) { if ( sdhc_mode_sd == 0 ) { - ret = ntfsMount( "NTFS", &__io_sdhc, 0, CACHE, SECTORS, NTFS_SHOW_HIDDEN_FILES | NTFS_READ_ONLY | NTFS_RECOVER ); + ret = ntfsMount( "NTFS", &__io_sdhc, 0, CACHE, SECTORS, NTFS_SHOW_HIDDEN_FILES | NTFS_RECOVER ); } else { - ret = ntfsMount( "NTFS", &__io_sdhc, 0, CACHE, SECTORS_SD, NTFS_SHOW_HIDDEN_FILES | NTFS_READ_ONLY | NTFS_RECOVER ); + ret = ntfsMount( "NTFS", &__io_sdhc, 0, CACHE, SECTORS_SD, NTFS_SHOW_HIDDEN_FILES | NTFS_RECOVER ); } if ( !ret ) { diff --git a/source/libntfs/acls.c b/source/libntfs/acls.c index eaf47ee7..76cc6ce5 100644 --- a/source/libntfs/acls.c +++ b/source/libntfs/acls.c @@ -1,8 +1,8 @@ /** * acls.c - General function to process NTFS ACLs * - * This module is part of ntfs-3g library, but may also be - * integrated in tools running over Linux or Windows + * This module is part of ntfs-3g library, but may also be + * integrated in tools running over Linux or Windows * * Copyright (c) 2007-2009 Jean-Pierre Andre * @@ -23,9 +23,9 @@ */ #ifdef HAVE_CONFIG_H -/* - * integration into ntfs-3g - */ + /* + * integration into ntfs-3g + */ #include "config.h" #ifdef HAVE_STDIO_H @@ -60,10 +60,10 @@ #include "misc.h" #else -/* - * integration into secaudit, check whether Win32, - * may have to be adapted to compiler or something else - */ + /* + * integration into secaudit, check whether Win32, + * may have to be adapted to compiler or something else + */ #ifndef WIN32 #if defined(__WIN32) | defined(__WIN32__) | defined(WNSC) @@ -79,26 +79,26 @@ #include #include -/* - * integration into secaudit/Win32 - */ + /* + * integration into secaudit/Win32 + */ #ifdef WIN32 #include #include #define __LITTLE_ENDIAN 1234 #define __BYTE_ORDER __LITTLE_ENDIAN #else -/* - * integration into secaudit/STSC - */ + /* + * integration into secaudit/STSC + */ #ifdef STSC #include #undef __BYTE_ORDER #define __BYTE_ORDER __BIG_ENDIAN #else -/* - * integration into secaudit/Linux - */ + /* + * integration into secaudit/Linux + */ #include #include #include @@ -109,3594 +109,3313 @@ #endif /* HAVE_CONFIG_H */ /* - * A few useful constants + * A few useful constants */ /* - * null SID (S-1-0-0) + * null SID (S-1-0-0) */ -static const char nullsidbytes[] = -{ - 1, /* revision */ - 1, /* auth count */ - 0, 0, 0, 0, 0, 0, /* base */ - 0, 0, 0, 0 /* 1st level */ -}; +static const char nullsidbytes[] = { + 1, /* revision */ + 1, /* auth count */ + 0, 0, 0, 0, 0, 0, /* base */ + 0, 0, 0, 0 /* 1st level */ + }; -static const SID *nullsid = ( const SID* )nullsidbytes; +static const SID *nullsid = (const SID*)nullsidbytes; /* - * SID for world (S-1-1-0) + * SID for world (S-1-1-0) */ -static const char worldsidbytes[] = -{ - 1, /* revision */ - 1, /* auth count */ - 0, 0, 0, 0, 0, 1, /* base */ - 0, 0, 0, 0 /* 1st level */ +static const char worldsidbytes[] = { + 1, /* revision */ + 1, /* auth count */ + 0, 0, 0, 0, 0, 1, /* base */ + 0, 0, 0, 0 /* 1st level */ } ; -const SID *worldsid = ( const SID* )worldsidbytes; +const SID *worldsid = (const SID*)worldsidbytes; /* - * SID for administrator + * SID for administrator */ -static const char adminsidbytes[] = -{ - 1, /* revision */ - 2, /* auth count */ - 0, 0, 0, 0, 0, 5, /* base */ - 32, 0, 0, 0, /* 1st level */ - 32, 2, 0, 0 /* 2nd level */ +static const char adminsidbytes[] = { + 1, /* revision */ + 2, /* auth count */ + 0, 0, 0, 0, 0, 5, /* base */ + 32, 0, 0, 0, /* 1st level */ + 32, 2, 0, 0 /* 2nd level */ }; -const SID *adminsid = ( const SID* )adminsidbytes; +const SID *adminsid = (const SID*)adminsidbytes; /* - * SID for system + * SID for system */ -static const char systemsidbytes[] = -{ - 1, /* revision */ - 1, /* auth count */ - 0, 0, 0, 0, 0, 5, /* base */ - 18, 0, 0, 0 /* 1st level */ -}; +static const char systemsidbytes[] = { + 1, /* revision */ + 1, /* auth count */ + 0, 0, 0, 0, 0, 5, /* base */ + 18, 0, 0, 0 /* 1st level */ + }; -static const SID *systemsid = ( const SID* )systemsidbytes; +static const SID *systemsid = (const SID*)systemsidbytes; /* - * SID for generic creator-owner - * S-1-3-0 + * SID for generic creator-owner + * S-1-3-0 */ -static const char ownersidbytes[] = -{ - 1, /* revision */ - 1, /* auth count */ - 0, 0, 0, 0, 0, 3, /* base */ - 0, 0, 0, 0 /* 1st level */ +static const char ownersidbytes[] = { + 1, /* revision */ + 1, /* auth count */ + 0, 0, 0, 0, 0, 3, /* base */ + 0, 0, 0, 0 /* 1st level */ } ; -static const SID *ownersid = ( const SID* )ownersidbytes; +static const SID *ownersid = (const SID*)ownersidbytes; /* - * SID for generic creator-group - * S-1-3-1 + * SID for generic creator-group + * S-1-3-1 */ -static const char groupsidbytes[] = -{ - 1, /* revision */ - 1, /* auth count */ - 0, 0, 0, 0, 0, 3, /* base */ - 1, 0, 0, 0 /* 1st level */ +static const char groupsidbytes[] = { + 1, /* revision */ + 1, /* auth count */ + 0, 0, 0, 0, 0, 3, /* base */ + 1, 0, 0, 0 /* 1st level */ } ; -static const SID *groupsid = ( const SID* )groupsidbytes; +static const SID *groupsid = (const SID*)groupsidbytes; /* - * Determine the size of a SID + * Determine the size of a SID */ -int ntfs_sid_size( const SID * sid ) +int ntfs_sid_size(const SID * sid) { - return ( sid->sub_authority_count * 4 + 8 ); + return (sid->sub_authority_count * 4 + 8); } /* - * Test whether two SID are equal + * Test whether two SID are equal */ -BOOL ntfs_same_sid( const SID *first, const SID *second ) +BOOL ntfs_same_sid(const SID *first, const SID *second) { - int size; + int size; - size = ntfs_sid_size( first ); - return ( ( ntfs_sid_size( second ) == size ) - && !memcmp( first, second, size ) ); + size = ntfs_sid_size(first); + return ((ntfs_sid_size(second) == size) + && !memcmp(first, second, size)); } /* - * Test whether a SID means "world user" - * Local users group also recognized as world + * Test whether a SID means "world user" + * Local users group also recognized as world */ -static int is_world_sid( const SID * usid ) +static int is_world_sid(const SID * usid) { - return ( - /* check whether S-1-1-0 : world */ - ( ( usid->sub_authority_count == 1 ) - && ( usid->identifier_authority.high_part == const_cpu_to_be16( 0 ) ) - && ( usid->identifier_authority.low_part == const_cpu_to_be32( 1 ) ) - && ( usid->sub_authority[0] == const_cpu_to_le32( 0 ) ) ) + return ( + /* check whether S-1-1-0 : world */ + ((usid->sub_authority_count == 1) + && (usid->identifier_authority.high_part == const_cpu_to_be16(0)) + && (usid->identifier_authority.low_part == const_cpu_to_be32(1)) + && (usid->sub_authority[0] == const_cpu_to_le32(0))) - /* check whether S-1-5-32-545 : local user */ - || ( ( usid->sub_authority_count == 2 ) - && ( usid->identifier_authority.high_part == const_cpu_to_be16( 0 ) ) - && ( usid->identifier_authority.low_part == const_cpu_to_be32( 5 ) ) - && ( usid->sub_authority[0] == const_cpu_to_le32( 32 ) ) - && ( usid->sub_authority[1] == const_cpu_to_le32( 545 ) ) ) - ); + /* check whether S-1-5-32-545 : local user */ + || ((usid->sub_authority_count == 2) + && (usid->identifier_authority.high_part == const_cpu_to_be16(0)) + && (usid->identifier_authority.low_part == const_cpu_to_be32(5)) + && (usid->sub_authority[0] == const_cpu_to_le32(32)) + && (usid->sub_authority[1] == const_cpu_to_le32(545))) + ); } /* - * Test whether a SID means "some user (or group)" - * Currently we only check for S-1-5-21... but we should - * probably test for other configurations + * Test whether a SID means "some user (or group)" + * Currently we only check for S-1-5-21... but we should + * probably test for other configurations */ -BOOL ntfs_is_user_sid( const SID *usid ) +BOOL ntfs_is_user_sid(const SID *usid) { - return ( ( usid->sub_authority_count == 5 ) - && ( usid->identifier_authority.high_part == const_cpu_to_be16( 0 ) ) - && ( usid->identifier_authority.low_part == const_cpu_to_be32( 5 ) ) - && ( usid->sub_authority[0] == const_cpu_to_le32( 21 ) ) ); + return ((usid->sub_authority_count == 5) + && (usid->identifier_authority.high_part == const_cpu_to_be16(0)) + && (usid->identifier_authority.low_part == const_cpu_to_be32(5)) + && (usid->sub_authority[0] == const_cpu_to_le32(21))); } /* - * Determine the size of a security attribute - * whatever the order of fields + * Determine the size of a security attribute + * whatever the order of fields */ -unsigned int ntfs_attr_size( const char *attr ) +unsigned int ntfs_attr_size(const char *attr) { - const SECURITY_DESCRIPTOR_RELATIVE *phead; - const ACL *pdacl; - const ACL *psacl; - const SID *psid; - unsigned int offdacl; - unsigned int offsacl; - unsigned int offowner; - unsigned int offgroup; - unsigned int endsid; - unsigned int endacl; - unsigned int attrsz; + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const ACL *pdacl; + const ACL *psacl; + const SID *psid; + unsigned int offdacl; + unsigned int offsacl; + unsigned int offowner; + unsigned int offgroup; + unsigned int endsid; + unsigned int endacl; + unsigned int attrsz; - phead = ( const SECURITY_DESCRIPTOR_RELATIVE* )attr; - /* - * First check group, which is the last field in all descriptors - * we build, and in most descriptors built by Windows - */ - attrsz = sizeof( SECURITY_DESCRIPTOR_RELATIVE ); - offgroup = le32_to_cpu( phead->group ); - if ( offgroup >= attrsz ) - { - /* find end of GSID */ - psid = ( const SID* ) & attr[offgroup]; - endsid = offgroup + ntfs_sid_size( psid ); - if ( endsid > attrsz ) attrsz = endsid; - } - offowner = le32_to_cpu( phead->owner ); - if ( offowner >= attrsz ) - { - /* find end of USID */ - psid = ( const SID* ) & attr[offowner]; - endsid = offowner + ntfs_sid_size( psid ); - attrsz = endsid; - } - offsacl = le32_to_cpu( phead->sacl ); - if ( offsacl >= attrsz ) - { - /* find end of SACL */ - psacl = ( const ACL* ) & attr[offsacl]; - endacl = offsacl + le16_to_cpu( psacl->size ); - if ( endacl > attrsz ) - attrsz = endacl; - } + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr; + /* + * First check group, which is the last field in all descriptors + * we build, and in most descriptors built by Windows + */ + attrsz = sizeof(SECURITY_DESCRIPTOR_RELATIVE); + offgroup = le32_to_cpu(phead->group); + if (offgroup >= attrsz) { + /* find end of GSID */ + psid = (const SID*)&attr[offgroup]; + endsid = offgroup + ntfs_sid_size(psid); + if (endsid > attrsz) attrsz = endsid; + } + offowner = le32_to_cpu(phead->owner); + if (offowner >= attrsz) { + /* find end of USID */ + psid = (const SID*)&attr[offowner]; + endsid = offowner + ntfs_sid_size(psid); + attrsz = endsid; + } + offsacl = le32_to_cpu(phead->sacl); + if (offsacl >= attrsz) { + /* find end of SACL */ + psacl = (const ACL*)&attr[offsacl]; + endacl = offsacl + le16_to_cpu(psacl->size); + if (endacl > attrsz) + attrsz = endacl; + } - /* find end of DACL */ - offdacl = le32_to_cpu( phead->dacl ); - if ( offdacl >= attrsz ) - { - pdacl = ( const ACL* ) & attr[offdacl]; - endacl = offdacl + le16_to_cpu( pdacl->size ); - if ( endacl > attrsz ) - attrsz = endacl; - } - return ( attrsz ); + /* find end of DACL */ + offdacl = le32_to_cpu(phead->dacl); + if (offdacl >= attrsz) { + pdacl = (const ACL*)&attr[offdacl]; + endacl = offdacl + le16_to_cpu(pdacl->size); + if (endacl > attrsz) + attrsz = endacl; + } + return (attrsz); } /* - * Do sanity checks on a SID read from storage - * (just check revision and number of authorities) + * Do sanity checks on a SID read from storage + * (just check revision and number of authorities) */ -BOOL ntfs_valid_sid( const SID *sid ) +BOOL ntfs_valid_sid(const SID *sid) { - return ( ( sid->revision == SID_REVISION ) - && ( sid->sub_authority_count >= 1 ) - && ( sid->sub_authority_count <= 8 ) ); + return ((sid->revision == SID_REVISION) + && (sid->sub_authority_count >= 1) + && (sid->sub_authority_count <= 8)); } /* - * Check whether a SID is acceptable for an implicit - * mapping pattern. - * It should have been already checked it is a valid user SID. + * Check whether a SID is acceptable for an implicit + * mapping pattern. + * It should have been already checked it is a valid user SID. * - * The last authority reference has to be >= 1000 (Windows usage) - * and <= 0x7fffffff, so that 30 bits from a uid and 30 more bits + * The last authority reference has to be >= 1000 (Windows usage) + * and <= 0x7fffffff, so that 30 bits from a uid and 30 more bits * from a gid an be inserted with no overflow. */ -BOOL ntfs_valid_pattern( const SID *sid ) +BOOL ntfs_valid_pattern(const SID *sid) { - int cnt; - u32 auth; - le32 leauth; + int cnt; + u32 auth; + le32 leauth; - cnt = sid->sub_authority_count; - leauth = sid->sub_authority[cnt-1]; - auth = le32_to_cpu( leauth ); - return ( ( auth >= 1000 ) && ( auth <= 0x7fffffff ) ); + cnt = sid->sub_authority_count; + leauth = sid->sub_authority[cnt-1]; + auth = le32_to_cpu(leauth); + return ((auth >= 1000) && (auth <= 0x7fffffff)); } /* - * Compute the uid or gid associated to a SID - * through an implicit mapping + * Compute the uid or gid associated to a SID + * through an implicit mapping * - * Returns 0 (root) if it does not match pattern + * Returns 0 (root) if it does not match pattern */ -static u32 findimplicit( const SID *xsid, const SID *pattern, int parity ) +static u32 findimplicit(const SID *xsid, const SID *pattern, int parity) { - BIGSID defsid; - SID *psid; - u32 xid; /* uid or gid */ - int cnt; - u32 carry; - le32 leauth; - u32 uauth; - u32 xlast; - u32 rlast; + BIGSID defsid; + SID *psid; + u32 xid; /* uid or gid */ + int cnt; + u32 carry; + le32 leauth; + u32 uauth; + u32 xlast; + u32 rlast; - memcpy( &defsid, pattern, ntfs_sid_size( pattern ) ); - psid = ( SID* ) & defsid; - cnt = psid->sub_authority_count; - xid = 0; - if ( xsid->sub_authority_count == cnt ) - { - psid->sub_authority[cnt-1] = xsid->sub_authority[cnt-1]; - leauth = xsid->sub_authority[cnt-1]; - xlast = le32_to_cpu( leauth ); - leauth = pattern->sub_authority[cnt-1]; - rlast = le32_to_cpu( leauth ); + memcpy(&defsid,pattern,ntfs_sid_size(pattern)); + psid = (SID*)&defsid; + cnt = psid->sub_authority_count; + xid = 0; + if (xsid->sub_authority_count == cnt) { + psid->sub_authority[cnt-1] = xsid->sub_authority[cnt-1]; + leauth = xsid->sub_authority[cnt-1]; + xlast = le32_to_cpu(leauth); + leauth = pattern->sub_authority[cnt-1]; + rlast = le32_to_cpu(leauth); - if ( ( xlast > rlast ) && !( ( xlast ^ rlast ^ parity ) & 1 ) ) - { - /* direct check for basic situation */ - if ( ntfs_same_sid( psid, xsid ) ) - xid = ( ( xlast - rlast ) >> 1 ) & 0x3fffffff; - else - { - /* - * check whether part of mapping had to be - * recorded in a higher level authority - */ - carry = 1; - do - { - leauth = psid->sub_authority[cnt-2]; - uauth = le32_to_cpu( leauth ) + 1; - psid->sub_authority[cnt-2] - = cpu_to_le32( uauth ); - } - while ( !ntfs_same_sid( psid, xsid ) - && ( ++carry < 4 ) ); - if ( carry < 4 ) - xid = ( ( ( xlast - rlast ) >> 1 ) - & 0x3fffffff ) | ( carry << 30 ); - } - } - } - return ( xid ); + if ((xlast > rlast) && !((xlast ^ rlast ^ parity) & 1)) { + /* direct check for basic situation */ + if (ntfs_same_sid(psid,xsid)) + xid = ((xlast - rlast) >> 1) & 0x3fffffff; + else { + /* + * check whether part of mapping had to be + * recorded in a higher level authority + */ + carry = 1; + do { + leauth = psid->sub_authority[cnt-2]; + uauth = le32_to_cpu(leauth) + 1; + psid->sub_authority[cnt-2] + = cpu_to_le32(uauth); + } while (!ntfs_same_sid(psid,xsid) + && (++carry < 4)); + if (carry < 4) + xid = (((xlast - rlast) >> 1) + & 0x3fffffff) | (carry << 30); + } + } + } + return (xid); } /* - * Find usid mapped to a Linux user - * Returns NULL if not found + * Find usid mapped to a Linux user + * Returns NULL if not found */ -const SID *ntfs_find_usid( const struct MAPPING* usermapping, - uid_t uid, SID *defusid ) +const SID *ntfs_find_usid(const struct MAPPING* usermapping, + uid_t uid, SID *defusid) { - const struct MAPPING *p; - const SID *sid; - le32 leauth; - u32 uauth; - int cnt; + const struct MAPPING *p; + const SID *sid; + le32 leauth; + u32 uauth; + int cnt; - if ( !uid ) - sid = adminsid; - else - { - p = usermapping; - while ( p && p->xid && ( ( uid_t )p->xid != uid ) ) - p = p->next; - if ( p && !p->xid ) - { - /* - * default pattern has been reached : - * build an implicit SID according to pattern - * (the pattern format was checked while reading - * the mapping file) - */ - memcpy( defusid, p->sid, ntfs_sid_size( p->sid ) ); - cnt = defusid->sub_authority_count; - leauth = defusid->sub_authority[cnt-1]; - uauth = le32_to_cpu( leauth ) + 2 * ( uid & 0x3fffffff ); - defusid->sub_authority[cnt-1] = cpu_to_le32( uauth ); - if ( uid & 0xc0000000 ) - { - leauth = defusid->sub_authority[cnt-2]; - uauth = le32_to_cpu( leauth ) + ( ( uid >> 30 ) & 3 ); - defusid->sub_authority[cnt-2] = cpu_to_le32( uauth ); - } - sid = defusid; - } - else - sid = ( p ? p->sid : ( const SID* )NULL ); - } - return ( sid ); + if (!uid) + sid = adminsid; + else { + p = usermapping; + while (p && p->xid && ((uid_t)p->xid != uid)) + p = p->next; + if (p && !p->xid) { + /* + * default pattern has been reached : + * build an implicit SID according to pattern + * (the pattern format was checked while reading + * the mapping file) + */ + memcpy(defusid, p->sid, ntfs_sid_size(p->sid)); + cnt = defusid->sub_authority_count; + leauth = defusid->sub_authority[cnt-1]; + uauth = le32_to_cpu(leauth) + 2*(uid & 0x3fffffff); + defusid->sub_authority[cnt-1] = cpu_to_le32(uauth); + if (uid & 0xc0000000) { + leauth = defusid->sub_authority[cnt-2]; + uauth = le32_to_cpu(leauth) + ((uid >> 30) & 3); + defusid->sub_authority[cnt-2] = cpu_to_le32(uauth); + } + sid = defusid; + } else + sid = (p ? p->sid : (const SID*)NULL); + } + return (sid); } /* - * Find Linux group mapped to a gsid - * Returns 0 (root) if not found + * Find Linux group mapped to a gsid + * Returns 0 (root) if not found */ -const SID *ntfs_find_gsid( const struct MAPPING* groupmapping, - gid_t gid, SID *defgsid ) +const SID *ntfs_find_gsid(const struct MAPPING* groupmapping, + gid_t gid, SID *defgsid) { - const struct MAPPING *p; - const SID *sid; - le32 leauth; - u32 uauth; - int cnt; + const struct MAPPING *p; + const SID *sid; + le32 leauth; + u32 uauth; + int cnt; - if ( !gid ) - sid = adminsid; - else - { - p = groupmapping; - while ( p && p->xid && ( ( gid_t )p->xid != gid ) ) - p = p->next; - if ( p && !p->xid ) - { - /* - * default pattern has been reached : - * build an implicit SID according to pattern - * (the pattern format was checked while reading - * the mapping file) - */ - memcpy( defgsid, p->sid, ntfs_sid_size( p->sid ) ); - cnt = defgsid->sub_authority_count; - leauth = defgsid->sub_authority[cnt-1]; - uauth = le32_to_cpu( leauth ) + 2 * ( gid & 0x3fffffff ) + 1; - defgsid->sub_authority[cnt-1] = cpu_to_le32( uauth ); - if ( gid & 0xc0000000 ) - { - leauth = defgsid->sub_authority[cnt-2]; - uauth = le32_to_cpu( leauth ) + ( ( gid >> 30 ) & 3 ); - defgsid->sub_authority[cnt-2] = cpu_to_le32( uauth ); - } - sid = defgsid; - } - else - sid = ( p ? p->sid : ( const SID* )NULL ); - } - return ( sid ); + if (!gid) + sid = adminsid; + else { + p = groupmapping; + while (p && p->xid && ((gid_t)p->xid != gid)) + p = p->next; + if (p && !p->xid) { + /* + * default pattern has been reached : + * build an implicit SID according to pattern + * (the pattern format was checked while reading + * the mapping file) + */ + memcpy(defgsid, p->sid, ntfs_sid_size(p->sid)); + cnt = defgsid->sub_authority_count; + leauth = defgsid->sub_authority[cnt-1]; + uauth = le32_to_cpu(leauth) + 2*(gid & 0x3fffffff) + 1; + defgsid->sub_authority[cnt-1] = cpu_to_le32(uauth); + if (gid & 0xc0000000) { + leauth = defgsid->sub_authority[cnt-2]; + uauth = le32_to_cpu(leauth) + ((gid >> 30) & 3); + defgsid->sub_authority[cnt-2] = cpu_to_le32(uauth); + } + sid = defgsid; + } else + sid = (p ? p->sid : (const SID*)NULL); + } + return (sid); } /* - * Find Linux owner mapped to a usid - * Returns 0 (root) if not found + * Find Linux owner mapped to a usid + * Returns 0 (root) if not found */ -uid_t ntfs_find_user( const struct MAPPING* usermapping, const SID *usid ) +uid_t ntfs_find_user(const struct MAPPING* usermapping, const SID *usid) { - uid_t uid; - const struct MAPPING *p; + uid_t uid; + const struct MAPPING *p; - p = usermapping; - while ( p && p->xid && !ntfs_same_sid( usid, p->sid ) ) - p = p->next; - if ( p && !p->xid ) - /* - * No explicit mapping found, try implicit mapping - */ - uid = findimplicit( usid, p->sid, 0 ); - else - uid = ( p ? p->xid : 0 ); - return ( uid ); + p = usermapping; + while (p && p->xid && !ntfs_same_sid(usid, p->sid)) + p = p->next; + if (p && !p->xid) + /* + * No explicit mapping found, try implicit mapping + */ + uid = findimplicit(usid,p->sid,0); + else + uid = (p ? p->xid : 0); + return (uid); } /* - * Find Linux group mapped to a gsid - * Returns 0 (root) if not found + * Find Linux group mapped to a gsid + * Returns 0 (root) if not found */ -gid_t ntfs_find_group( const struct MAPPING* groupmapping, const SID * gsid ) +gid_t ntfs_find_group(const struct MAPPING* groupmapping, const SID * gsid) { - gid_t gid; - const struct MAPPING *p; - int gsidsz; + gid_t gid; + const struct MAPPING *p; + int gsidsz; - gsidsz = ntfs_sid_size( gsid ); - p = groupmapping; - while ( p && p->xid && !ntfs_same_sid( gsid, p->sid ) ) - p = p->next; - if ( p && !p->xid ) - /* - * No explicit mapping found, try implicit mapping - */ - gid = findimplicit( gsid, p->sid, 1 ); - else - gid = ( p ? p->xid : 0 ); - return ( gid ); + gsidsz = ntfs_sid_size(gsid); + p = groupmapping; + while (p && p->xid && !ntfs_same_sid(gsid, p->sid)) + p = p->next; + if (p && !p->xid) + /* + * No explicit mapping found, try implicit mapping + */ + gid = findimplicit(gsid,p->sid,1); + else + gid = (p ? p->xid : 0); + return (gid); } /* - * Check the validity of the ACEs in a DACL or SACL + * Check the validity of the ACEs in a DACL or SACL */ -static BOOL valid_acl( const ACL *pacl, unsigned int end ) +static BOOL valid_acl(const ACL *pacl, unsigned int end) { - const ACCESS_ALLOWED_ACE *pace; - unsigned int offace; - unsigned int acecnt; - unsigned int acesz; - unsigned int nace; - BOOL ok; + const ACCESS_ALLOWED_ACE *pace; + unsigned int offace; + unsigned int acecnt; + unsigned int acesz; + unsigned int nace; + BOOL ok; - ok = TRUE; - acecnt = le16_to_cpu( pacl->ace_count ); - offace = sizeof( ACL ); - for ( nace = 0; ( nace < acecnt ) && ok; nace++ ) - { - /* be sure the beginning is within range */ - if ( ( offace + sizeof( ACCESS_ALLOWED_ACE ) ) > end ) - ok = FALSE; - else - { - pace = ( const ACCESS_ALLOWED_ACE* ) - & ( ( const char* )pacl )[offace]; - acesz = le16_to_cpu( pace->size ); - if ( ( ( offace + acesz ) > end ) - || !ntfs_valid_sid( &pace->sid ) - || ( ( ntfs_sid_size( &pace->sid ) + 8 ) != ( int )acesz ) ) - ok = FALSE; - offace += acesz; - } - } - return ( ok ); + ok = TRUE; + acecnt = le16_to_cpu(pacl->ace_count); + offace = sizeof(ACL); + for (nace = 0; (nace < acecnt) && ok; nace++) { + /* be sure the beginning is within range */ + if ((offace + sizeof(ACCESS_ALLOWED_ACE)) > end) + ok = FALSE; + else { + pace = (const ACCESS_ALLOWED_ACE*) + &((const char*)pacl)[offace]; + acesz = le16_to_cpu(pace->size); + if (((offace + acesz) > end) + || !ntfs_valid_sid(&pace->sid) + || ((ntfs_sid_size(&pace->sid) + 8) != (int)acesz)) + ok = FALSE; + offace += acesz; + } + } + return (ok); } /* - * Do sanity checks on security descriptors read from storage - * basically, we make sure that every field holds within - * allocated storage - * Should not be called with a NULL argument - * returns TRUE if considered safe - * if not, error should be logged by caller + * Do sanity checks on security descriptors read from storage + * basically, we make sure that every field holds within + * allocated storage + * Should not be called with a NULL argument + * returns TRUE if considered safe + * if not, error should be logged by caller */ -BOOL ntfs_valid_descr( const char *securattr, unsigned int attrsz ) +BOOL ntfs_valid_descr(const char *securattr, unsigned int attrsz) { - const SECURITY_DESCRIPTOR_RELATIVE *phead; - const ACL *pdacl; - const ACL *psacl; - unsigned int offdacl; - unsigned int offsacl; - unsigned int offowner; - unsigned int offgroup; - BOOL ok; + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const ACL *pdacl; + const ACL *psacl; + unsigned int offdacl; + unsigned int offsacl; + unsigned int offowner; + unsigned int offgroup; + BOOL ok; - ok = TRUE; + ok = TRUE; - /* - * first check overall size if within allocation range - * and a DACL is present - * and owner and group SID are valid - */ + /* + * first check overall size if within allocation range + * and a DACL is present + * and owner and group SID are valid + */ - phead = ( const SECURITY_DESCRIPTOR_RELATIVE* )securattr; - offdacl = le32_to_cpu( phead->dacl ); - offsacl = le32_to_cpu( phead->sacl ); - offowner = le32_to_cpu( phead->owner ); - offgroup = le32_to_cpu( phead->group ); - pdacl = ( const ACL* ) & securattr[offdacl]; - psacl = ( const ACL* ) & securattr[offsacl]; + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; + offdacl = le32_to_cpu(phead->dacl); + offsacl = le32_to_cpu(phead->sacl); + offowner = le32_to_cpu(phead->owner); + offgroup = le32_to_cpu(phead->group); + pdacl = (const ACL*)&securattr[offdacl]; + psacl = (const ACL*)&securattr[offsacl]; - /* - * size check occurs before the above pointers are used - * - * "DR Watson" standard directory on WinXP has an - * old revision and no DACL though SE_DACL_PRESENT is set - */ - if ( ( attrsz >= sizeof( SECURITY_DESCRIPTOR_RELATIVE ) ) - && ( phead->revision == SECURITY_DESCRIPTOR_REVISION ) - && ( offowner >= sizeof( SECURITY_DESCRIPTOR_RELATIVE ) ) - && ( ( offowner + 2 ) < attrsz ) - && ( offgroup >= sizeof( SECURITY_DESCRIPTOR_RELATIVE ) ) - && ( ( offgroup + 2 ) < attrsz ) - && ( !offdacl - || ( ( offdacl >= sizeof( SECURITY_DESCRIPTOR_RELATIVE ) ) - && ( offdacl + sizeof( ACL ) < attrsz ) ) ) - && ( !offsacl - || ( ( offsacl >= sizeof( SECURITY_DESCRIPTOR_RELATIVE ) ) - && ( offsacl + sizeof( ACL ) < attrsz ) ) ) - && !( phead->owner & const_cpu_to_le32( 3 ) ) - && !( phead->group & const_cpu_to_le32( 3 ) ) - && !( phead->dacl & const_cpu_to_le32( 3 ) ) - && !( phead->sacl & const_cpu_to_le32( 3 ) ) - && ( ntfs_attr_size( securattr ) <= attrsz ) - && ntfs_valid_sid( ( const SID* )&securattr[offowner] ) - && ntfs_valid_sid( ( const SID* )&securattr[offgroup] ) - /* - * if there is an ACL, as indicated by offdacl, - * require SE_DACL_PRESENT - * but "Dr Watson" has SE_DACL_PRESENT though no DACL - */ - && ( !offdacl - || ( ( phead->control & SE_DACL_PRESENT ) - && ( ( pdacl->revision == ACL_REVISION ) - || ( pdacl->revision == ACL_REVISION_DS ) ) ) ) - /* same for SACL */ - && ( !offsacl - || ( ( phead->control & SE_SACL_PRESENT ) - && ( ( psacl->revision == ACL_REVISION ) - || ( psacl->revision == ACL_REVISION_DS ) ) ) ) ) - { - /* - * Check the DACL and SACL if present - */ - if ( ( offdacl && !valid_acl( pdacl, attrsz - offdacl ) ) - || ( offsacl && !valid_acl( psacl, attrsz - offsacl ) ) ) - ok = FALSE; - } - else - ok = FALSE; - return ( ok ); + /* + * size check occurs before the above pointers are used + * + * "DR Watson" standard directory on WinXP has an + * old revision and no DACL though SE_DACL_PRESENT is set + */ + if ((attrsz >= sizeof(SECURITY_DESCRIPTOR_RELATIVE)) + && (phead->revision == SECURITY_DESCRIPTOR_REVISION) + && (offowner >= sizeof(SECURITY_DESCRIPTOR_RELATIVE)) + && ((offowner + 2) < attrsz) + && (offgroup >= sizeof(SECURITY_DESCRIPTOR_RELATIVE)) + && ((offgroup + 2) < attrsz) + && (!offdacl + || ((offdacl >= sizeof(SECURITY_DESCRIPTOR_RELATIVE)) + && (offdacl+sizeof(ACL) < attrsz))) + && (!offsacl + || ((offsacl >= sizeof(SECURITY_DESCRIPTOR_RELATIVE)) + && (offsacl+sizeof(ACL) < attrsz))) + && !(phead->owner & const_cpu_to_le32(3)) + && !(phead->group & const_cpu_to_le32(3)) + && !(phead->dacl & const_cpu_to_le32(3)) + && !(phead->sacl & const_cpu_to_le32(3)) + && (ntfs_attr_size(securattr) <= attrsz) + && ntfs_valid_sid((const SID*)&securattr[offowner]) + && ntfs_valid_sid((const SID*)&securattr[offgroup]) + /* + * if there is an ACL, as indicated by offdacl, + * require SE_DACL_PRESENT + * but "Dr Watson" has SE_DACL_PRESENT though no DACL + */ + && (!offdacl + || ((phead->control & SE_DACL_PRESENT) + && ((pdacl->revision == ACL_REVISION) + || (pdacl->revision == ACL_REVISION_DS)))) + /* same for SACL */ + && (!offsacl + || ((phead->control & SE_SACL_PRESENT) + && ((psacl->revision == ACL_REVISION) + || (psacl->revision == ACL_REVISION_DS))))) { + /* + * Check the DACL and SACL if present + */ + if ((offdacl && !valid_acl(pdacl,attrsz - offdacl)) + || (offsacl && !valid_acl(psacl,attrsz - offsacl))) + ok = FALSE; + } else + ok = FALSE; + return (ok); } /* - * Copy the inheritable parts of an ACL + * Copy the inheritable parts of an ACL * - * Returns the size of the new ACL - * or zero if nothing is inheritable + * Returns the size of the new ACL + * or zero if nothing is inheritable */ -int ntfs_inherit_acl( const ACL *oldacl, ACL *newacl, - const SID *usid, const SID *gsid, BOOL fordir ) +int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl, + const SID *usid, const SID *gsid, BOOL fordir) { - unsigned int src; - unsigned int dst; - int oldcnt; - int newcnt; - unsigned int selection; - int nace; - int acesz; - int usidsz; - int gsidsz; - const ACCESS_ALLOWED_ACE *poldace; - ACCESS_ALLOWED_ACE *pnewace; + unsigned int src; + unsigned int dst; + int oldcnt; + int newcnt; + unsigned int selection; + int nace; + int acesz; + int usidsz; + int gsidsz; + const ACCESS_ALLOWED_ACE *poldace; + ACCESS_ALLOWED_ACE *pnewace; - usidsz = ntfs_sid_size( usid ); - gsidsz = ntfs_sid_size( gsid ); + usidsz = ntfs_sid_size(usid); + gsidsz = ntfs_sid_size(gsid); - /* ACL header */ + /* ACL header */ - newacl->revision = ACL_REVISION; - newacl->alignment1 = 0; - newacl->alignment2 = const_cpu_to_le16( 0 ); - src = dst = sizeof( ACL ); + newacl->revision = ACL_REVISION; + newacl->alignment1 = 0; + newacl->alignment2 = const_cpu_to_le16(0); + src = dst = sizeof(ACL); - selection = ( fordir ? CONTAINER_INHERIT_ACE : OBJECT_INHERIT_ACE ); - newcnt = 0; - oldcnt = le16_to_cpu( oldacl->ace_count ); - for ( nace = 0; nace < oldcnt; nace++ ) - { - poldace = ( const ACCESS_ALLOWED_ACE* )( ( const char* )oldacl + src ); - acesz = le16_to_cpu( poldace->size ); - /* inheritance for access */ - if ( poldace->flags & selection ) - { - pnewace = ( ACCESS_ALLOWED_ACE* ) - ( ( char* )newacl + dst ); - memcpy( pnewace, poldace, acesz ); - /* - * Replace generic creator-owner and - * creator-group by owner and group - */ - if ( ntfs_same_sid( &pnewace->sid, ownersid ) ) - { - memcpy( &pnewace->sid, usid, usidsz ); - acesz = usidsz + 8; - pnewace->size = cpu_to_le16( acesz ); - } - if ( ntfs_same_sid( &pnewace->sid, groupsid ) ) - { - memcpy( &pnewace->sid, gsid, gsidsz ); - acesz = gsidsz + 8; - pnewace->size = cpu_to_le16( acesz ); - } - if ( pnewace->mask & GENERIC_ALL ) - { - pnewace->mask &= ~GENERIC_ALL; - if ( fordir ) - pnewace->mask |= OWNER_RIGHTS - | DIR_READ - | DIR_WRITE - | DIR_EXEC; - else - /* - * The last flag is not defined for a file, - * however Windows sets it, so do the same - */ - pnewace->mask |= OWNER_RIGHTS - | FILE_READ - | FILE_WRITE - | FILE_EXEC - | cpu_to_le32( 0x40 ); - } - /* remove inheritance flags */ - pnewace->flags &= ~( OBJECT_INHERIT_ACE - | CONTAINER_INHERIT_ACE - | INHERIT_ONLY_ACE ); - dst += acesz; - newcnt++; - } - /* inheritance for further inheritance */ - if ( fordir - && ( poldace->flags - & ( CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE ) ) ) - { - pnewace = ( ACCESS_ALLOWED_ACE* ) - ( ( char* )newacl + dst ); - memcpy( pnewace, poldace, acesz ); - /* - * Replace generic creator-owner and - * creator-group by owner and group - */ - if ( ntfs_same_sid( &pnewace->sid, ownersid ) ) - { - memcpy( &pnewace->sid, usid, usidsz ); - acesz = usidsz + 8; - } - if ( ntfs_same_sid( &pnewace->sid, groupsid ) ) - { - memcpy( &pnewace->sid, gsid, gsidsz ); - acesz = gsidsz + 8; - } - dst += acesz; - newcnt++; - } - src += acesz; - } - /* - * Adjust header if something was inherited - */ - if ( dst > sizeof( ACL ) ) - { - newacl->ace_count = cpu_to_le16( newcnt ); - newacl->size = cpu_to_le16( dst ); - } - else - dst = 0; - return ( dst ); + selection = (fordir ? CONTAINER_INHERIT_ACE : OBJECT_INHERIT_ACE); + newcnt = 0; + oldcnt = le16_to_cpu(oldacl->ace_count); + for (nace = 0; nace < oldcnt; nace++) { + poldace = (const ACCESS_ALLOWED_ACE*)((const char*)oldacl + src); + acesz = le16_to_cpu(poldace->size); + /* inheritance for access */ + if (poldace->flags & selection) { + pnewace = (ACCESS_ALLOWED_ACE*) + ((char*)newacl + dst); + memcpy(pnewace,poldace,acesz); + /* + * Replace generic creator-owner and + * creator-group by owner and group + */ + if (ntfs_same_sid(&pnewace->sid, ownersid)) { + memcpy(&pnewace->sid, usid, usidsz); + acesz = usidsz + 8; + pnewace->size = cpu_to_le16(acesz); + } + if (ntfs_same_sid(&pnewace->sid, groupsid)) { + memcpy(&pnewace->sid, gsid, gsidsz); + acesz = gsidsz + 8; + pnewace->size = cpu_to_le16(acesz); + } + if (pnewace->mask & GENERIC_ALL) { + pnewace->mask &= ~GENERIC_ALL; + if (fordir) + pnewace->mask |= OWNER_RIGHTS + | DIR_READ + | DIR_WRITE + | DIR_EXEC; + else + /* + * The last flag is not defined for a file, + * however Windows sets it, so do the same + */ + pnewace->mask |= OWNER_RIGHTS + | FILE_READ + | FILE_WRITE + | FILE_EXEC + | cpu_to_le32(0x40); + } + /* remove inheritance flags */ + pnewace->flags &= ~(OBJECT_INHERIT_ACE + | CONTAINER_INHERIT_ACE + | INHERIT_ONLY_ACE); + dst += acesz; + newcnt++; + } + /* inheritance for further inheritance */ + if (fordir + && (poldace->flags + & (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE))) { + pnewace = (ACCESS_ALLOWED_ACE*) + ((char*)newacl + dst); + memcpy(pnewace,poldace,acesz); + /* + * Replace generic creator-owner and + * creator-group by owner and group + */ + if (ntfs_same_sid(&pnewace->sid, ownersid)) { + memcpy(&pnewace->sid, usid, usidsz); + acesz = usidsz + 8; + } + if (ntfs_same_sid(&pnewace->sid, groupsid)) { + memcpy(&pnewace->sid, gsid, gsidsz); + acesz = gsidsz + 8; + } + dst += acesz; + newcnt++; + } + src += acesz; + } + /* + * Adjust header if something was inherited + */ + if (dst > sizeof(ACL)) { + newacl->ace_count = cpu_to_le16(newcnt); + newacl->size = cpu_to_le16(dst); + } else + dst = 0; + return (dst); } #if POSIXACLS /* - * Do sanity checks on a Posix descriptor - * Should not be called with a NULL argument - * returns TRUE if considered safe - * if not, error should be logged by caller + * Do sanity checks on a Posix descriptor + * Should not be called with a NULL argument + * returns TRUE if considered safe + * if not, error should be logged by caller */ -BOOL ntfs_valid_posix( const struct POSIX_SECURITY *pxdesc ) +BOOL ntfs_valid_posix(const struct POSIX_SECURITY *pxdesc) { - const struct POSIX_ACL *pacl; - int i; - BOOL ok; - u16 tag; - u32 id; - int perms; - struct - { - u16 previous; - u32 previousid; - u16 tagsset; - mode_t mode; - int owners; - int groups; - int others; - } checks[2], *pchk; + const struct POSIX_ACL *pacl; + int i; + BOOL ok; + u16 tag; + u32 id; + int perms; + struct { + u16 previous; + u32 previousid; + u16 tagsset; + mode_t mode; + int owners; + int groups; + int others; + } checks[2], *pchk; - for ( i = 0; i < 2; i++ ) - { - checks[i].mode = 0; - checks[i].tagsset = 0; - checks[i].owners = 0; - checks[i].groups = 0; - checks[i].others = 0; - checks[i].previous = 0; - checks[i].previousid = 0; - } - ok = TRUE; - pacl = &pxdesc->acl; - /* - * header (strict for now) - */ - if ( ( pacl->version != POSIX_VERSION ) - || ( pacl->flags != 0 ) - || ( pacl->filler != 0 ) ) - ok = FALSE; - /* - * Reject multiple owner, group or other - * but do not require them to be present - * Also check the ACEs are in correct order - * which implies there is no duplicates - */ - for ( i = 0; i < pxdesc->acccnt + pxdesc->defcnt; i++ ) - { - if ( i >= pxdesc->firstdef ) - pchk = &checks[1]; - else - pchk = &checks[0]; - perms = pacl->ace[i].perms; - tag = pacl->ace[i].tag; - pchk->tagsset |= tag; - id = pacl->ace[i].id; - if ( perms & ~7 ) ok = FALSE; - if ( ( tag < pchk->previous ) - || ( ( tag == pchk->previous ) - && ( id <= pchk->previousid ) ) ) - ok = FALSE; - pchk->previous = tag; - pchk->previousid = id; - switch ( tag ) - { - case POSIX_ACL_USER_OBJ : - if ( pchk->owners++ ) - ok = FALSE; - if ( id != ( u32 ) - 1 ) - ok = FALSE; - pchk->mode |= perms << 6; - break; - case POSIX_ACL_GROUP_OBJ : - if ( pchk->groups++ ) - ok = FALSE; - if ( id != ( u32 ) - 1 ) - ok = FALSE; - pchk->mode = ( pchk->mode & 07707 ) | ( perms << 3 ); - break; - case POSIX_ACL_OTHER : - if ( pchk->others++ ) - ok = FALSE; - if ( id != ( u32 ) - 1 ) - ok = FALSE; - pchk->mode |= perms; - break; - case POSIX_ACL_USER : - case POSIX_ACL_GROUP : - if ( id == ( u32 ) - 1 ) - ok = FALSE; - break; - case POSIX_ACL_MASK : - if ( id != ( u32 ) - 1 ) - ok = FALSE; - pchk->mode = ( pchk->mode & 07707 ) | ( perms << 3 ); - break; - default : - ok = FALSE; - break; - } - } - if ( ( pxdesc->acccnt > 0 ) - && ( ( checks[0].owners != 1 ) || ( checks[0].groups != 1 ) - || ( checks[0].others != 1 ) ) ) - ok = FALSE; - /* do not check owner, group or other are present in */ - /* the default ACL, Windows does not necessarily set them */ - /* descriptor */ - if ( pxdesc->defcnt && ( pxdesc->acccnt > pxdesc->firstdef ) ) - ok = FALSE; - if ( ( pxdesc->acccnt < 0 ) || ( pxdesc->defcnt < 0 ) ) - ok = FALSE; - /* check mode, unless null or no tag set */ - if ( pxdesc->mode - && checks[0].tagsset - && ( checks[0].mode != ( pxdesc->mode & 0777 ) ) ) - ok = FALSE; - /* check tagsset */ - if ( pxdesc->tagsset != checks[0].tagsset ) - ok = FALSE; - return ( ok ); + for (i=0; i<2; i++) { + checks[i].mode = 0; + checks[i].tagsset = 0; + checks[i].owners = 0; + checks[i].groups = 0; + checks[i].others = 0; + checks[i].previous = 0; + checks[i].previousid = 0; + } + ok = TRUE; + pacl = &pxdesc->acl; + /* + * header (strict for now) + */ + if ((pacl->version != POSIX_VERSION) + || (pacl->flags != 0) + || (pacl->filler != 0)) + ok = FALSE; + /* + * Reject multiple owner, group or other + * but do not require them to be present + * Also check the ACEs are in correct order + * which implies there is no duplicates + */ + for (i=0; iacccnt + pxdesc->defcnt; i++) { + if (i >= pxdesc->firstdef) + pchk = &checks[1]; + else + pchk = &checks[0]; + perms = pacl->ace[i].perms; + tag = pacl->ace[i].tag; + pchk->tagsset |= tag; + id = pacl->ace[i].id; + if (perms & ~7) ok = FALSE; + if ((tag < pchk->previous) + || ((tag == pchk->previous) + && (id <= pchk->previousid))) + ok = FALSE; + pchk->previous = tag; + pchk->previousid = id; + switch (tag) { + case POSIX_ACL_USER_OBJ : + if (pchk->owners++) + ok = FALSE; + if (id != (u32)-1) + ok = FALSE; + pchk->mode |= perms << 6; + break; + case POSIX_ACL_GROUP_OBJ : + if (pchk->groups++) + ok = FALSE; + if (id != (u32)-1) + ok = FALSE; + pchk->mode = (pchk->mode & 07707) | (perms << 3); + break; + case POSIX_ACL_OTHER : + if (pchk->others++) + ok = FALSE; + if (id != (u32)-1) + ok = FALSE; + pchk->mode |= perms; + break; + case POSIX_ACL_USER : + case POSIX_ACL_GROUP : + if (id == (u32)-1) + ok = FALSE; + break; + case POSIX_ACL_MASK : + if (id != (u32)-1) + ok = FALSE; + pchk->mode = (pchk->mode & 07707) | (perms << 3); + break; + default : + ok = FALSE; + break; + } + } + if ((pxdesc->acccnt > 0) + && ((checks[0].owners != 1) || (checks[0].groups != 1) + || (checks[0].others != 1))) + ok = FALSE; + /* do not check owner, group or other are present in */ + /* the default ACL, Windows does not necessarily set them */ + /* descriptor */ + if (pxdesc->defcnt && (pxdesc->acccnt > pxdesc->firstdef)) + ok = FALSE; + if ((pxdesc->acccnt < 0) || (pxdesc->defcnt < 0)) + ok = FALSE; + /* check mode, unless null or no tag set */ + if (pxdesc->mode + && checks[0].tagsset + && (checks[0].mode != (pxdesc->mode & 0777))) + ok = FALSE; + /* check tagsset */ + if (pxdesc->tagsset != checks[0].tagsset) + ok = FALSE; + return (ok); } /* - * Set standard header data into a Posix ACL - * The mode argument should provide the 3 upper bits of target mode + * Set standard header data into a Posix ACL + * The mode argument should provide the 3 upper bits of target mode */ -static mode_t posix_header( struct POSIX_SECURITY *pxdesc, mode_t basemode ) +static mode_t posix_header(struct POSIX_SECURITY *pxdesc, mode_t basemode) { - mode_t mode; - u16 tagsset; - struct POSIX_ACE *pace; - int i; + mode_t mode; + u16 tagsset; + struct POSIX_ACE *pace; + int i; - mode = basemode & 07000; - tagsset = 0; - for ( i = 0; i < pxdesc->acccnt; i++ ) - { - pace = &pxdesc->acl.ace[i]; - tagsset |= pace->tag; - switch ( pace->tag ) - { - case POSIX_ACL_USER_OBJ : - mode |= ( pace->perms & 7 ) << 6; - break; - case POSIX_ACL_GROUP_OBJ : - case POSIX_ACL_MASK : - mode = ( mode & 07707 ) | ( ( pace->perms & 7 ) << 3 ); - break; - case POSIX_ACL_OTHER : - mode |= pace->perms & 7; - break; - default : - break; - } - } - pxdesc->tagsset = tagsset; - pxdesc->mode = mode; - pxdesc->acl.version = POSIX_VERSION; - pxdesc->acl.flags = 0; - pxdesc->acl.filler = 0; - return ( mode ); + mode = basemode & 07000; + tagsset = 0; + for (i=0; iacccnt; i++) { + pace = &pxdesc->acl.ace[i]; + tagsset |= pace->tag; + switch(pace->tag) { + case POSIX_ACL_USER_OBJ : + mode |= (pace->perms & 7) << 6; + break; + case POSIX_ACL_GROUP_OBJ : + case POSIX_ACL_MASK : + mode = (mode & 07707) | ((pace->perms & 7) << 3); + break; + case POSIX_ACL_OTHER : + mode |= pace->perms & 7; + break; + default : + break; + } + } + pxdesc->tagsset = tagsset; + pxdesc->mode = mode; + pxdesc->acl.version = POSIX_VERSION; + pxdesc->acl.flags = 0; + pxdesc->acl.filler = 0; + return (mode); } /* - * Sort ACEs in a Posix ACL - * This is useful for always getting reusable converted ACLs, - * it also helps in merging ACEs. - * Repeated tag+id are allowed and not merged here. + * Sort ACEs in a Posix ACL + * This is useful for always getting reusable converted ACLs, + * it also helps in merging ACEs. + * Repeated tag+id are allowed and not merged here. * - * Tags should be in ascending sequence and for a repeatable tag - * ids should be in ascending sequence. + * Tags should be in ascending sequence and for a repeatable tag + * ids should be in ascending sequence. */ -void ntfs_sort_posix( struct POSIX_SECURITY *pxdesc ) +void ntfs_sort_posix(struct POSIX_SECURITY *pxdesc) { - struct POSIX_ACL *pacl; - struct POSIX_ACE ace; - int i; - int offs; - BOOL done; - u16 tag; - u16 previous; - u32 id; - u32 previousid; + struct POSIX_ACL *pacl; + struct POSIX_ACE ace; + int i; + int offs; + BOOL done; + u16 tag; + u16 previous; + u32 id; + u32 previousid; - /* - * Check sequencing of tag+id in access ACE's - */ - pacl = &pxdesc->acl; - do - { - done = TRUE; - previous = pacl->ace[0].tag; - previousid = pacl->ace[0].id; - for ( i = 1; i < pxdesc->acccnt; i++ ) - { - tag = pacl->ace[i].tag; - id = pacl->ace[i].id; + /* + * Check sequencing of tag+id in access ACE's + */ + pacl = &pxdesc->acl; + do { + done = TRUE; + previous = pacl->ace[0].tag; + previousid = pacl->ace[0].id; + for (i=1; iacccnt; i++) { + tag = pacl->ace[i].tag; + id = pacl->ace[i].id; - if ( ( tag < previous ) - || ( ( tag == previous ) && ( id < previousid ) ) ) - { - done = FALSE; - memcpy( &ace, &pacl->ace[i-1], sizeof( struct POSIX_ACE ) ); - memcpy( &pacl->ace[i-1], &pacl->ace[i], sizeof( struct POSIX_ACE ) ); - memcpy( &pacl->ace[i], &ace, sizeof( struct POSIX_ACE ) ); - } - else - { - previous = tag; - previousid = id; - } - } - } - while ( !done ); - /* - * Same for default ACEs - */ - do - { - done = TRUE; - if ( ( pxdesc->defcnt ) > 1 ) - { - offs = pxdesc->firstdef; - previous = pacl->ace[offs].tag; - previousid = pacl->ace[offs].id; - for ( i = offs + 1; i < offs + pxdesc->defcnt; i++ ) - { - tag = pacl->ace[i].tag; - id = pacl->ace[i].id; + if ((tag < previous) + || ((tag == previous) && (id < previousid))) { + done = FALSE; + memcpy(&ace,&pacl->ace[i-1],sizeof(struct POSIX_ACE)); + memcpy(&pacl->ace[i-1],&pacl->ace[i],sizeof(struct POSIX_ACE)); + memcpy(&pacl->ace[i],&ace,sizeof(struct POSIX_ACE)); + } else { + previous = tag; + previousid = id; + } + } + } while (!done); + /* + * Same for default ACEs + */ + do { + done = TRUE; + if ((pxdesc->defcnt) > 1) { + offs = pxdesc->firstdef; + previous = pacl->ace[offs].tag; + previousid = pacl->ace[offs].id; + for (i=offs+1; idefcnt; i++) { + tag = pacl->ace[i].tag; + id = pacl->ace[i].id; - if ( ( tag < previous ) - || ( ( tag == previous ) && ( id < previousid ) ) ) - { - done = FALSE; - memcpy( &ace, &pacl->ace[i-1], sizeof( struct POSIX_ACE ) ); - memcpy( &pacl->ace[i-1], &pacl->ace[i], sizeof( struct POSIX_ACE ) ); - memcpy( &pacl->ace[i], &ace, sizeof( struct POSIX_ACE ) ); - } - else - { - previous = tag; - previousid = id; - } - } - } - } - while ( !done ); + if ((tag < previous) + || ((tag == previous) && (id < previousid))) { + done = FALSE; + memcpy(&ace,&pacl->ace[i-1],sizeof(struct POSIX_ACE)); + memcpy(&pacl->ace[i-1],&pacl->ace[i],sizeof(struct POSIX_ACE)); + memcpy(&pacl->ace[i],&ace,sizeof(struct POSIX_ACE)); + } else { + previous = tag; + previousid = id; + } + } + } + } while (!done); } /* - * Merge a new mode into a Posix descriptor - * The Posix descriptor is not reallocated, its size is unchanged + * Merge a new mode into a Posix descriptor + * The Posix descriptor is not reallocated, its size is unchanged * - * returns 0 if ok + * returns 0 if ok */ -int ntfs_merge_mode_posix( struct POSIX_SECURITY *pxdesc, mode_t mode ) +int ntfs_merge_mode_posix(struct POSIX_SECURITY *pxdesc, mode_t mode) { - int i; - BOOL maskfound; - struct POSIX_ACE *pace; - int todo; + int i; + BOOL maskfound; + struct POSIX_ACE *pace; + int todo; - maskfound = FALSE; - todo = POSIX_ACL_USER_OBJ | POSIX_ACL_GROUP_OBJ | POSIX_ACL_OTHER; - for ( i = pxdesc->acccnt - 1; i >= 0; i-- ) - { - pace = &pxdesc->acl.ace[i]; - switch ( pace->tag ) - { - case POSIX_ACL_USER_OBJ : - pace->perms = ( mode >> 6 ) & 7; - todo &= ~POSIX_ACL_USER_OBJ; - break; - case POSIX_ACL_GROUP_OBJ : - if ( !maskfound ) - pace->perms = ( mode >> 3 ) & 7; - todo &= ~POSIX_ACL_GROUP_OBJ; - break; - case POSIX_ACL_MASK : - pace->perms = ( mode >> 3 ) & 7; - maskfound = TRUE; - break; - case POSIX_ACL_OTHER : - pace->perms = mode & 7; - todo &= ~POSIX_ACL_OTHER; - break; - default : - break; - } - } - pxdesc->mode = mode; - return ( todo ? -1 : 0 ); + maskfound = FALSE; + todo = POSIX_ACL_USER_OBJ | POSIX_ACL_GROUP_OBJ | POSIX_ACL_OTHER; + for (i=pxdesc->acccnt-1; i>=0; i--) { + pace = &pxdesc->acl.ace[i]; + switch(pace->tag) { + case POSIX_ACL_USER_OBJ : + pace->perms = (mode >> 6) & 7; + todo &= ~POSIX_ACL_USER_OBJ; + break; + case POSIX_ACL_GROUP_OBJ : + if (!maskfound) + pace->perms = (mode >> 3) & 7; + todo &= ~POSIX_ACL_GROUP_OBJ; + break; + case POSIX_ACL_MASK : + pace->perms = (mode >> 3) & 7; + maskfound = TRUE; + break; + case POSIX_ACL_OTHER : + pace->perms = mode & 7; + todo &= ~POSIX_ACL_OTHER; + break; + default : + break; + } + } + pxdesc->mode = mode; + return (todo ? -1 : 0); } /* - * Replace an access or default Posix ACL - * The resulting ACL is checked for validity + * Replace an access or default Posix ACL + * The resulting ACL is checked for validity * - * Returns a new ACL or NULL if there is a problem + * Returns a new ACL or NULL if there is a problem */ -struct POSIX_SECURITY *ntfs_replace_acl( const struct POSIX_SECURITY *oldpxdesc, - const struct POSIX_ACL *newacl, int count, BOOL deflt ) +struct POSIX_SECURITY *ntfs_replace_acl(const struct POSIX_SECURITY *oldpxdesc, + const struct POSIX_ACL *newacl, int count, BOOL deflt) { - struct POSIX_SECURITY *newpxdesc; - size_t newsize; - int offset; - int oldoffset; - int i; + struct POSIX_SECURITY *newpxdesc; + size_t newsize; + int offset; + int oldoffset; + int i; - if ( deflt ) - newsize = sizeof( struct POSIX_SECURITY ) - + ( oldpxdesc->acccnt + count )*sizeof( struct POSIX_ACE ); - else - newsize = sizeof( struct POSIX_SECURITY ) - + ( oldpxdesc->defcnt + count )*sizeof( struct POSIX_ACE ); - newpxdesc = ( struct POSIX_SECURITY* )malloc( newsize ); - if ( newpxdesc ) - { - if ( deflt ) - { - offset = oldpxdesc->acccnt; - newpxdesc->acccnt = oldpxdesc->acccnt; - newpxdesc->defcnt = count; - newpxdesc->firstdef = offset; - /* copy access ACEs */ - for ( i = 0; i < newpxdesc->acccnt; i++ ) - newpxdesc->acl.ace[i] = oldpxdesc->acl.ace[i]; - /* copy default ACEs */ - for ( i = 0; i < count; i++ ) - newpxdesc->acl.ace[i + offset] = newacl->ace[i]; - } - else - { - offset = count; - newpxdesc->acccnt = count; - newpxdesc->defcnt = oldpxdesc->defcnt; - newpxdesc->firstdef = count; - /* copy access ACEs */ - for ( i = 0; i < count; i++ ) - newpxdesc->acl.ace[i] = newacl->ace[i]; - /* copy default ACEs */ - oldoffset = oldpxdesc->firstdef; - for ( i = 0; i < newpxdesc->defcnt; i++ ) - newpxdesc->acl.ace[i + offset] = oldpxdesc->acl.ace[i + oldoffset]; - } - /* assume special flags unchanged */ - posix_header( newpxdesc, oldpxdesc->mode ); - if ( !ntfs_valid_posix( newpxdesc ) ) - { - /* do not log, this is an application error */ - free( newpxdesc ); - newpxdesc = ( struct POSIX_SECURITY* )NULL; - errno = EINVAL; - } - } - else - errno = ENOMEM; - return ( newpxdesc ); + if (deflt) + newsize = sizeof(struct POSIX_SECURITY) + + (oldpxdesc->acccnt + count)*sizeof(struct POSIX_ACE); + else + newsize = sizeof(struct POSIX_SECURITY) + + (oldpxdesc->defcnt + count)*sizeof(struct POSIX_ACE); + newpxdesc = (struct POSIX_SECURITY*)malloc(newsize); + if (newpxdesc) { + if (deflt) { + offset = oldpxdesc->acccnt; + newpxdesc->acccnt = oldpxdesc->acccnt; + newpxdesc->defcnt = count; + newpxdesc->firstdef = offset; + /* copy access ACEs */ + for (i=0; iacccnt; i++) + newpxdesc->acl.ace[i] = oldpxdesc->acl.ace[i]; + /* copy default ACEs */ + for (i=0; iacl.ace[i + offset] = newacl->ace[i]; + } else { + offset = count; + newpxdesc->acccnt = count; + newpxdesc->defcnt = oldpxdesc->defcnt; + newpxdesc->firstdef = count; + /* copy access ACEs */ + for (i=0; iacl.ace[i] = newacl->ace[i]; + /* copy default ACEs */ + oldoffset = oldpxdesc->firstdef; + for (i=0; idefcnt; i++) + newpxdesc->acl.ace[i + offset] = oldpxdesc->acl.ace[i + oldoffset]; + } + /* assume special flags unchanged */ + posix_header(newpxdesc, oldpxdesc->mode); + if (!ntfs_valid_posix(newpxdesc)) { + /* do not log, this is an application error */ + free(newpxdesc); + newpxdesc = (struct POSIX_SECURITY*)NULL; + errno = EINVAL; + } + } else + errno = ENOMEM; + return (newpxdesc); } /* - * Build an inherited Posix descriptor from parent - * descriptor (if any) restricted to creation mode + * Build an inherited Posix descriptor from parent + * descriptor (if any) restricted to creation mode * - * Returns the inherited descriptor or NULL if there is a problem + * Returns the inherited descriptor or NULL if there is a problem */ struct POSIX_SECURITY *ntfs_build_inherited_posix( - const struct POSIX_SECURITY *pxdesc, mode_t mode, - mode_t mask, BOOL isdir ) + const struct POSIX_SECURITY *pxdesc, mode_t mode, + mode_t mask, BOOL isdir) { - struct POSIX_SECURITY *pydesc; - struct POSIX_ACE *pyace; - int count; - int defcnt; - int size; - int i; - s16 tagsset; + struct POSIX_SECURITY *pydesc; + struct POSIX_ACE *pyace; + int count; + int defcnt; + int size; + int i; + s16 tagsset; - if ( pxdesc && pxdesc->defcnt ) - { - if ( isdir ) - count = 2 * pxdesc->defcnt + 3; - else - count = pxdesc->defcnt + 3; - } - else - count = 3; - pydesc = ( struct POSIX_SECURITY* )malloc( - sizeof( struct POSIX_SECURITY ) + count*sizeof( struct POSIX_ACE ) ); - if ( pydesc ) - { - /* - * Copy inherited tags and adapt perms - * Use requested mode, ignoring umask - * (not possible with older versions of fuse) - */ - tagsset = 0; - defcnt = ( pxdesc ? pxdesc->defcnt : 0 ); - for ( i = defcnt - 1; i >= 0; i-- ) - { - pyace = &pydesc->acl.ace[i]; - *pyace = pxdesc->acl.ace[pxdesc->firstdef + i]; - switch ( pyace->tag ) - { - case POSIX_ACL_USER_OBJ : - pyace->perms &= ( mode >> 6 ) & 7; - break; - case POSIX_ACL_GROUP_OBJ : - if ( !( tagsset & POSIX_ACL_MASK ) ) - pyace->perms &= ( mode >> 3 ) & 7; - break; - case POSIX_ACL_OTHER : - pyace->perms &= mode & 7; - break; - case POSIX_ACL_MASK : - pyace->perms &= ( mode >> 3 ) & 7; - break; - default : - break; - } - tagsset |= pyace->tag; - } - pydesc->acccnt = defcnt; - /* - * If some standard tags were missing, append them from mode - * and sort the list - * Here we have to use the umask'ed mode - */ - if ( ~tagsset & ( POSIX_ACL_USER_OBJ - | POSIX_ACL_GROUP_OBJ | POSIX_ACL_OTHER ) ) - { - i = defcnt; - /* owner was missing */ - if ( !( tagsset & POSIX_ACL_USER_OBJ ) ) - { - pyace = &pydesc->acl.ace[i]; - pyace->tag = POSIX_ACL_USER_OBJ; - pyace->id = -1; - pyace->perms = ( ( mode & ~mask ) >> 6 ) & 7; - tagsset |= POSIX_ACL_USER_OBJ; - i++; - } - /* owning group was missing */ - if ( !( tagsset & POSIX_ACL_GROUP_OBJ ) ) - { - pyace = &pydesc->acl.ace[i]; - pyace->tag = POSIX_ACL_GROUP_OBJ; - pyace->id = -1; - pyace->perms = ( ( mode & ~mask ) >> 3 ) & 7; - tagsset |= POSIX_ACL_GROUP_OBJ; - i++; - } - /* other was missing */ - if ( !( tagsset & POSIX_ACL_OTHER ) ) - { - pyace = &pydesc->acl.ace[i]; - pyace->tag = POSIX_ACL_OTHER; - pyace->id = -1; - pyace->perms = mode & ~mask & 7; - tagsset |= POSIX_ACL_OTHER; - i++; - } - pydesc->acccnt = i; - pydesc->firstdef = i; - pydesc->defcnt = 0; - ntfs_sort_posix( pydesc ); - } + if (pxdesc && pxdesc->defcnt) { + if (isdir) + count = 2*pxdesc->defcnt + 3; + else + count = pxdesc->defcnt + 3; + } else + count = 3; + pydesc = (struct POSIX_SECURITY*)malloc( + sizeof(struct POSIX_SECURITY) + count*sizeof(struct POSIX_ACE)); + if (pydesc) { + /* + * Copy inherited tags and adapt perms + * Use requested mode, ignoring umask + * (not possible with older versions of fuse) + */ + tagsset = 0; + defcnt = (pxdesc ? pxdesc->defcnt : 0); + for (i=defcnt-1; i>=0; i--) { + pyace = &pydesc->acl.ace[i]; + *pyace = pxdesc->acl.ace[pxdesc->firstdef + i]; + switch (pyace->tag) { + case POSIX_ACL_USER_OBJ : + pyace->perms &= (mode >> 6) & 7; + break; + case POSIX_ACL_GROUP_OBJ : + if (!(tagsset & POSIX_ACL_MASK)) + pyace->perms &= (mode >> 3) & 7; + break; + case POSIX_ACL_OTHER : + pyace->perms &= mode & 7; + break; + case POSIX_ACL_MASK : + pyace->perms &= (mode >> 3) & 7; + break; + default : + break; + } + tagsset |= pyace->tag; + } + pydesc->acccnt = defcnt; + /* + * If some standard tags were missing, append them from mode + * and sort the list + * Here we have to use the umask'ed mode + */ + if (~tagsset & (POSIX_ACL_USER_OBJ + | POSIX_ACL_GROUP_OBJ | POSIX_ACL_OTHER)) { + i = defcnt; + /* owner was missing */ + if (!(tagsset & POSIX_ACL_USER_OBJ)) { + pyace = &pydesc->acl.ace[i]; + pyace->tag = POSIX_ACL_USER_OBJ; + pyace->id = -1; + pyace->perms = ((mode & ~mask) >> 6) & 7; + tagsset |= POSIX_ACL_USER_OBJ; + i++; + } + /* owning group was missing */ + if (!(tagsset & POSIX_ACL_GROUP_OBJ)) { + pyace = &pydesc->acl.ace[i]; + pyace->tag = POSIX_ACL_GROUP_OBJ; + pyace->id = -1; + pyace->perms = ((mode & ~mask) >> 3) & 7; + tagsset |= POSIX_ACL_GROUP_OBJ; + i++; + } + /* other was missing */ + if (!(tagsset & POSIX_ACL_OTHER)) { + pyace = &pydesc->acl.ace[i]; + pyace->tag = POSIX_ACL_OTHER; + pyace->id = -1; + pyace->perms = mode & ~mask & 7; + tagsset |= POSIX_ACL_OTHER; + i++; + } + pydesc->acccnt = i; + pydesc->firstdef = i; + pydesc->defcnt = 0; + ntfs_sort_posix(pydesc); + } - /* - * append as a default ACL if a directory - */ - pydesc->firstdef = pydesc->acccnt; - if ( defcnt && isdir ) - { - size = sizeof( struct POSIX_ACE ) * defcnt; - memcpy( &pydesc->acl.ace[pydesc->firstdef], - &pxdesc->acl.ace[pxdesc->firstdef], size ); - pydesc->defcnt = defcnt; - } - else - { - pydesc->defcnt = 0; - } - /* assume special bits are not inherited */ - posix_header( pydesc, mode & 07000 ); - if ( !ntfs_valid_posix( pydesc ) ) - { - ntfs_log_error( "Error building an inherited Posix desc\n" ); - errno = EIO; - free( pydesc ); - pydesc = ( struct POSIX_SECURITY* )NULL; - } - } - else - errno = ENOMEM; - return ( pydesc ); + /* + * append as a default ACL if a directory + */ + pydesc->firstdef = pydesc->acccnt; + if (defcnt && isdir) { + size = sizeof(struct POSIX_ACE)*defcnt; + memcpy(&pydesc->acl.ace[pydesc->firstdef], + &pxdesc->acl.ace[pxdesc->firstdef],size); + pydesc->defcnt = defcnt; + } else { + pydesc->defcnt = 0; + } + /* assume special bits are not inherited */ + posix_header(pydesc, mode & 07000); + if (!ntfs_valid_posix(pydesc)) { + ntfs_log_error("Error building an inherited Posix desc\n"); + errno = EIO; + free(pydesc); + pydesc = (struct POSIX_SECURITY*)NULL; + } + } else + errno = ENOMEM; + return (pydesc); } -static int merge_lists_posix( struct POSIX_ACE *targetace, - const struct POSIX_ACE *firstace, - const struct POSIX_ACE *secondace, - int firstcnt, int secondcnt ) +static int merge_lists_posix(struct POSIX_ACE *targetace, + const struct POSIX_ACE *firstace, + const struct POSIX_ACE *secondace, + int firstcnt, int secondcnt) { - int k; + int k; - k = 0; - /* - * No list is exhausted : - * if same tag+id in both list : - * ignore ACE from second list - * else take the one with smaller tag+id - */ - while ( ( firstcnt > 0 ) && ( secondcnt > 0 ) ) - if ( ( firstace->tag == secondace->tag ) - && ( firstace->id == secondace->id ) ) - { - secondace++; - secondcnt--; - } - else if ( ( firstace->tag < secondace->tag ) - || ( ( firstace->tag == secondace->tag ) - && ( firstace->id < secondace->id ) ) ) - { - targetace->tag = firstace->tag; - targetace->id = firstace->id; - targetace->perms = firstace->perms; - firstace++; - targetace++; - firstcnt--; - k++; - } - else - { - targetace->tag = secondace->tag; - targetace->id = secondace->id; - targetace->perms = secondace->perms; - secondace++; - targetace++; - secondcnt--; - k++; - } - /* - * One list is exhausted, copy the other one - */ - while ( firstcnt > 0 ) - { - targetace->tag = firstace->tag; - targetace->id = firstace->id; - targetace->perms = firstace->perms; - firstace++; - targetace++; - firstcnt--; - k++; - } - while ( secondcnt > 0 ) - { - targetace->tag = secondace->tag; - targetace->id = secondace->id; - targetace->perms = secondace->perms; - secondace++; - targetace++; - secondcnt--; - k++; - } - return ( k ); + k = 0; + /* + * No list is exhausted : + * if same tag+id in both list : + * ignore ACE from second list + * else take the one with smaller tag+id + */ + while ((firstcnt > 0) && (secondcnt > 0)) + if ((firstace->tag == secondace->tag) + && (firstace->id == secondace->id)) { + secondace++; + secondcnt--; + } else + if ((firstace->tag < secondace->tag) + || ((firstace->tag == secondace->tag) + && (firstace->id < secondace->id))) { + targetace->tag = firstace->tag; + targetace->id = firstace->id; + targetace->perms = firstace->perms; + firstace++; + targetace++; + firstcnt--; + k++; + } else { + targetace->tag = secondace->tag; + targetace->id = secondace->id; + targetace->perms = secondace->perms; + secondace++; + targetace++; + secondcnt--; + k++; + } + /* + * One list is exhausted, copy the other one + */ + while (firstcnt > 0) { + targetace->tag = firstace->tag; + targetace->id = firstace->id; + targetace->perms = firstace->perms; + firstace++; + targetace++; + firstcnt--; + k++; + } + while (secondcnt > 0) { + targetace->tag = secondace->tag; + targetace->id = secondace->id; + targetace->perms = secondace->perms; + secondace++; + targetace++; + secondcnt--; + k++; + } + return (k); } /* - * Merge two Posix ACLs - * The input ACLs have to be adequately sorted + * Merge two Posix ACLs + * The input ACLs have to be adequately sorted * - * Returns the merged ACL, which is allocated and has to be freed by caller, - * or NULL if failed + * Returns the merged ACL, which is allocated and has to be freed by caller, + * or NULL if failed */ -struct POSIX_SECURITY *ntfs_merge_descr_posix( const struct POSIX_SECURITY *first, - const struct POSIX_SECURITY *second ) +struct POSIX_SECURITY *ntfs_merge_descr_posix(const struct POSIX_SECURITY *first, + const struct POSIX_SECURITY *second) { - struct POSIX_SECURITY *pxdesc; - struct POSIX_ACE *targetace; - const struct POSIX_ACE *firstace; - const struct POSIX_ACE *secondace; - size_t size; - int k; + struct POSIX_SECURITY *pxdesc; + struct POSIX_ACE *targetace; + const struct POSIX_ACE *firstace; + const struct POSIX_ACE *secondace; + size_t size; + int k; - size = sizeof( struct POSIX_SECURITY ) - + ( first->acccnt + first->defcnt - + second->acccnt + second->defcnt )*sizeof( struct POSIX_ACE ); - pxdesc = ( struct POSIX_SECURITY* )malloc( size ); - if ( pxdesc ) - { - /* - * merge access ACEs - */ - firstace = first->acl.ace; - secondace = second->acl.ace; - targetace = pxdesc->acl.ace; - k = merge_lists_posix( targetace, firstace, secondace, - first->acccnt, second->acccnt ); - pxdesc->acccnt = k; - /* - * merge default ACEs - */ - pxdesc->firstdef = k; - firstace = &first->acl.ace[first->firstdef]; - secondace = &second->acl.ace[second->firstdef]; - targetace = &pxdesc->acl.ace[k]; - k = merge_lists_posix( targetace, firstace, secondace, - first->defcnt, second->defcnt ); - pxdesc->defcnt = k; - /* - * build header - */ - pxdesc->acl.version = POSIX_VERSION; - pxdesc->acl.flags = 0; - pxdesc->acl.filler = 0; - pxdesc->mode = 0; - pxdesc->tagsset = 0; - } - else - errno = ENOMEM; - return ( pxdesc ); + size = sizeof(struct POSIX_SECURITY) + + (first->acccnt + first->defcnt + + second->acccnt + second->defcnt)*sizeof(struct POSIX_ACE); + pxdesc = (struct POSIX_SECURITY*)malloc(size); + if (pxdesc) { + /* + * merge access ACEs + */ + firstace = first->acl.ace; + secondace = second->acl.ace; + targetace = pxdesc->acl.ace; + k = merge_lists_posix(targetace,firstace,secondace, + first->acccnt,second->acccnt); + pxdesc->acccnt = k; + /* + * merge default ACEs + */ + pxdesc->firstdef = k; + firstace = &first->acl.ace[first->firstdef]; + secondace = &second->acl.ace[second->firstdef]; + targetace = &pxdesc->acl.ace[k]; + k = merge_lists_posix(targetace,firstace,secondace, + first->defcnt,second->defcnt); + pxdesc->defcnt = k; + /* + * build header + */ + pxdesc->acl.version = POSIX_VERSION; + pxdesc->acl.flags = 0; + pxdesc->acl.filler = 0; + pxdesc->mode = 0; + pxdesc->tagsset = 0; + } else + errno = ENOMEM; + return (pxdesc); } -struct BUILD_CONTEXT -{ - BOOL isdir; - BOOL adminowns; - BOOL groupowns; - u16 selfuserperms; - u16 selfgrpperms; - u16 grpperms; - u16 othperms; - u16 mask; - u16 designates; - u16 withmask; - u16 rootspecial; +struct BUILD_CONTEXT { + BOOL isdir; + BOOL adminowns; + BOOL groupowns; + u16 selfuserperms; + u16 selfgrpperms; + u16 grpperms; + u16 othperms; + u16 mask; + u16 designates; + u16 withmask; + u16 rootspecial; } ; -static BOOL build_user_denials( ACL *pacl, - const SID *usid, struct MAPPING* const mapping[], - ACE_FLAGS flags, const struct POSIX_ACE *pxace, - struct BUILD_CONTEXT *pset ) +static BOOL build_user_denials(ACL *pacl, + const SID *usid, struct MAPPING* const mapping[], + ACE_FLAGS flags, const struct POSIX_ACE *pxace, + struct BUILD_CONTEXT *pset) { - BIGSID defsid; - ACCESS_ALLOWED_ACE *pdace; - const SID *sid; - int sidsz; - int pos; - int acecnt; - le32 grants; - le32 denials; - u16 perms; - u16 mixperms; - u16 tag; - BOOL rejected; - BOOL rootuser; - BOOL avoidmask; + BIGSID defsid; + ACCESS_ALLOWED_ACE *pdace; + const SID *sid; + int sidsz; + int pos; + int acecnt; + le32 grants; + le32 denials; + u16 perms; + u16 mixperms; + u16 tag; + BOOL rejected; + BOOL rootuser; + BOOL avoidmask; - rejected = FALSE; - tag = pxace->tag; - perms = pxace->perms; - rootuser = FALSE; - pos = le16_to_cpu( pacl->size ); - acecnt = le16_to_cpu( pacl->ace_count ); - avoidmask = ( pset->mask == ( POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X ) ) - && ( ( pset->designates && pset->withmask ) - || ( !pset->designates && !pset->withmask ) ); - if ( tag == POSIX_ACL_USER_OBJ ) - { - sid = usid; - sidsz = ntfs_sid_size( sid ); - grants = OWNER_RIGHTS; - } - else - { - if ( pxace->id ) - { - sid = NTFS_FIND_USID( mapping[MAPUSERS], - pxace->id, ( SID* ) & defsid ); - grants = WORLD_RIGHTS; - } - else - { - sid = adminsid; - rootuser = TRUE; - grants = WORLD_RIGHTS & ~ROOT_OWNER_UNMARK; - } - if ( sid ) - { - sidsz = ntfs_sid_size( sid ); - /* - * Insert denial of complement of mask for - * each designated user (except root) - * WRITE_OWNER is inserted so that - * the mask can be identified - */ - if ( !avoidmask && !rootuser ) - { - denials = WRITE_OWNER; - pdace = ( ACCESS_DENIED_ACE* ) & ( ( char* )pacl )[pos]; - if ( pset->isdir ) - { - if ( !( pset->mask & POSIX_PERM_X ) ) - denials |= DIR_EXEC; - if ( !( pset->mask & POSIX_PERM_W ) ) - denials |= DIR_WRITE; - if ( !( pset->mask & POSIX_PERM_R ) ) - denials |= DIR_READ; - } - else - { - if ( !( pset->mask & POSIX_PERM_X ) ) - denials |= FILE_EXEC; - if ( !( pset->mask & POSIX_PERM_W ) ) - denials |= FILE_WRITE; - if ( !( pset->mask & POSIX_PERM_R ) ) - denials |= FILE_READ; - } - if ( rootuser ) - grants &= ~ROOT_OWNER_UNMARK; - pdace->type = ACCESS_DENIED_ACE_TYPE; - pdace->flags = flags; - pdace->size = cpu_to_le16( sidsz + 8 ); - pdace->mask = denials; - memcpy( ( char* )&pdace->sid, sid, sidsz ); - pos += sidsz + 8; - acecnt++; - } - } - else - rejected = TRUE; - } - if ( !rejected ) - { - if ( pset->isdir ) - { - if ( perms & POSIX_PERM_X ) - grants |= DIR_EXEC; - if ( perms & POSIX_PERM_W ) - grants |= DIR_WRITE; - if ( perms & POSIX_PERM_R ) - grants |= DIR_READ; - } - else - { - if ( perms & POSIX_PERM_X ) - grants |= FILE_EXEC; - if ( perms & POSIX_PERM_W ) - grants |= FILE_WRITE; - if ( perms & POSIX_PERM_R ) - grants |= FILE_READ; - } + rejected = FALSE; + tag = pxace->tag; + perms = pxace->perms; + rootuser = FALSE; + pos = le16_to_cpu(pacl->size); + acecnt = le16_to_cpu(pacl->ace_count); + avoidmask = (pset->mask == (POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X)) + && ((pset->designates && pset->withmask) + || (!pset->designates && !pset->withmask)); + if (tag == POSIX_ACL_USER_OBJ) { + sid = usid; + sidsz = ntfs_sid_size(sid); + grants = OWNER_RIGHTS; + } else { + if (pxace->id) { + sid = NTFS_FIND_USID(mapping[MAPUSERS], + pxace->id, (SID*)&defsid); + grants = WORLD_RIGHTS; + } else { + sid = adminsid; + rootuser = TRUE; + grants = WORLD_RIGHTS & ~ROOT_OWNER_UNMARK; + } + if (sid) { + sidsz = ntfs_sid_size(sid); + /* + * Insert denial of complement of mask for + * each designated user (except root) + * WRITE_OWNER is inserted so that + * the mask can be identified + */ + if (!avoidmask && !rootuser) { + denials = WRITE_OWNER; + pdace = (ACCESS_DENIED_ACE*)&((char*)pacl)[pos]; + if (pset->isdir) { + if (!(pset->mask & POSIX_PERM_X)) + denials |= DIR_EXEC; + if (!(pset->mask & POSIX_PERM_W)) + denials |= DIR_WRITE; + if (!(pset->mask & POSIX_PERM_R)) + denials |= DIR_READ; + } else { + if (!(pset->mask & POSIX_PERM_X)) + denials |= FILE_EXEC; + if (!(pset->mask & POSIX_PERM_W)) + denials |= FILE_WRITE; + if (!(pset->mask & POSIX_PERM_R)) + denials |= FILE_READ; + } + if (rootuser) + grants &= ~ROOT_OWNER_UNMARK; + pdace->type = ACCESS_DENIED_ACE_TYPE; + pdace->flags = flags; + pdace->size = cpu_to_le16(sidsz + 8); + pdace->mask = denials; + memcpy((char*)&pdace->sid, sid, sidsz); + pos += sidsz + 8; + acecnt++; + } + } else + rejected = TRUE; + } + if (!rejected) { + if (pset->isdir) { + if (perms & POSIX_PERM_X) + grants |= DIR_EXEC; + if (perms & POSIX_PERM_W) + grants |= DIR_WRITE; + if (perms & POSIX_PERM_R) + grants |= DIR_READ; + } else { + if (perms & POSIX_PERM_X) + grants |= FILE_EXEC; + if (perms & POSIX_PERM_W) + grants |= FILE_WRITE; + if (perms & POSIX_PERM_R) + grants |= FILE_READ; + } - /* a possible ACE to deny owner what he/she would */ - /* induely get from administrator, group or world */ - /* unless owner is administrator or group */ + /* a possible ACE to deny owner what he/she would */ + /* induely get from administrator, group or world */ + /* unless owner is administrator or group */ - denials = const_cpu_to_le32( 0 ); - pdace = ( ACCESS_DENIED_ACE* ) & ( ( char* )pacl )[pos]; - if ( !pset->adminowns && !rootuser ) - { - if ( !pset->groupowns ) - { - mixperms = pset->grpperms | pset->othperms; - if ( tag == POSIX_ACL_USER_OBJ ) - mixperms |= pset->selfuserperms; - if ( pset->isdir ) - { - if ( mixperms & POSIX_PERM_X ) - denials |= DIR_EXEC; - if ( mixperms & POSIX_PERM_W ) - denials |= DIR_WRITE; - if ( mixperms & POSIX_PERM_R ) - denials |= DIR_READ; - } - else - { - if ( mixperms & POSIX_PERM_X ) - denials |= FILE_EXEC; - if ( mixperms & POSIX_PERM_W ) - denials |= FILE_WRITE; - if ( mixperms & POSIX_PERM_R ) - denials |= FILE_READ; - } - } - else - { - mixperms = ~pset->grpperms & pset->othperms; - if ( tag == POSIX_ACL_USER_OBJ ) - mixperms |= pset->selfuserperms; - if ( pset->isdir ) - { - if ( mixperms & POSIX_PERM_X ) - denials |= DIR_EXEC; - if ( mixperms & POSIX_PERM_W ) - denials |= DIR_WRITE; - if ( mixperms & POSIX_PERM_R ) - denials |= DIR_READ; - } - else - { - if ( mixperms & POSIX_PERM_X ) - denials |= FILE_EXEC; - if ( mixperms & POSIX_PERM_W ) - denials |= FILE_WRITE; - if ( mixperms & POSIX_PERM_R ) - denials |= FILE_READ; - } - } - denials &= ~grants; - if ( denials ) - { - pdace->type = ACCESS_DENIED_ACE_TYPE; - pdace->flags = flags; - pdace->size = cpu_to_le16( sidsz + 8 ); - pdace->mask = denials; - memcpy( ( char* )&pdace->sid, sid, sidsz ); - pos += sidsz + 8; - acecnt++; - } - } - } - pacl->size = cpu_to_le16( pos ); - pacl->ace_count = cpu_to_le16( acecnt ); - return ( !rejected ); + denials = const_cpu_to_le32(0); + pdace = (ACCESS_DENIED_ACE*)&((char*)pacl)[pos]; + if (!pset->adminowns && !rootuser) { + if (!pset->groupowns) { + mixperms = pset->grpperms | pset->othperms; + if (tag == POSIX_ACL_USER_OBJ) + mixperms |= pset->selfuserperms; + if (pset->isdir) { + if (mixperms & POSIX_PERM_X) + denials |= DIR_EXEC; + if (mixperms & POSIX_PERM_W) + denials |= DIR_WRITE; + if (mixperms & POSIX_PERM_R) + denials |= DIR_READ; + } else { + if (mixperms & POSIX_PERM_X) + denials |= FILE_EXEC; + if (mixperms & POSIX_PERM_W) + denials |= FILE_WRITE; + if (mixperms & POSIX_PERM_R) + denials |= FILE_READ; + } + } else { + mixperms = ~pset->grpperms & pset->othperms; + if (tag == POSIX_ACL_USER_OBJ) + mixperms |= pset->selfuserperms; + if (pset->isdir) { + if (mixperms & POSIX_PERM_X) + denials |= DIR_EXEC; + if (mixperms & POSIX_PERM_W) + denials |= DIR_WRITE; + if (mixperms & POSIX_PERM_R) + denials |= DIR_READ; + } else { + if (mixperms & POSIX_PERM_X) + denials |= FILE_EXEC; + if (mixperms & POSIX_PERM_W) + denials |= FILE_WRITE; + if (mixperms & POSIX_PERM_R) + denials |= FILE_READ; + } + } + denials &= ~grants; + if (denials) { + pdace->type = ACCESS_DENIED_ACE_TYPE; + pdace->flags = flags; + pdace->size = cpu_to_le16(sidsz + 8); + pdace->mask = denials; + memcpy((char*)&pdace->sid, sid, sidsz); + pos += sidsz + 8; + acecnt++; + } + } + } + pacl->size = cpu_to_le16(pos); + pacl->ace_count = cpu_to_le16(acecnt); + return (!rejected); } -static BOOL build_user_grants( ACL *pacl, - const SID *usid, struct MAPPING* const mapping[], - ACE_FLAGS flags, const struct POSIX_ACE *pxace, - struct BUILD_CONTEXT *pset ) +static BOOL build_user_grants(ACL *pacl, + const SID *usid, struct MAPPING* const mapping[], + ACE_FLAGS flags, const struct POSIX_ACE *pxace, + struct BUILD_CONTEXT *pset) { - BIGSID defsid; - ACCESS_ALLOWED_ACE *pgace; - const SID *sid; - int sidsz; - int pos; - int acecnt; - le32 grants; - u16 perms; - u16 tag; - BOOL rejected; - BOOL rootuser; + BIGSID defsid; + ACCESS_ALLOWED_ACE *pgace; + const SID *sid; + int sidsz; + int pos; + int acecnt; + le32 grants; + u16 perms; + u16 tag; + BOOL rejected; + BOOL rootuser; - rejected = FALSE; - tag = pxace->tag; - perms = pxace->perms; - rootuser = FALSE; - pos = le16_to_cpu( pacl->size ); - acecnt = le16_to_cpu( pacl->ace_count ); - if ( tag == POSIX_ACL_USER_OBJ ) - { - sid = usid; - sidsz = ntfs_sid_size( sid ); - grants = OWNER_RIGHTS; - } - else - { - if ( pxace->id ) - { - sid = NTFS_FIND_USID( mapping[MAPUSERS], - pxace->id, ( SID* ) & defsid ); - if ( sid ) - sidsz = ntfs_sid_size( sid ); - else - rejected = TRUE; - grants = WORLD_RIGHTS; - } - else - { - sid = adminsid; - sidsz = ntfs_sid_size( sid ); - rootuser = TRUE; - grants = WORLD_RIGHTS & ~ROOT_OWNER_UNMARK; - } - } - if ( !rejected ) - { - if ( pset->isdir ) - { - if ( perms & POSIX_PERM_X ) - grants |= DIR_EXEC; - if ( perms & POSIX_PERM_W ) - grants |= DIR_WRITE; - if ( perms & POSIX_PERM_R ) - grants |= DIR_READ; - } - else - { - if ( perms & POSIX_PERM_X ) - grants |= FILE_EXEC; - if ( perms & POSIX_PERM_W ) - grants |= FILE_WRITE; - if ( perms & POSIX_PERM_R ) - grants |= FILE_READ; - } - if ( rootuser ) - grants &= ~ROOT_OWNER_UNMARK; - pgace = ( ACCESS_DENIED_ACE* ) & ( ( char* )pacl )[pos]; - pgace->type = ACCESS_ALLOWED_ACE_TYPE; - pgace->size = cpu_to_le16( sidsz + 8 ); - pgace->flags = flags; - pgace->mask = grants; - memcpy( ( char* )&pgace->sid, sid, sidsz ); - pos += sidsz + 8; - acecnt = le16_to_cpu( pacl->ace_count ) + 1; - pacl->ace_count = cpu_to_le16( acecnt ); - pacl->size = cpu_to_le16( pos ); - } - return ( !rejected ); + rejected = FALSE; + tag = pxace->tag; + perms = pxace->perms; + rootuser = FALSE; + pos = le16_to_cpu(pacl->size); + acecnt = le16_to_cpu(pacl->ace_count); + if (tag == POSIX_ACL_USER_OBJ) { + sid = usid; + sidsz = ntfs_sid_size(sid); + grants = OWNER_RIGHTS; + } else { + if (pxace->id) { + sid = NTFS_FIND_USID(mapping[MAPUSERS], + pxace->id, (SID*)&defsid); + if (sid) + sidsz = ntfs_sid_size(sid); + else + rejected = TRUE; + grants = WORLD_RIGHTS; + } else { + sid = adminsid; + sidsz = ntfs_sid_size(sid); + rootuser = TRUE; + grants = WORLD_RIGHTS & ~ROOT_OWNER_UNMARK; + } + } + if (!rejected) { + if (pset->isdir) { + if (perms & POSIX_PERM_X) + grants |= DIR_EXEC; + if (perms & POSIX_PERM_W) + grants |= DIR_WRITE; + if (perms & POSIX_PERM_R) + grants |= DIR_READ; + } else { + if (perms & POSIX_PERM_X) + grants |= FILE_EXEC; + if (perms & POSIX_PERM_W) + grants |= FILE_WRITE; + if (perms & POSIX_PERM_R) + grants |= FILE_READ; + } + if (rootuser) + grants &= ~ROOT_OWNER_UNMARK; + pgace = (ACCESS_DENIED_ACE*)&((char*)pacl)[pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + pgace->size = cpu_to_le16(sidsz + 8); + pgace->flags = flags; + pgace->mask = grants; + memcpy((char*)&pgace->sid, sid, sidsz); + pos += sidsz + 8; + acecnt = le16_to_cpu(pacl->ace_count) + 1; + pacl->ace_count = cpu_to_le16(acecnt); + pacl->size = cpu_to_le16(pos); + } + return (!rejected); } -/* a grant ACE for group */ -/* unless group-obj has the same rights as world */ -/* but present if group is owner or owner is administrator */ -/* this ACE will be inserted after denials for group */ + /* a grant ACE for group */ + /* unless group-obj has the same rights as world */ + /* but present if group is owner or owner is administrator */ + /* this ACE will be inserted after denials for group */ -static BOOL build_group_denials_grant( ACL *pacl, - const SID *gsid, struct MAPPING* const mapping[], - ACE_FLAGS flags, const struct POSIX_ACE *pxace, - struct BUILD_CONTEXT *pset ) +static BOOL build_group_denials_grant(ACL *pacl, + const SID *gsid, struct MAPPING* const mapping[], + ACE_FLAGS flags, const struct POSIX_ACE *pxace, + struct BUILD_CONTEXT *pset) { - BIGSID defsid; - ACCESS_ALLOWED_ACE *pdace; - ACCESS_ALLOWED_ACE *pgace; - const SID *sid; - int sidsz; - int pos; - int acecnt; - le32 grants; - le32 denials; - u16 perms; - u16 mixperms; - u16 tag; - BOOL avoidmask; - BOOL rootgroup; - BOOL rejected; + BIGSID defsid; + ACCESS_ALLOWED_ACE *pdace; + ACCESS_ALLOWED_ACE *pgace; + const SID *sid; + int sidsz; + int pos; + int acecnt; + le32 grants; + le32 denials; + u16 perms; + u16 mixperms; + u16 tag; + BOOL avoidmask; + BOOL rootgroup; + BOOL rejected; - rejected = FALSE; - tag = pxace->tag; - perms = pxace->perms; - pos = le16_to_cpu( pacl->size ); - acecnt = le16_to_cpu( pacl->ace_count ); - rootgroup = FALSE; - avoidmask = ( pset->mask == ( POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X ) ) - && ( ( pset->designates && pset->withmask ) - || ( !pset->designates && !pset->withmask ) ); - if ( tag == POSIX_ACL_GROUP_OBJ ) - sid = gsid; - else if ( pxace->id ) - sid = NTFS_FIND_GSID( mapping[MAPGROUPS], - pxace->id, ( SID* ) & defsid ); - else - { - sid = adminsid; - rootgroup = TRUE; - } - if ( sid ) - { - sidsz = ntfs_sid_size( sid ); - /* - * Insert denial of complement of mask for - * each group - * WRITE_OWNER is inserted so that - * the mask can be identified - * Note : this mask may lead on Windows to - * deny rights to administrators belonging - * to some user group - */ - if ( ( !avoidmask && !rootgroup ) - || ( pset->rootspecial - && ( tag == POSIX_ACL_GROUP_OBJ ) ) ) - { - denials = WRITE_OWNER; - pdace = ( ACCESS_DENIED_ACE* ) & ( ( char* )pacl )[pos]; - if ( pset->isdir ) - { - if ( !( pset->mask & POSIX_PERM_X ) ) - denials |= DIR_EXEC; - if ( !( pset->mask & POSIX_PERM_W ) ) - denials |= DIR_WRITE; - if ( !( pset->mask & POSIX_PERM_R ) ) - denials |= DIR_READ; - } - else - { - if ( !( pset->mask & POSIX_PERM_X ) ) - denials |= FILE_EXEC; - if ( !( pset->mask & POSIX_PERM_W ) ) - denials |= FILE_WRITE; - if ( !( pset->mask & POSIX_PERM_R ) ) - denials |= FILE_READ; - } - pdace->type = ACCESS_DENIED_ACE_TYPE; - pdace->flags = flags; - pdace->size = cpu_to_le16( sidsz + 8 ); - pdace->mask = denials; - memcpy( ( char* )&pdace->sid, sid, sidsz ); - pos += sidsz + 8; - acecnt++; - } - } - else - rejected = TRUE; - if ( !rejected - && ( pset->adminowns - || pset->groupowns - || avoidmask - || rootgroup - || ( perms != pset->othperms ) ) ) - { - grants = WORLD_RIGHTS; - if ( rootgroup ) - grants &= ~ROOT_GROUP_UNMARK; - if ( pset->isdir ) - { - if ( perms & POSIX_PERM_X ) - grants |= DIR_EXEC; - if ( perms & POSIX_PERM_W ) - grants |= DIR_WRITE; - if ( perms & POSIX_PERM_R ) - grants |= DIR_READ; - } - else - { - if ( perms & POSIX_PERM_X ) - grants |= FILE_EXEC; - if ( perms & POSIX_PERM_W ) - grants |= FILE_WRITE; - if ( perms & POSIX_PERM_R ) - grants |= FILE_READ; - } + rejected = FALSE; + tag = pxace->tag; + perms = pxace->perms; + pos = le16_to_cpu(pacl->size); + acecnt = le16_to_cpu(pacl->ace_count); + rootgroup = FALSE; + avoidmask = (pset->mask == (POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X)) + && ((pset->designates && pset->withmask) + || (!pset->designates && !pset->withmask)); + if (tag == POSIX_ACL_GROUP_OBJ) + sid = gsid; + else + if (pxace->id) + sid = NTFS_FIND_GSID(mapping[MAPGROUPS], + pxace->id, (SID*)&defsid); + else { + sid = adminsid; + rootgroup = TRUE; + } + if (sid) { + sidsz = ntfs_sid_size(sid); + /* + * Insert denial of complement of mask for + * each group + * WRITE_OWNER is inserted so that + * the mask can be identified + * Note : this mask may lead on Windows to + * deny rights to administrators belonging + * to some user group + */ + if ((!avoidmask && !rootgroup) + || (pset->rootspecial + && (tag == POSIX_ACL_GROUP_OBJ))) { + denials = WRITE_OWNER; + pdace = (ACCESS_DENIED_ACE*)&((char*)pacl)[pos]; + if (pset->isdir) { + if (!(pset->mask & POSIX_PERM_X)) + denials |= DIR_EXEC; + if (!(pset->mask & POSIX_PERM_W)) + denials |= DIR_WRITE; + if (!(pset->mask & POSIX_PERM_R)) + denials |= DIR_READ; + } else { + if (!(pset->mask & POSIX_PERM_X)) + denials |= FILE_EXEC; + if (!(pset->mask & POSIX_PERM_W)) + denials |= FILE_WRITE; + if (!(pset->mask & POSIX_PERM_R)) + denials |= FILE_READ; + } + pdace->type = ACCESS_DENIED_ACE_TYPE; + pdace->flags = flags; + pdace->size = cpu_to_le16(sidsz + 8); + pdace->mask = denials; + memcpy((char*)&pdace->sid, sid, sidsz); + pos += sidsz + 8; + acecnt++; + } + } else + rejected = TRUE; + if (!rejected + && (pset->adminowns + || pset->groupowns + || avoidmask + || rootgroup + || (perms != pset->othperms))) { + grants = WORLD_RIGHTS; + if (rootgroup) + grants &= ~ROOT_GROUP_UNMARK; + if (pset->isdir) { + if (perms & POSIX_PERM_X) + grants |= DIR_EXEC; + if (perms & POSIX_PERM_W) + grants |= DIR_WRITE; + if (perms & POSIX_PERM_R) + grants |= DIR_READ; + } else { + if (perms & POSIX_PERM_X) + grants |= FILE_EXEC; + if (perms & POSIX_PERM_W) + grants |= FILE_WRITE; + if (perms & POSIX_PERM_R) + grants |= FILE_READ; + } - /* a possible ACE to deny group what it would get from world */ - /* or administrator, unless owner is administrator or group */ + /* a possible ACE to deny group what it would get from world */ + /* or administrator, unless owner is administrator or group */ - denials = const_cpu_to_le32( 0 ); - pdace = ( ACCESS_DENIED_ACE* ) & ( ( char* )pacl )[pos]; - if ( !pset->adminowns - && !pset->groupowns - && !rootgroup ) - { - mixperms = pset->othperms; - if ( tag == POSIX_ACL_GROUP_OBJ ) - mixperms |= pset->selfgrpperms; - if ( pset->isdir ) - { - if ( mixperms & POSIX_PERM_X ) - denials |= DIR_EXEC; - if ( mixperms & POSIX_PERM_W ) - denials |= DIR_WRITE; - if ( mixperms & POSIX_PERM_R ) - denials |= DIR_READ; - } - else - { - if ( mixperms & POSIX_PERM_X ) - denials |= FILE_EXEC; - if ( mixperms & POSIX_PERM_W ) - denials |= FILE_WRITE; - if ( mixperms & POSIX_PERM_R ) - denials |= FILE_READ; - } - denials &= ~( grants | OWNER_RIGHTS ); - if ( denials ) - { - pdace->type = ACCESS_DENIED_ACE_TYPE; - pdace->flags = flags; - pdace->size = cpu_to_le16( sidsz + 8 ); - pdace->mask = denials; - memcpy( ( char* )&pdace->sid, sid, sidsz ); - pos += sidsz + 8; - acecnt++; - } - } + denials = const_cpu_to_le32(0); + pdace = (ACCESS_DENIED_ACE*)&((char*)pacl)[pos]; + if (!pset->adminowns + && !pset->groupowns + && !rootgroup) { + mixperms = pset->othperms; + if (tag == POSIX_ACL_GROUP_OBJ) + mixperms |= pset->selfgrpperms; + if (pset->isdir) { + if (mixperms & POSIX_PERM_X) + denials |= DIR_EXEC; + if (mixperms & POSIX_PERM_W) + denials |= DIR_WRITE; + if (mixperms & POSIX_PERM_R) + denials |= DIR_READ; + } else { + if (mixperms & POSIX_PERM_X) + denials |= FILE_EXEC; + if (mixperms & POSIX_PERM_W) + denials |= FILE_WRITE; + if (mixperms & POSIX_PERM_R) + denials |= FILE_READ; + } + denials &= ~(grants | OWNER_RIGHTS); + if (denials) { + pdace->type = ACCESS_DENIED_ACE_TYPE; + pdace->flags = flags; + pdace->size = cpu_to_le16(sidsz + 8); + pdace->mask = denials; + memcpy((char*)&pdace->sid, sid, sidsz); + pos += sidsz + 8; + acecnt++; + } + } - /* now insert grants to group if more than world */ - if ( pset->adminowns - || pset->groupowns - || ( avoidmask && ( pset->designates || pset->withmask ) ) - || ( perms & ~pset->othperms ) - || ( pset->rootspecial - && ( tag == POSIX_ACL_GROUP_OBJ ) ) - || ( tag == POSIX_ACL_GROUP ) ) - { - if ( rootgroup ) - grants &= ~ROOT_GROUP_UNMARK; - pgace = ( ACCESS_DENIED_ACE* ) & ( ( char* )pacl )[pos]; - pgace->type = ACCESS_ALLOWED_ACE_TYPE; - pgace->flags = flags; - pgace->size = cpu_to_le16( sidsz + 8 ); - pgace->mask = grants; - memcpy( ( char* )&pgace->sid, sid, sidsz ); - pos += sidsz + 8; - acecnt++; - } - } - pacl->size = cpu_to_le16( pos ); - pacl->ace_count = cpu_to_le16( acecnt ); - return ( !rejected ); + /* now insert grants to group if more than world */ + if (pset->adminowns + || pset->groupowns + || (avoidmask && (pset->designates || pset->withmask)) + || (perms & ~pset->othperms) + || (pset->rootspecial + && (tag == POSIX_ACL_GROUP_OBJ)) + || (tag == POSIX_ACL_GROUP)) { + if (rootgroup) + grants &= ~ROOT_GROUP_UNMARK; + pgace = (ACCESS_DENIED_ACE*)&((char*)pacl)[pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + pgace->flags = flags; + pgace->size = cpu_to_le16(sidsz + 8); + pgace->mask = grants; + memcpy((char*)&pgace->sid, sid, sidsz); + pos += sidsz + 8; + acecnt++; + } + } + pacl->size = cpu_to_le16(pos); + pacl->ace_count = cpu_to_le16(acecnt); + return (!rejected); } /* - * Build an ACL composed of several ACE's - * returns size of ACL or zero if failed + * Build an ACL composed of several ACE's + * returns size of ACL or zero if failed * - * Three schemes are defined : + * Three schemes are defined : * - * 1) if root is neither owner nor group up to 7 ACE's are set up : - * - denials to owner (preventing grants to world or group to apply) + * 1) if root is neither owner nor group up to 7 ACE's are set up : + * - denials to owner (preventing grants to world or group to apply) * + mask denials to designated user (unless mask allows all) * + denials to designated user - * - grants to owner (always present - first grant) + * - grants to owner (always present - first grant) * + grants to designated user * + mask denial to group (unless mask allows all) - * - denials to group (preventing grants to world to apply) - * - grants to group (unless group has no more than world rights) + * - denials to group (preventing grants to world to apply) + * - grants to group (unless group has no more than world rights) * + mask denials to designated group (unless mask allows all) * + grants to designated group * + denials to designated group - * - grants to world (unless none) - * - full privileges to administrator, always present - * - full privileges to system, always present + * - grants to world (unless none) + * - full privileges to administrator, always present + * - full privileges to system, always present * - * The same scheme is applied for Posix ACLs, with the mask represented - * as denials prepended to grants for designated users and groups + * The same scheme is applied for Posix ACLs, with the mask represented + * as denials prepended to grants for designated users and groups * - * This is inspired by an Internet Draft from Marius Aamodt Eriksen - * for mapping NFSv4 ACLs to Posix ACLs (draft-ietf-nfsv4-acl-mapping-00.txt) - * More recent versions of the draft (draft-ietf-nfsv4-acl-mapping-05.txt) - * are not followed, as they ignore the Posix mask and lead to - * loss of compatibility with Linux implementations on other fs. + * This is inspired by an Internet Draft from Marius Aamodt Eriksen + * for mapping NFSv4 ACLs to Posix ACLs (draft-ietf-nfsv4-acl-mapping-00.txt) + * More recent versions of the draft (draft-ietf-nfsv4-acl-mapping-05.txt) + * are not followed, as they ignore the Posix mask and lead to + * loss of compatibility with Linux implementations on other fs. * - * Note that denials to group are located after grants to owner. - * This only occurs in the unfrequent situation where world - * has more rights than group and cannot be avoided if owner and other - * have some common right which is denied to group (eg for mode 745 - * executing has to be denied to group, but not to owner or world). - * This rare situation is processed by Windows correctly, but - * Windows utilities may want to change the order, with a - * consequence of applying the group denials to the Windows owner. - * The interpretation on Linux is not affected by the order change. + * Note that denials to group are located after grants to owner. + * This only occurs in the unfrequent situation where world + * has more rights than group and cannot be avoided if owner and other + * have some common right which is denied to group (eg for mode 745 + * executing has to be denied to group, but not to owner or world). + * This rare situation is processed by Windows correctly, but + * Windows utilities may want to change the order, with a + * consequence of applying the group denials to the Windows owner. + * The interpretation on Linux is not affected by the order change. * - * 2) if root is either owner or group, two problems arise : - * - granting full rights to administrator (as needed to transpose - * to Windows rights bypassing granting to root) would imply - * Linux permissions to always be seen as rwx, no matter the chmod - * - there is no different SID to separate an administrator owner - * from an administrator group. Hence Linux permissions for owner - * would always be similar to permissions to group. + * 2) if root is either owner or group, two problems arise : + * - granting full rights to administrator (as needed to transpose + * to Windows rights bypassing granting to root) would imply + * Linux permissions to always be seen as rwx, no matter the chmod + * - there is no different SID to separate an administrator owner + * from an administrator group. Hence Linux permissions for owner + * would always be similar to permissions to group. * - * as a work-around, up to 5 ACE's are set up if owner or group : - * - grants to owner, always present at first position - * - grants to group, always present - * - grants to world, unless none - * - full privileges to administrator, always present - * - full privileges to system, always present + * as a work-around, up to 5 ACE's are set up if owner or group : + * - grants to owner, always present at first position + * - grants to group, always present + * - grants to world, unless none + * - full privileges to administrator, always present + * - full privileges to system, always present * - * On Windows, these ACE's are processed normally, though they - * are redundant (owner, group and administrator are the same, - * as a consequence any denials would damage administrator rights) - * but on Linux, privileges to administrator are ignored (they - * are not needed as root has always full privileges), and - * neither grants to group are applied to owner, nor grants to - * world are applied to owner or group. + * On Windows, these ACE's are processed normally, though they + * are redundant (owner, group and administrator are the same, + * as a consequence any denials would damage administrator rights) + * but on Linux, privileges to administrator are ignored (they + * are not needed as root has always full privileges), and + * neither grants to group are applied to owner, nor grants to + * world are applied to owner or group. * - * 3) finally a similar situation arises when group is owner (they - * have the same SID), but is not root. - * In this situation up to 6 ACE's are set up : + * 3) finally a similar situation arises when group is owner (they + * have the same SID), but is not root. + * In this situation up to 6 ACE's are set up : * - * - denials to owner (preventing grants to world to apply) - * - grants to owner (always present) - * - grants to group (unless groups has same rights as world) - * - grants to world (unless none) - * - full privileges to administrator, always present - * - full privileges to system, always present + * - denials to owner (preventing grants to world to apply) + * - grants to owner (always present) + * - grants to group (unless groups has same rights as world) + * - grants to world (unless none) + * - full privileges to administrator, always present + * - full privileges to system, always present * - * On Windows, these ACE's are processed normally, though they - * are redundant (as owner and group are the same), but this has - * no impact on administrator rights + * On Windows, these ACE's are processed normally, though they + * are redundant (as owner and group are the same), but this has + * no impact on administrator rights * - * Special flags (S_ISVTX, S_ISGID, S_ISUID) : - * an extra null ACE is inserted to hold these flags, using - * the same conventions as cygwin. + * Special flags (S_ISVTX, S_ISGID, S_ISUID) : + * an extra null ACE is inserted to hold these flags, using + * the same conventions as cygwin. * */ -static int buildacls_posix( struct MAPPING* const mapping[], - char *secattr, int offs, const struct POSIX_SECURITY *pxdesc, - int isdir, const SID *usid, const SID *gsid ) +static int buildacls_posix(struct MAPPING* const mapping[], + char *secattr, int offs, const struct POSIX_SECURITY *pxdesc, + int isdir, const SID *usid, const SID *gsid) { - struct BUILD_CONTEXT aceset[2], *pset; - BOOL adminowns; - BOOL groupowns; - ACL *pacl; - ACCESS_ALLOWED_ACE *pgace; - ACCESS_ALLOWED_ACE *pdace; - const struct POSIX_ACE *pxace; - BOOL ok; - mode_t mode; - u16 tag; - u16 perms; - ACE_FLAGS flags; - int pos; - int i; - int k; - BIGSID defsid; - const SID *sid; - int acecnt; - int usidsz; - int gsidsz; - int wsidsz; - int asidsz; - int ssidsz; - int nsidsz; - le32 grants; + struct BUILD_CONTEXT aceset[2], *pset; + BOOL adminowns; + BOOL groupowns; + ACL *pacl; + ACCESS_ALLOWED_ACE *pgace; + ACCESS_ALLOWED_ACE *pdace; + const struct POSIX_ACE *pxace; + BOOL ok; + mode_t mode; + u16 tag; + u16 perms; + ACE_FLAGS flags; + int pos; + int i; + int k; + BIGSID defsid; + const SID *sid; + int acecnt; + int usidsz; + int gsidsz; + int wsidsz; + int asidsz; + int ssidsz; + int nsidsz; + le32 grants; - usidsz = ntfs_sid_size( usid ); - gsidsz = ntfs_sid_size( gsid ); - wsidsz = ntfs_sid_size( worldsid ); - asidsz = ntfs_sid_size( adminsid ); - ssidsz = ntfs_sid_size( systemsid ); - mode = pxdesc->mode; - /* adminowns and groupowns are used for both lists */ - adminowns = ntfs_same_sid( usid, adminsid ) - || ntfs_same_sid( gsid, adminsid ); - groupowns = !adminowns && ntfs_same_sid( usid, gsid ); + usidsz = ntfs_sid_size(usid); + gsidsz = ntfs_sid_size(gsid); + wsidsz = ntfs_sid_size(worldsid); + asidsz = ntfs_sid_size(adminsid); + ssidsz = ntfs_sid_size(systemsid); + mode = pxdesc->mode; + /* adminowns and groupowns are used for both lists */ + adminowns = ntfs_same_sid(usid, adminsid) + || ntfs_same_sid(gsid, adminsid); + groupowns = !adminowns && ntfs_same_sid(usid, gsid); - ok = TRUE; + ok = TRUE; - /* ACL header */ - pacl = ( ACL* ) & secattr[offs]; - pacl->revision = ACL_REVISION; - pacl->alignment1 = 0; - pacl->size = cpu_to_le16( sizeof( ACL ) + usidsz + 8 ); - pacl->ace_count = const_cpu_to_le16( 0 ); - pacl->alignment2 = const_cpu_to_le16( 0 ); + /* ACL header */ + pacl = (ACL*)&secattr[offs]; + pacl->revision = ACL_REVISION; + pacl->alignment1 = 0; + pacl->size = cpu_to_le16(sizeof(ACL) + usidsz + 8); + pacl->ace_count = const_cpu_to_le16(0); + pacl->alignment2 = const_cpu_to_le16(0); - /* - * Determine what is allowed to some group or world - * to prevent designated users or other groups to get - * rights from groups or world - * Do the same if owner and group appear as designated - * user or group - * Also get global mask - */ - for ( k = 0; k < 2; k++ ) - { - pset = &aceset[k]; - pset->selfuserperms = 0; - pset->selfgrpperms = 0; - pset->grpperms = 0; - pset->othperms = 0; - pset->mask = ( POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X ); - pset->designates = 0; - pset->withmask = 0; - pset->rootspecial = 0; - pset->adminowns = adminowns; - pset->groupowns = groupowns; - pset->isdir = isdir; - } + /* + * Determine what is allowed to some group or world + * to prevent designated users or other groups to get + * rights from groups or world + * Do the same if owner and group appear as designated + * user or group + * Also get global mask + */ + for (k=0; k<2; k++) { + pset = &aceset[k]; + pset->selfuserperms = 0; + pset->selfgrpperms = 0; + pset->grpperms = 0; + pset->othperms = 0; + pset->mask = (POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X); + pset->designates = 0; + pset->withmask = 0; + pset->rootspecial = 0; + pset->adminowns = adminowns; + pset->groupowns = groupowns; + pset->isdir = isdir; + } - for ( i = pxdesc->acccnt + pxdesc->defcnt - 1; i >= 0; i-- ) - { - if ( i >= pxdesc->acccnt ) - { - pset = &aceset[1]; - pxace = &pxdesc->acl.ace[i + pxdesc->firstdef - pxdesc->acccnt]; - } - else - { - pset = &aceset[0]; - pxace = &pxdesc->acl.ace[i]; - } - switch ( pxace->tag ) - { - case POSIX_ACL_USER : - pset->designates++; - if ( pxace->id ) - { - sid = NTFS_FIND_USID( mapping[MAPUSERS], - pxace->id, ( SID* ) & defsid ); - if ( sid && ntfs_same_sid( sid, usid ) ) - pset->selfuserperms |= pxace->perms; - } - else - /* root as designated user is processed apart */ - pset->rootspecial = TRUE; - break; - case POSIX_ACL_GROUP : - pset->designates++; - if ( pxace->id ) - { - sid = NTFS_FIND_GSID( mapping[MAPUSERS], - pxace->id, ( SID* ) & defsid ); - if ( sid && ntfs_same_sid( sid, gsid ) ) - pset->selfgrpperms |= pxace->perms; - } - else - /* root as designated group is processed apart */ - pset->rootspecial = TRUE; - /* fall through */ - case POSIX_ACL_GROUP_OBJ : - pset->grpperms |= pxace->perms; - break; - case POSIX_ACL_OTHER : - pset->othperms = pxace->perms; - break; - case POSIX_ACL_MASK : - pset->withmask++; - pset->mask = pxace->perms; - default : - break; - } - } + for (i=pxdesc->acccnt+pxdesc->defcnt-1; i>=0; i--) { + if (i >= pxdesc->acccnt) { + pset = &aceset[1]; + pxace = &pxdesc->acl.ace[i + pxdesc->firstdef - pxdesc->acccnt]; + } else { + pset = &aceset[0]; + pxace = &pxdesc->acl.ace[i]; + } + switch (pxace->tag) { + case POSIX_ACL_USER : + pset->designates++; + if (pxace->id) { + sid = NTFS_FIND_USID(mapping[MAPUSERS], + pxace->id, (SID*)&defsid); + if (sid && ntfs_same_sid(sid,usid)) + pset->selfuserperms |= pxace->perms; + } else + /* root as designated user is processed apart */ + pset->rootspecial = TRUE; + break; + case POSIX_ACL_GROUP : + pset->designates++; + if (pxace->id) { + sid = NTFS_FIND_GSID(mapping[MAPUSERS], + pxace->id, (SID*)&defsid); + if (sid && ntfs_same_sid(sid,gsid)) + pset->selfgrpperms |= pxace->perms; + } else + /* root as designated group is processed apart */ + pset->rootspecial = TRUE; + /* fall through */ + case POSIX_ACL_GROUP_OBJ : + pset->grpperms |= pxace->perms; + break; + case POSIX_ACL_OTHER : + pset->othperms = pxace->perms; + break; + case POSIX_ACL_MASK : + pset->withmask++; + pset->mask = pxace->perms; + default : + break; + } + } - if ( pxdesc->defcnt && ( pxdesc->firstdef != pxdesc->acccnt ) ) - { - ntfs_log_error( "** error : access and default not consecutive\n" ); - return ( 0 ); - } - /* - * First insert all denials for owner and each - * designated user (with mask if needed) - */ +if (pxdesc->defcnt && (pxdesc->firstdef != pxdesc->acccnt)) { +ntfs_log_error("** error : access and default not consecutive\n"); +return (0); +} + /* + * First insert all denials for owner and each + * designated user (with mask if needed) + */ - pacl->ace_count = const_cpu_to_le16( 0 ); - pacl->size = const_cpu_to_le16( sizeof( ACL ) ); - for ( i = 0; ( i < ( pxdesc->acccnt + pxdesc->defcnt ) ) && ok; i++ ) - { - if ( i >= pxdesc->acccnt ) - { - flags = INHERIT_ONLY_ACE - | OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE; - pset = &aceset[1]; - pxace = &pxdesc->acl.ace[i + pxdesc->firstdef - pxdesc->acccnt]; - } - else - { - if ( pxdesc->defcnt ) - flags = NO_PROPAGATE_INHERIT_ACE; - else - flags = ( isdir ? DIR_INHERITANCE - : FILE_INHERITANCE ); - pset = &aceset[0]; - pxace = &pxdesc->acl.ace[i]; - } - tag = pxace->tag; - perms = pxace->perms; - switch ( tag ) - { + pacl->ace_count = const_cpu_to_le16(0); + pacl->size = const_cpu_to_le16(sizeof(ACL)); + for (i=0; (i<(pxdesc->acccnt + pxdesc->defcnt)) && ok; i++) { + if (i >= pxdesc->acccnt) { + flags = INHERIT_ONLY_ACE + | OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE; + pset = &aceset[1]; + pxace = &pxdesc->acl.ace[i + pxdesc->firstdef - pxdesc->acccnt]; + } else { + if (pxdesc->defcnt) + flags = NO_PROPAGATE_INHERIT_ACE; + else + flags = (isdir ? DIR_INHERITANCE + : FILE_INHERITANCE); + pset = &aceset[0]; + pxace = &pxdesc->acl.ace[i]; + } + tag = pxace->tag; + perms = pxace->perms; + switch (tag) { - /* insert denial ACEs for each owner or allowed user */ + /* insert denial ACEs for each owner or allowed user */ - case POSIX_ACL_USER : - case POSIX_ACL_USER_OBJ : + case POSIX_ACL_USER : + case POSIX_ACL_USER_OBJ : - ok = build_user_denials( pacl, - usid, mapping, flags, pxace, pset ); - break; - default : - break; - } - } + ok = build_user_denials(pacl, + usid, mapping, flags, pxace, pset); + break; + default : + break; + } + } - /* - * for directories, insert a world execution denial - * inherited to plain files. - * This is to prevent Windows from granting execution - * of files through inheritance from parent directory - */ + /* + * for directories, insert a world execution denial + * inherited to plain files. + * This is to prevent Windows from granting execution + * of files through inheritance from parent directory + */ - if ( isdir && ok ) - { - pos = le16_to_cpu( pacl->size ); - pdace = ( ACCESS_DENIED_ACE* ) & secattr[offs + pos]; - pdace->type = ACCESS_DENIED_ACE_TYPE; - pdace->flags = INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE; - pdace->size = cpu_to_le16( wsidsz + 8 ); - pdace->mask = FILE_EXEC; - memcpy( ( char* )&pdace->sid, worldsid, wsidsz ); - pos += wsidsz + 8; - acecnt = le16_to_cpu( pacl->ace_count ) + 1; - pacl->ace_count = cpu_to_le16( acecnt ); - pacl->size = cpu_to_le16( pos ); - } + if (isdir && ok) { + pos = le16_to_cpu(pacl->size); + pdace = (ACCESS_DENIED_ACE*) &secattr[offs + pos]; + pdace->type = ACCESS_DENIED_ACE_TYPE; + pdace->flags = INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE; + pdace->size = cpu_to_le16(wsidsz + 8); + pdace->mask = FILE_EXEC; + memcpy((char*)&pdace->sid, worldsid, wsidsz); + pos += wsidsz + 8; + acecnt = le16_to_cpu(pacl->ace_count) + 1; + pacl->ace_count = cpu_to_le16(acecnt); + pacl->size = cpu_to_le16(pos); + } - /* - * now insert (if needed) - * - grants to owner and designated users - * - mask and denials for all groups - * - grants to other - */ + /* + * now insert (if needed) + * - grants to owner and designated users + * - mask and denials for all groups + * - grants to other + */ - for ( i = 0; ( i < ( pxdesc->acccnt + pxdesc->defcnt ) ) && ok; i++ ) - { - if ( i >= pxdesc->acccnt ) - { - flags = INHERIT_ONLY_ACE - | OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE; - pset = &aceset[1]; - pxace = &pxdesc->acl.ace[i + pxdesc->firstdef - pxdesc->acccnt]; - } - else - { - if ( pxdesc->defcnt ) - flags = NO_PROPAGATE_INHERIT_ACE; - else - flags = ( isdir ? DIR_INHERITANCE - : FILE_INHERITANCE ); - pset = &aceset[0]; - pxace = &pxdesc->acl.ace[i]; - } - tag = pxace->tag; - perms = pxace->perms; - switch ( tag ) - { + for (i=0; (i<(pxdesc->acccnt + pxdesc->defcnt)) && ok; i++) { + if (i >= pxdesc->acccnt) { + flags = INHERIT_ONLY_ACE + | OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE; + pset = &aceset[1]; + pxace = &pxdesc->acl.ace[i + pxdesc->firstdef - pxdesc->acccnt]; + } else { + if (pxdesc->defcnt) + flags = NO_PROPAGATE_INHERIT_ACE; + else + flags = (isdir ? DIR_INHERITANCE + : FILE_INHERITANCE); + pset = &aceset[0]; + pxace = &pxdesc->acl.ace[i]; + } + tag = pxace->tag; + perms = pxace->perms; + switch (tag) { - /* ACE for each owner or allowed user */ + /* ACE for each owner or allowed user */ - case POSIX_ACL_USER : - case POSIX_ACL_USER_OBJ : - ok = build_user_grants( pacl, usid, - mapping, flags, pxace, pset ); - break; + case POSIX_ACL_USER : + case POSIX_ACL_USER_OBJ : + ok = build_user_grants(pacl,usid, + mapping,flags,pxace,pset); + break; - case POSIX_ACL_GROUP : - case POSIX_ACL_GROUP_OBJ : + case POSIX_ACL_GROUP : + case POSIX_ACL_GROUP_OBJ : - /* denials and grants for groups */ + /* denials and grants for groups */ - ok = build_group_denials_grant( pacl, gsid, - mapping, flags, pxace, pset ); - break; + ok = build_group_denials_grant(pacl,gsid, + mapping,flags,pxace,pset); + break; - case POSIX_ACL_OTHER : + case POSIX_ACL_OTHER : - /* grants for other users */ + /* grants for other users */ - pos = le16_to_cpu( pacl->size ); - pgace = ( ACCESS_ALLOWED_ACE* ) & secattr[offs + pos]; - grants = WORLD_RIGHTS; - if ( isdir ) - { - if ( perms & POSIX_PERM_X ) - grants |= DIR_EXEC; - if ( perms & POSIX_PERM_W ) - grants |= DIR_WRITE; - if ( perms & POSIX_PERM_R ) - grants |= DIR_READ; - } - else - { - if ( perms & POSIX_PERM_X ) - grants |= FILE_EXEC; - if ( perms & POSIX_PERM_W ) - grants |= FILE_WRITE; - if ( perms & POSIX_PERM_R ) - grants |= FILE_READ; - } - pgace->type = ACCESS_ALLOWED_ACE_TYPE; - pgace->flags = flags; - pgace->size = cpu_to_le16( wsidsz + 8 ); - pgace->mask = grants; - memcpy( ( char* )&pgace->sid, worldsid, wsidsz ); - pos += wsidsz + 8; - acecnt = le16_to_cpu( pacl->ace_count ) + 1; - pacl->ace_count = cpu_to_le16( acecnt ); - pacl->size = cpu_to_le16( pos ); - break; - } - } + pos = le16_to_cpu(pacl->size); + pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + grants = WORLD_RIGHTS; + if (isdir) { + if (perms & POSIX_PERM_X) + grants |= DIR_EXEC; + if (perms & POSIX_PERM_W) + grants |= DIR_WRITE; + if (perms & POSIX_PERM_R) + grants |= DIR_READ; + } else { + if (perms & POSIX_PERM_X) + grants |= FILE_EXEC; + if (perms & POSIX_PERM_W) + grants |= FILE_WRITE; + if (perms & POSIX_PERM_R) + grants |= FILE_READ; + } + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + pgace->flags = flags; + pgace->size = cpu_to_le16(wsidsz + 8); + pgace->mask = grants; + memcpy((char*)&pgace->sid, worldsid, wsidsz); + pos += wsidsz + 8; + acecnt = le16_to_cpu(pacl->ace_count) + 1; + pacl->ace_count = cpu_to_le16(acecnt); + pacl->size = cpu_to_le16(pos); + break; + } + } - if ( !ok ) - { - errno = EINVAL; - pos = 0; - } - else - { - /* an ACE for administrators */ - /* always full access */ + if (!ok) { + errno = EINVAL; + pos = 0; + } else { + /* an ACE for administrators */ + /* always full access */ - pos = le16_to_cpu( pacl->size ); - acecnt = le16_to_cpu( pacl->ace_count ); - if ( isdir ) - flags = OBJECT_INHERIT_ACE - | CONTAINER_INHERIT_ACE; - else - flags = NO_PROPAGATE_INHERIT_ACE; - pgace = ( ACCESS_ALLOWED_ACE* ) & secattr[offs + pos]; - pgace->type = ACCESS_ALLOWED_ACE_TYPE; - pgace->flags = flags; - pgace->size = cpu_to_le16( asidsz + 8 ); - grants = OWNER_RIGHTS | FILE_READ | FILE_WRITE | FILE_EXEC; - pgace->mask = grants; - memcpy( ( char* )&pgace->sid, adminsid, asidsz ); - pos += asidsz + 8; - acecnt++; + pos = le16_to_cpu(pacl->size); + acecnt = le16_to_cpu(pacl->ace_count); + if (isdir) + flags = OBJECT_INHERIT_ACE + | CONTAINER_INHERIT_ACE; + else + flags = NO_PROPAGATE_INHERIT_ACE; + pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + pgace->flags = flags; + pgace->size = cpu_to_le16(asidsz + 8); + grants = OWNER_RIGHTS | FILE_READ | FILE_WRITE | FILE_EXEC; + pgace->mask = grants; + memcpy((char*)&pgace->sid, adminsid, asidsz); + pos += asidsz + 8; + acecnt++; - /* an ACE for system (needed ?) */ - /* always full access */ + /* an ACE for system (needed ?) */ + /* always full access */ - pgace = ( ACCESS_ALLOWED_ACE* ) & secattr[offs + pos]; - pgace->type = ACCESS_ALLOWED_ACE_TYPE; - pgace->flags = flags; - pgace->size = cpu_to_le16( ssidsz + 8 ); - grants = OWNER_RIGHTS | FILE_READ | FILE_WRITE | FILE_EXEC; - pgace->mask = grants; - memcpy( ( char* )&pgace->sid, systemsid, ssidsz ); - pos += ssidsz + 8; - acecnt++; + pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + pgace->flags = flags; + pgace->size = cpu_to_le16(ssidsz + 8); + grants = OWNER_RIGHTS | FILE_READ | FILE_WRITE | FILE_EXEC; + pgace->mask = grants; + memcpy((char*)&pgace->sid, systemsid, ssidsz); + pos += ssidsz + 8; + acecnt++; - /* a null ACE to hold special flags */ - /* using the same representation as cygwin */ + /* a null ACE to hold special flags */ + /* using the same representation as cygwin */ - if ( mode & ( S_ISVTX | S_ISGID | S_ISUID ) ) - { - nsidsz = ntfs_sid_size( nullsid ); - pgace = ( ACCESS_ALLOWED_ACE* ) & secattr[offs + pos]; - pgace->type = ACCESS_ALLOWED_ACE_TYPE; - pgace->flags = NO_PROPAGATE_INHERIT_ACE; - pgace->size = cpu_to_le16( nsidsz + 8 ); - grants = const_cpu_to_le32( 0 ); - if ( mode & S_ISUID ) - grants |= FILE_APPEND_DATA; - if ( mode & S_ISGID ) - grants |= FILE_WRITE_DATA; - if ( mode & S_ISVTX ) - grants |= FILE_READ_DATA; - pgace->mask = grants; - memcpy( ( char* )&pgace->sid, nullsid, nsidsz ); - pos += nsidsz + 8; - acecnt++; - } + if (mode & (S_ISVTX | S_ISGID | S_ISUID)) { + nsidsz = ntfs_sid_size(nullsid); + pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + pgace->flags = NO_PROPAGATE_INHERIT_ACE; + pgace->size = cpu_to_le16(nsidsz + 8); + grants = const_cpu_to_le32(0); + if (mode & S_ISUID) + grants |= FILE_APPEND_DATA; + if (mode & S_ISGID) + grants |= FILE_WRITE_DATA; + if (mode & S_ISVTX) + grants |= FILE_READ_DATA; + pgace->mask = grants; + memcpy((char*)&pgace->sid, nullsid, nsidsz); + pos += nsidsz + 8; + acecnt++; + } - /* fix ACL header */ - pacl->size = cpu_to_le16( pos ); - pacl->ace_count = cpu_to_le16( acecnt ); - } - return ( ok ? pos : 0 ); + /* fix ACL header */ + pacl->size = cpu_to_le16(pos); + pacl->ace_count = cpu_to_le16(acecnt); + } + return (ok ? pos : 0); } #endif /* POSIXACLS */ -static int buildacls( char *secattr, int offs, mode_t mode, int isdir, - const SID * usid, const SID * gsid ) +static int buildacls(char *secattr, int offs, mode_t mode, int isdir, + const SID * usid, const SID * gsid) { - ACL *pacl; - ACCESS_ALLOWED_ACE *pgace; - ACCESS_ALLOWED_ACE *pdace; - BOOL adminowns; - BOOL groupowns; - ACE_FLAGS gflags; - int pos; - int acecnt; - int usidsz; - int gsidsz; - int wsidsz; - int asidsz; - int ssidsz; - int nsidsz; - le32 grants; - le32 denials; + ACL *pacl; + ACCESS_ALLOWED_ACE *pgace; + ACCESS_ALLOWED_ACE *pdace; + BOOL adminowns; + BOOL groupowns; + ACE_FLAGS gflags; + int pos; + int acecnt; + int usidsz; + int gsidsz; + int wsidsz; + int asidsz; + int ssidsz; + int nsidsz; + le32 grants; + le32 denials; - usidsz = ntfs_sid_size( usid ); - gsidsz = ntfs_sid_size( gsid ); - wsidsz = ntfs_sid_size( worldsid ); - asidsz = ntfs_sid_size( adminsid ); - ssidsz = ntfs_sid_size( systemsid ); - adminowns = ntfs_same_sid( usid, adminsid ) - || ntfs_same_sid( gsid, adminsid ); - groupowns = !adminowns && ntfs_same_sid( usid, gsid ); + usidsz = ntfs_sid_size(usid); + gsidsz = ntfs_sid_size(gsid); + wsidsz = ntfs_sid_size(worldsid); + asidsz = ntfs_sid_size(adminsid); + ssidsz = ntfs_sid_size(systemsid); + adminowns = ntfs_same_sid(usid, adminsid) + || ntfs_same_sid(gsid, adminsid); + groupowns = !adminowns && ntfs_same_sid(usid, gsid); - /* ACL header */ - pacl = ( ACL* ) & secattr[offs]; - pacl->revision = ACL_REVISION; - pacl->alignment1 = 0; - pacl->size = cpu_to_le16( sizeof( ACL ) + usidsz + 8 ); - pacl->ace_count = const_cpu_to_le16( 1 ); - pacl->alignment2 = const_cpu_to_le16( 0 ); - pos = sizeof( ACL ); - acecnt = 0; + /* ACL header */ + pacl = (ACL*)&secattr[offs]; + pacl->revision = ACL_REVISION; + pacl->alignment1 = 0; + pacl->size = cpu_to_le16(sizeof(ACL) + usidsz + 8); + pacl->ace_count = const_cpu_to_le16(1); + pacl->alignment2 = const_cpu_to_le16(0); + pos = sizeof(ACL); + acecnt = 0; - /* compute a grant ACE for owner */ - /* this ACE will be inserted after denial for owner */ + /* compute a grant ACE for owner */ + /* this ACE will be inserted after denial for owner */ - grants = OWNER_RIGHTS; - if ( isdir ) - { - gflags = DIR_INHERITANCE; - if ( mode & S_IXUSR ) - grants |= DIR_EXEC; - if ( mode & S_IWUSR ) - grants |= DIR_WRITE; - if ( mode & S_IRUSR ) - grants |= DIR_READ; - } - else - { - gflags = FILE_INHERITANCE; - if ( mode & S_IXUSR ) - grants |= FILE_EXEC; - if ( mode & S_IWUSR ) - grants |= FILE_WRITE; - if ( mode & S_IRUSR ) - grants |= FILE_READ; - } + grants = OWNER_RIGHTS; + if (isdir) { + gflags = DIR_INHERITANCE; + if (mode & S_IXUSR) + grants |= DIR_EXEC; + if (mode & S_IWUSR) + grants |= DIR_WRITE; + if (mode & S_IRUSR) + grants |= DIR_READ; + } else { + gflags = FILE_INHERITANCE; + if (mode & S_IXUSR) + grants |= FILE_EXEC; + if (mode & S_IWUSR) + grants |= FILE_WRITE; + if (mode & S_IRUSR) + grants |= FILE_READ; + } - /* a possible ACE to deny owner what he/she would */ - /* induely get from administrator, group or world */ - /* unless owner is administrator or group */ + /* a possible ACE to deny owner what he/she would */ + /* induely get from administrator, group or world */ + /* unless owner is administrator or group */ - denials = const_cpu_to_le32( 0 ); - pdace = ( ACCESS_DENIED_ACE* ) & secattr[offs + pos]; - if ( !adminowns ) - { - if ( !groupowns ) - { - if ( isdir ) - { - pdace->flags = DIR_INHERITANCE; - if ( mode & ( S_IXGRP | S_IXOTH ) ) - denials |= DIR_EXEC; - if ( mode & ( S_IWGRP | S_IWOTH ) ) - denials |= DIR_WRITE; - if ( mode & ( S_IRGRP | S_IROTH ) ) - denials |= DIR_READ; - } - else - { - pdace->flags = FILE_INHERITANCE; - if ( mode & ( S_IXGRP | S_IXOTH ) ) - denials |= FILE_EXEC; - if ( mode & ( S_IWGRP | S_IWOTH ) ) - denials |= FILE_WRITE; - if ( mode & ( S_IRGRP | S_IROTH ) ) - denials |= FILE_READ; - } - } - else - { - if ( isdir ) - { - pdace->flags = DIR_INHERITANCE; - if ( ( mode & S_IXOTH ) && !( mode & S_IXGRP ) ) - denials |= DIR_EXEC; - if ( ( mode & S_IWOTH ) && !( mode & S_IWGRP ) ) - denials |= DIR_WRITE; - if ( ( mode & S_IROTH ) && !( mode & S_IRGRP ) ) - denials |= DIR_READ; - } - else - { - pdace->flags = FILE_INHERITANCE; - if ( ( mode & S_IXOTH ) && !( mode & S_IXGRP ) ) - denials |= FILE_EXEC; - if ( ( mode & S_IWOTH ) && !( mode & S_IWGRP ) ) - denials |= FILE_WRITE; - if ( ( mode & S_IROTH ) && !( mode & S_IRGRP ) ) - denials |= FILE_READ; - } - } - denials &= ~grants; - if ( denials ) - { - pdace->type = ACCESS_DENIED_ACE_TYPE; - pdace->size = cpu_to_le16( usidsz + 8 ); - pdace->mask = denials; - memcpy( ( char* )&pdace->sid, usid, usidsz ); - pos += usidsz + 8; - acecnt++; - } - } - /* - * for directories, a world execution denial - * inherited to plain files - */ + denials = const_cpu_to_le32(0); + pdace = (ACCESS_DENIED_ACE*) &secattr[offs + pos]; + if (!adminowns) { + if (!groupowns) { + if (isdir) { + pdace->flags = DIR_INHERITANCE; + if (mode & (S_IXGRP | S_IXOTH)) + denials |= DIR_EXEC; + if (mode & (S_IWGRP | S_IWOTH)) + denials |= DIR_WRITE; + if (mode & (S_IRGRP | S_IROTH)) + denials |= DIR_READ; + } else { + pdace->flags = FILE_INHERITANCE; + if (mode & (S_IXGRP | S_IXOTH)) + denials |= FILE_EXEC; + if (mode & (S_IWGRP | S_IWOTH)) + denials |= FILE_WRITE; + if (mode & (S_IRGRP | S_IROTH)) + denials |= FILE_READ; + } + } else { + if (isdir) { + pdace->flags = DIR_INHERITANCE; + if ((mode & S_IXOTH) && !(mode & S_IXGRP)) + denials |= DIR_EXEC; + if ((mode & S_IWOTH) && !(mode & S_IWGRP)) + denials |= DIR_WRITE; + if ((mode & S_IROTH) && !(mode & S_IRGRP)) + denials |= DIR_READ; + } else { + pdace->flags = FILE_INHERITANCE; + if ((mode & S_IXOTH) && !(mode & S_IXGRP)) + denials |= FILE_EXEC; + if ((mode & S_IWOTH) && !(mode & S_IWGRP)) + denials |= FILE_WRITE; + if ((mode & S_IROTH) && !(mode & S_IRGRP)) + denials |= FILE_READ; + } + } + denials &= ~grants; + if (denials) { + pdace->type = ACCESS_DENIED_ACE_TYPE; + pdace->size = cpu_to_le16(usidsz + 8); + pdace->mask = denials; + memcpy((char*)&pdace->sid, usid, usidsz); + pos += usidsz + 8; + acecnt++; + } + } + /* + * for directories, a world execution denial + * inherited to plain files + */ - if ( isdir ) - { - pdace = ( ACCESS_DENIED_ACE* ) & secattr[offs + pos]; - pdace->type = ACCESS_DENIED_ACE_TYPE; - pdace->flags = INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE; - pdace->size = cpu_to_le16( wsidsz + 8 ); - pdace->mask = FILE_EXEC; - memcpy( ( char* )&pdace->sid, worldsid, wsidsz ); - pos += wsidsz + 8; - acecnt++; - } + if (isdir) { + pdace = (ACCESS_DENIED_ACE*) &secattr[offs + pos]; + pdace->type = ACCESS_DENIED_ACE_TYPE; + pdace->flags = INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE; + pdace->size = cpu_to_le16(wsidsz + 8); + pdace->mask = FILE_EXEC; + memcpy((char*)&pdace->sid, worldsid, wsidsz); + pos += wsidsz + 8; + acecnt++; + } - /* now insert grants to owner */ - pgace = ( ACCESS_ALLOWED_ACE* ) & secattr[offs + pos]; - pgace->type = ACCESS_ALLOWED_ACE_TYPE; - pgace->size = cpu_to_le16( usidsz + 8 ); - pgace->flags = gflags; - pgace->mask = grants; - memcpy( ( char* )&pgace->sid, usid, usidsz ); - pos += usidsz + 8; - acecnt++; + /* now insert grants to owner */ + pgace = (ACCESS_ALLOWED_ACE*) &secattr[offs + pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + pgace->size = cpu_to_le16(usidsz + 8); + pgace->flags = gflags; + pgace->mask = grants; + memcpy((char*)&pgace->sid, usid, usidsz); + pos += usidsz + 8; + acecnt++; - /* a grant ACE for group */ - /* unless group has the same rights as world */ - /* but present if group is owner or owner is administrator */ - /* this ACE will be inserted after denials for group */ + /* a grant ACE for group */ + /* unless group has the same rights as world */ + /* but present if group is owner or owner is administrator */ + /* this ACE will be inserted after denials for group */ - if ( adminowns - || groupowns - || ( ( ( mode >> 3 ) ^ mode ) & 7 ) ) - { - grants = WORLD_RIGHTS; - if ( isdir ) - { - gflags = DIR_INHERITANCE; - if ( mode & S_IXGRP ) - grants |= DIR_EXEC; - if ( mode & S_IWGRP ) - grants |= DIR_WRITE; - if ( mode & S_IRGRP ) - grants |= DIR_READ; - } - else - { - gflags = FILE_INHERITANCE; - if ( mode & S_IXGRP ) - grants |= FILE_EXEC; - if ( mode & S_IWGRP ) - grants |= FILE_WRITE; - if ( mode & S_IRGRP ) - grants |= FILE_READ; - } + if (adminowns + || groupowns + || (((mode >> 3) ^ mode) & 7)) { + grants = WORLD_RIGHTS; + if (isdir) { + gflags = DIR_INHERITANCE; + if (mode & S_IXGRP) + grants |= DIR_EXEC; + if (mode & S_IWGRP) + grants |= DIR_WRITE; + if (mode & S_IRGRP) + grants |= DIR_READ; + } else { + gflags = FILE_INHERITANCE; + if (mode & S_IXGRP) + grants |= FILE_EXEC; + if (mode & S_IWGRP) + grants |= FILE_WRITE; + if (mode & S_IRGRP) + grants |= FILE_READ; + } - /* a possible ACE to deny group what it would get from world */ - /* or administrator, unless owner is administrator or group */ + /* a possible ACE to deny group what it would get from world */ + /* or administrator, unless owner is administrator or group */ - denials = const_cpu_to_le32( 0 ); - pdace = ( ACCESS_ALLOWED_ACE* ) & secattr[offs + pos]; - if ( !adminowns && !groupowns ) - { - if ( isdir ) - { - pdace->flags = DIR_INHERITANCE; - if ( mode & S_IXOTH ) - denials |= DIR_EXEC; - if ( mode & S_IWOTH ) - denials |= DIR_WRITE; - if ( mode & S_IROTH ) - denials |= DIR_READ; - } - else - { - pdace->flags = FILE_INHERITANCE; - if ( mode & S_IXOTH ) - denials |= FILE_EXEC; - if ( mode & S_IWOTH ) - denials |= FILE_WRITE; - if ( mode & S_IROTH ) - denials |= FILE_READ; - } - denials &= ~( grants | OWNER_RIGHTS ); - if ( denials ) - { - pdace->type = ACCESS_DENIED_ACE_TYPE; - pdace->size = cpu_to_le16( gsidsz + 8 ); - pdace->mask = denials; - memcpy( ( char* )&pdace->sid, gsid, gsidsz ); - pos += gsidsz + 8; - acecnt++; - } - } + denials = const_cpu_to_le32(0); + pdace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + if (!adminowns && !groupowns) { + if (isdir) { + pdace->flags = DIR_INHERITANCE; + if (mode & S_IXOTH) + denials |= DIR_EXEC; + if (mode & S_IWOTH) + denials |= DIR_WRITE; + if (mode & S_IROTH) + denials |= DIR_READ; + } else { + pdace->flags = FILE_INHERITANCE; + if (mode & S_IXOTH) + denials |= FILE_EXEC; + if (mode & S_IWOTH) + denials |= FILE_WRITE; + if (mode & S_IROTH) + denials |= FILE_READ; + } + denials &= ~(grants | OWNER_RIGHTS); + if (denials) { + pdace->type = ACCESS_DENIED_ACE_TYPE; + pdace->size = cpu_to_le16(gsidsz + 8); + pdace->mask = denials; + memcpy((char*)&pdace->sid, gsid, gsidsz); + pos += gsidsz + 8; + acecnt++; + } + } - if ( adminowns - || groupowns - || ( ( mode >> 3 ) & ~mode & 7 ) ) - { - /* now insert grants to group */ - /* if more rights than other */ - pgace = ( ACCESS_ALLOWED_ACE* ) & secattr[offs + pos]; - pgace->type = ACCESS_ALLOWED_ACE_TYPE; - pgace->flags = gflags; - pgace->size = cpu_to_le16( gsidsz + 8 ); - pgace->mask = grants; - memcpy( ( char* )&pgace->sid, gsid, gsidsz ); - pos += gsidsz + 8; - acecnt++; - } - } + if (adminowns + || groupowns + || ((mode >> 3) & ~mode & 7)) { + /* now insert grants to group */ + /* if more rights than other */ + pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + pgace->flags = gflags; + pgace->size = cpu_to_le16(gsidsz + 8); + pgace->mask = grants; + memcpy((char*)&pgace->sid, gsid, gsidsz); + pos += gsidsz + 8; + acecnt++; + } + } - /* an ACE for world users */ + /* an ACE for world users */ - pgace = ( ACCESS_ALLOWED_ACE* ) & secattr[offs + pos]; - pgace->type = ACCESS_ALLOWED_ACE_TYPE; - grants = WORLD_RIGHTS; - if ( isdir ) - { - pgace->flags = DIR_INHERITANCE; - if ( mode & S_IXOTH ) - grants |= DIR_EXEC; - if ( mode & S_IWOTH ) - grants |= DIR_WRITE; - if ( mode & S_IROTH ) - grants |= DIR_READ; - } - else - { - pgace->flags = FILE_INHERITANCE; - if ( mode & S_IXOTH ) - grants |= FILE_EXEC; - if ( mode & S_IWOTH ) - grants |= FILE_WRITE; - if ( mode & S_IROTH ) - grants |= FILE_READ; - } - pgace->size = cpu_to_le16( wsidsz + 8 ); - pgace->mask = grants; - memcpy( ( char* )&pgace->sid, worldsid, wsidsz ); - pos += wsidsz + 8; - acecnt++; + pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + grants = WORLD_RIGHTS; + if (isdir) { + pgace->flags = DIR_INHERITANCE; + if (mode & S_IXOTH) + grants |= DIR_EXEC; + if (mode & S_IWOTH) + grants |= DIR_WRITE; + if (mode & S_IROTH) + grants |= DIR_READ; + } else { + pgace->flags = FILE_INHERITANCE; + if (mode & S_IXOTH) + grants |= FILE_EXEC; + if (mode & S_IWOTH) + grants |= FILE_WRITE; + if (mode & S_IROTH) + grants |= FILE_READ; + } + pgace->size = cpu_to_le16(wsidsz + 8); + pgace->mask = grants; + memcpy((char*)&pgace->sid, worldsid, wsidsz); + pos += wsidsz + 8; + acecnt++; - /* an ACE for administrators */ - /* always full access */ + /* an ACE for administrators */ + /* always full access */ - pgace = ( ACCESS_ALLOWED_ACE* ) & secattr[offs + pos]; - pgace->type = ACCESS_ALLOWED_ACE_TYPE; - if ( isdir ) - pgace->flags = DIR_INHERITANCE; - else - pgace->flags = FILE_INHERITANCE; - pgace->size = cpu_to_le16( asidsz + 8 ); - grants = OWNER_RIGHTS | FILE_READ | FILE_WRITE | FILE_EXEC; - pgace->mask = grants; - memcpy( ( char* )&pgace->sid, adminsid, asidsz ); - pos += asidsz + 8; - acecnt++; + pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + if (isdir) + pgace->flags = DIR_INHERITANCE; + else + pgace->flags = FILE_INHERITANCE; + pgace->size = cpu_to_le16(asidsz + 8); + grants = OWNER_RIGHTS | FILE_READ | FILE_WRITE | FILE_EXEC; + pgace->mask = grants; + memcpy((char*)&pgace->sid, adminsid, asidsz); + pos += asidsz + 8; + acecnt++; - /* an ACE for system (needed ?) */ - /* always full access */ + /* an ACE for system (needed ?) */ + /* always full access */ - pgace = ( ACCESS_ALLOWED_ACE* ) & secattr[offs + pos]; - pgace->type = ACCESS_ALLOWED_ACE_TYPE; - if ( isdir ) - pgace->flags = DIR_INHERITANCE; - else - pgace->flags = FILE_INHERITANCE; - pgace->size = cpu_to_le16( ssidsz + 8 ); - grants = OWNER_RIGHTS | FILE_READ | FILE_WRITE | FILE_EXEC; - pgace->mask = grants; - memcpy( ( char* )&pgace->sid, systemsid, ssidsz ); - pos += ssidsz + 8; - acecnt++; + pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + if (isdir) + pgace->flags = DIR_INHERITANCE; + else + pgace->flags = FILE_INHERITANCE; + pgace->size = cpu_to_le16(ssidsz + 8); + grants = OWNER_RIGHTS | FILE_READ | FILE_WRITE | FILE_EXEC; + pgace->mask = grants; + memcpy((char*)&pgace->sid, systemsid, ssidsz); + pos += ssidsz + 8; + acecnt++; - /* a null ACE to hold special flags */ - /* using the same representation as cygwin */ + /* a null ACE to hold special flags */ + /* using the same representation as cygwin */ - if ( mode & ( S_ISVTX | S_ISGID | S_ISUID ) ) - { - nsidsz = ntfs_sid_size( nullsid ); - pgace = ( ACCESS_ALLOWED_ACE* ) & secattr[offs + pos]; - pgace->type = ACCESS_ALLOWED_ACE_TYPE; - pgace->flags = NO_PROPAGATE_INHERIT_ACE; - pgace->size = cpu_to_le16( nsidsz + 8 ); - grants = const_cpu_to_le32( 0 ); - if ( mode & S_ISUID ) - grants |= FILE_APPEND_DATA; - if ( mode & S_ISGID ) - grants |= FILE_WRITE_DATA; - if ( mode & S_ISVTX ) - grants |= FILE_READ_DATA; - pgace->mask = grants; - memcpy( ( char* )&pgace->sid, nullsid, nsidsz ); - pos += nsidsz + 8; - acecnt++; - } + if (mode & (S_ISVTX | S_ISGID | S_ISUID)) { + nsidsz = ntfs_sid_size(nullsid); + pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + pgace->flags = NO_PROPAGATE_INHERIT_ACE; + pgace->size = cpu_to_le16(nsidsz + 8); + grants = const_cpu_to_le32(0); + if (mode & S_ISUID) + grants |= FILE_APPEND_DATA; + if (mode & S_ISGID) + grants |= FILE_WRITE_DATA; + if (mode & S_ISVTX) + grants |= FILE_READ_DATA; + pgace->mask = grants; + memcpy((char*)&pgace->sid, nullsid, nsidsz); + pos += nsidsz + 8; + acecnt++; + } - /* fix ACL header */ - pacl->size = cpu_to_le16( pos ); - pacl->ace_count = cpu_to_le16( acecnt ); - return ( pos ); + /* fix ACL header */ + pacl->size = cpu_to_le16(pos); + pacl->ace_count = cpu_to_le16(acecnt); + return (pos); } #if POSIXACLS /* - * Build a full security descriptor from a Posix ACL - * returns descriptor in allocated memory, must free() after use + * Build a full security descriptor from a Posix ACL + * returns descriptor in allocated memory, must free() after use */ -char *ntfs_build_descr_posix( struct MAPPING* const mapping[], - struct POSIX_SECURITY *pxdesc, - int isdir, const SID *usid, const SID *gsid ) +char *ntfs_build_descr_posix(struct MAPPING* const mapping[], + struct POSIX_SECURITY *pxdesc, + int isdir, const SID *usid, const SID *gsid) { - int newattrsz; - SECURITY_DESCRIPTOR_RELATIVE *pnhead; - char *newattr; - int aclsz; - int usidsz; - int gsidsz; - int wsidsz; - int asidsz; - int ssidsz; - int k; + int newattrsz; + SECURITY_DESCRIPTOR_RELATIVE *pnhead; + char *newattr; + int aclsz; + int usidsz; + int gsidsz; + int wsidsz; + int asidsz; + int ssidsz; + int k; - usidsz = ntfs_sid_size( usid ); - gsidsz = ntfs_sid_size( gsid ); - wsidsz = ntfs_sid_size( worldsid ); - asidsz = ntfs_sid_size( adminsid ); - ssidsz = ntfs_sid_size( systemsid ); + usidsz = ntfs_sid_size(usid); + gsidsz = ntfs_sid_size(gsid); + wsidsz = ntfs_sid_size(worldsid); + asidsz = ntfs_sid_size(adminsid); + ssidsz = ntfs_sid_size(systemsid); - /* allocate enough space for the new security attribute */ - newattrsz = sizeof( SECURITY_DESCRIPTOR_RELATIVE ) /* header */ - + usidsz + gsidsz /* usid and gsid */ - + sizeof( ACL ) /* acl header */ - + 2 * ( 8 + usidsz ) /* two possible ACE for user */ - + 3 * ( 8 + gsidsz ) /* three possible ACE for group and mask */ - + 8 + wsidsz /* one ACE for world */ - + 8 + asidsz /* one ACE for admin */ - + 8 + ssidsz; /* one ACE for system */ - if ( isdir ) /* a world denial for directories */ - newattrsz += 8 + wsidsz; - if ( pxdesc->mode & 07000 ) /* a NULL ACE for special modes */ - newattrsz += 8 + ntfs_sid_size( nullsid ); - /* account for non-owning users and groups */ - for ( k = 0; k < pxdesc->acccnt; k++ ) - { - if ( ( pxdesc->acl.ace[k].tag == POSIX_ACL_USER ) - || ( pxdesc->acl.ace[k].tag == POSIX_ACL_GROUP ) ) - newattrsz += 3 * 40; /* fixme : maximum size */ - } - /* account for default ACE's */ - newattrsz += 2 * 40 * pxdesc->defcnt; /* fixme : maximum size */ - newattr = ( char* )ntfs_malloc( newattrsz ); - if ( newattr ) - { - /* build the main header part */ - pnhead = ( SECURITY_DESCRIPTOR_RELATIVE* )newattr; - pnhead->revision = SECURITY_DESCRIPTOR_REVISION; - pnhead->alignment = 0; - /* - * The flag SE_DACL_PROTECTED prevents the ACL - * to be changed in an inheritance after creation - */ - pnhead->control = SE_DACL_PRESENT | SE_DACL_PROTECTED - | SE_SELF_RELATIVE; - /* - * Windows prefers ACL first, do the same to - * get the same hash value and avoid duplication - */ - /* build permissions */ - aclsz = buildacls_posix( mapping, newattr, - sizeof( SECURITY_DESCRIPTOR_RELATIVE ), - pxdesc, isdir, usid, gsid ); - if ( aclsz && ( ( int )( sizeof( SECURITY_DESCRIPTOR_RELATIVE ) - + aclsz + usidsz + gsidsz ) <= newattrsz ) ) - { - /* append usid and gsid */ - memcpy( &newattr[sizeof( SECURITY_DESCRIPTOR_RELATIVE ) - + aclsz], usid, usidsz ); - memcpy( &newattr[sizeof( SECURITY_DESCRIPTOR_RELATIVE ) - + aclsz + usidsz], gsid, gsidsz ); - /* positions of ACL, USID and GSID into header */ - pnhead->owner = - cpu_to_le32( sizeof( SECURITY_DESCRIPTOR_RELATIVE ) - + aclsz ); - pnhead->group = - cpu_to_le32( sizeof( SECURITY_DESCRIPTOR_RELATIVE ) - + aclsz + usidsz ); - pnhead->sacl = const_cpu_to_le32( 0 ); - pnhead->dacl = - const_cpu_to_le32( sizeof( SECURITY_DESCRIPTOR_RELATIVE ) ); - } - else - { - /* ACL failure (errno set) or overflow */ - free( newattr ); - newattr = ( char* )NULL; - if ( aclsz ) - { - /* hope error was detected before overflowing */ - ntfs_log_error( "Security descriptor is longer than expected\n" ); - errno = EIO; - } - } - } - else - errno = ENOMEM; - return ( newattr ); + /* allocate enough space for the new security attribute */ + newattrsz = sizeof(SECURITY_DESCRIPTOR_RELATIVE) /* header */ + + usidsz + gsidsz /* usid and gsid */ + + sizeof(ACL) /* acl header */ + + 2*(8 + usidsz) /* two possible ACE for user */ + + 3*(8 + gsidsz) /* three possible ACE for group and mask */ + + 8 + wsidsz /* one ACE for world */ + + 8 + asidsz /* one ACE for admin */ + + 8 + ssidsz; /* one ACE for system */ + if (isdir) /* a world denial for directories */ + newattrsz += 8 + wsidsz; + if (pxdesc->mode & 07000) /* a NULL ACE for special modes */ + newattrsz += 8 + ntfs_sid_size(nullsid); + /* account for non-owning users and groups */ + for (k=0; kacccnt; k++) { + if ((pxdesc->acl.ace[k].tag == POSIX_ACL_USER) + || (pxdesc->acl.ace[k].tag == POSIX_ACL_GROUP)) + newattrsz += 3*40; /* fixme : maximum size */ + } + /* account for default ACE's */ + newattrsz += 2*40*pxdesc->defcnt; /* fixme : maximum size */ + newattr = (char*)ntfs_malloc(newattrsz); + if (newattr) { + /* build the main header part */ + pnhead = (SECURITY_DESCRIPTOR_RELATIVE*)newattr; + pnhead->revision = SECURITY_DESCRIPTOR_REVISION; + pnhead->alignment = 0; + /* + * The flag SE_DACL_PROTECTED prevents the ACL + * to be changed in an inheritance after creation + */ + pnhead->control = SE_DACL_PRESENT | SE_DACL_PROTECTED + | SE_SELF_RELATIVE; + /* + * Windows prefers ACL first, do the same to + * get the same hash value and avoid duplication + */ + /* build permissions */ + aclsz = buildacls_posix(mapping,newattr, + sizeof(SECURITY_DESCRIPTOR_RELATIVE), + pxdesc, isdir, usid, gsid); + if (aclsz && ((int)(sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz + usidsz + gsidsz) <= newattrsz)) { + /* append usid and gsid */ + memcpy(&newattr[sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz], usid, usidsz); + memcpy(&newattr[sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz + usidsz], gsid, gsidsz); + /* positions of ACL, USID and GSID into header */ + pnhead->owner = + cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz); + pnhead->group = + cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz + usidsz); + pnhead->sacl = const_cpu_to_le32(0); + pnhead->dacl = + const_cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE)); + } else { + /* ACL failure (errno set) or overflow */ + free(newattr); + newattr = (char*)NULL; + if (aclsz) { + /* hope error was detected before overflowing */ + ntfs_log_error("Security descriptor is longer than expected\n"); + errno = EIO; + } + } + } else + errno = ENOMEM; + return (newattr); } #endif /* POSIXACLS */ /* - * Build a full security descriptor - * returns descriptor in allocated memory, must free() after use + * Build a full security descriptor + * returns descriptor in allocated memory, must free() after use */ -char *ntfs_build_descr( mode_t mode, - int isdir, const SID * usid, const SID * gsid ) +char *ntfs_build_descr(mode_t mode, + int isdir, const SID * usid, const SID * gsid) { - int newattrsz; - SECURITY_DESCRIPTOR_RELATIVE *pnhead; - char *newattr; - int aclsz; - int usidsz; - int gsidsz; - int wsidsz; - int asidsz; - int ssidsz; + int newattrsz; + SECURITY_DESCRIPTOR_RELATIVE *pnhead; + char *newattr; + int aclsz; + int usidsz; + int gsidsz; + int wsidsz; + int asidsz; + int ssidsz; - usidsz = ntfs_sid_size( usid ); - gsidsz = ntfs_sid_size( gsid ); - wsidsz = ntfs_sid_size( worldsid ); - asidsz = ntfs_sid_size( adminsid ); - ssidsz = ntfs_sid_size( systemsid ); + usidsz = ntfs_sid_size(usid); + gsidsz = ntfs_sid_size(gsid); + wsidsz = ntfs_sid_size(worldsid); + asidsz = ntfs_sid_size(adminsid); + ssidsz = ntfs_sid_size(systemsid); - /* allocate enough space for the new security attribute */ - newattrsz = sizeof( SECURITY_DESCRIPTOR_RELATIVE ) /* header */ - + usidsz + gsidsz /* usid and gsid */ - + sizeof( ACL ) /* acl header */ - + 2 * ( 8 + usidsz ) /* two possible ACE for user */ - + 2 * ( 8 + gsidsz ) /* two possible ACE for group */ - + 8 + wsidsz /* one ACE for world */ - + 8 + asidsz /* one ACE for admin */ - + 8 + ssidsz; /* one ACE for system */ - if ( isdir ) /* a world denial for directories */ - newattrsz += 8 + wsidsz; - if ( mode & 07000 ) /* a NULL ACE for special modes */ - newattrsz += 8 + ntfs_sid_size( nullsid ); - newattr = ( char* )ntfs_malloc( newattrsz ); - if ( newattr ) - { - /* build the main header part */ - pnhead = ( SECURITY_DESCRIPTOR_RELATIVE* ) newattr; - pnhead->revision = SECURITY_DESCRIPTOR_REVISION; - pnhead->alignment = 0; - /* - * The flag SE_DACL_PROTECTED prevents the ACL - * to be changed in an inheritance after creation - */ - pnhead->control = SE_DACL_PRESENT | SE_DACL_PROTECTED - | SE_SELF_RELATIVE; - /* - * Windows prefers ACL first, do the same to - * get the same hash value and avoid duplication - */ - /* build permissions */ - aclsz = buildacls( newattr, - sizeof( SECURITY_DESCRIPTOR_RELATIVE ), - mode, isdir, usid, gsid ); - if ( ( ( int )sizeof( SECURITY_DESCRIPTOR_RELATIVE ) - + aclsz + usidsz + gsidsz ) <= newattrsz ) - { - /* append usid and gsid */ - memcpy( &newattr[sizeof( SECURITY_DESCRIPTOR_RELATIVE ) - + aclsz], usid, usidsz ); - memcpy( &newattr[sizeof( SECURITY_DESCRIPTOR_RELATIVE ) - + aclsz + usidsz], gsid, gsidsz ); - /* positions of ACL, USID and GSID into header */ - pnhead->owner = - cpu_to_le32( sizeof( SECURITY_DESCRIPTOR_RELATIVE ) - + aclsz ); - pnhead->group = - cpu_to_le32( sizeof( SECURITY_DESCRIPTOR_RELATIVE ) - + aclsz + usidsz ); - pnhead->sacl = const_cpu_to_le32( 0 ); - pnhead->dacl = - const_cpu_to_le32( sizeof( SECURITY_DESCRIPTOR_RELATIVE ) ); - } - else - { - /* hope error was detected before overflowing */ - free( newattr ); - newattr = ( char* )NULL; - ntfs_log_error( "Security descriptor is longer than expected\n" ); - errno = EIO; - } - } - else - errno = ENOMEM; - return ( newattr ); + /* allocate enough space for the new security attribute */ + newattrsz = sizeof(SECURITY_DESCRIPTOR_RELATIVE) /* header */ + + usidsz + gsidsz /* usid and gsid */ + + sizeof(ACL) /* acl header */ + + 2*(8 + usidsz) /* two possible ACE for user */ + + 2*(8 + gsidsz) /* two possible ACE for group */ + + 8 + wsidsz /* one ACE for world */ + + 8 + asidsz /* one ACE for admin */ + + 8 + ssidsz; /* one ACE for system */ + if (isdir) /* a world denial for directories */ + newattrsz += 8 + wsidsz; + if (mode & 07000) /* a NULL ACE for special modes */ + newattrsz += 8 + ntfs_sid_size(nullsid); + newattr = (char*)ntfs_malloc(newattrsz); + if (newattr) { + /* build the main header part */ + pnhead = (SECURITY_DESCRIPTOR_RELATIVE*) newattr; + pnhead->revision = SECURITY_DESCRIPTOR_REVISION; + pnhead->alignment = 0; + /* + * The flag SE_DACL_PROTECTED prevents the ACL + * to be changed in an inheritance after creation + */ + pnhead->control = SE_DACL_PRESENT | SE_DACL_PROTECTED + | SE_SELF_RELATIVE; + /* + * Windows prefers ACL first, do the same to + * get the same hash value and avoid duplication + */ + /* build permissions */ + aclsz = buildacls(newattr, + sizeof(SECURITY_DESCRIPTOR_RELATIVE), + mode, isdir, usid, gsid); + if (((int)sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz + usidsz + gsidsz) <= newattrsz) { + /* append usid and gsid */ + memcpy(&newattr[sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz], usid, usidsz); + memcpy(&newattr[sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz + usidsz], gsid, gsidsz); + /* positions of ACL, USID and GSID into header */ + pnhead->owner = + cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz); + pnhead->group = + cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz + usidsz); + pnhead->sacl = const_cpu_to_le32(0); + pnhead->dacl = + const_cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE)); + } else { + /* hope error was detected before overflowing */ + free(newattr); + newattr = (char*)NULL; + ntfs_log_error("Security descriptor is longer than expected\n"); + errno = EIO; + } + } else + errno = ENOMEM; + return (newattr); } /* - * Create a mode_t permission set - * from owner, group and world grants as represented in ACEs + * Create a mode_t permission set + * from owner, group and world grants as represented in ACEs */ -static int merge_permissions( BOOL isdir, - le32 owner, le32 group, le32 world, le32 special ) +static int merge_permissions(BOOL isdir, + le32 owner, le32 group, le32 world, le32 special) { - int perm; + int perm; - perm = 0; - /* build owner permission */ - if ( owner ) - { - if ( isdir ) - { - /* exec if any of list, traverse */ - if ( owner & DIR_GEXEC ) - perm |= S_IXUSR; - /* write if any of addfile, adddir, delchild */ - if ( owner & DIR_GWRITE ) - perm |= S_IWUSR; - /* read if any of list */ - if ( owner & DIR_GREAD ) - perm |= S_IRUSR; - } - else - { - /* exec if execute or generic execute */ - if ( owner & FILE_GEXEC ) - perm |= S_IXUSR; - /* write if any of writedata or generic write */ - if ( owner & FILE_GWRITE ) - perm |= S_IWUSR; - /* read if any of readdata or generic read */ - if ( owner & FILE_GREAD ) - perm |= S_IRUSR; - } - } - /* build group permission */ - if ( group ) - { - if ( isdir ) - { - /* exec if any of list, traverse */ - if ( group & DIR_GEXEC ) - perm |= S_IXGRP; - /* write if any of addfile, adddir, delchild */ - if ( group & DIR_GWRITE ) - perm |= S_IWGRP; - /* read if any of list */ - if ( group & DIR_GREAD ) - perm |= S_IRGRP; - } - else - { - /* exec if execute */ - if ( group & FILE_GEXEC ) - perm |= S_IXGRP; - /* write if any of writedata, appenddata */ - if ( group & FILE_GWRITE ) - perm |= S_IWGRP; - /* read if any of readdata */ - if ( group & FILE_GREAD ) - perm |= S_IRGRP; - } - } - /* build world permission */ - if ( world ) - { - if ( isdir ) - { - /* exec if any of list, traverse */ - if ( world & DIR_GEXEC ) - perm |= S_IXOTH; - /* write if any of addfile, adddir, delchild */ - if ( world & DIR_GWRITE ) - perm |= S_IWOTH; - /* read if any of list */ - if ( world & DIR_GREAD ) - perm |= S_IROTH; - } - else - { - /* exec if execute */ - if ( world & FILE_GEXEC ) - perm |= S_IXOTH; - /* write if any of writedata, appenddata */ - if ( world & FILE_GWRITE ) - perm |= S_IWOTH; - /* read if any of readdata */ - if ( world & FILE_GREAD ) - perm |= S_IROTH; - } - } - /* build special permission flags */ - if ( special ) - { - if ( special & FILE_APPEND_DATA ) - perm |= S_ISUID; - if ( special & FILE_WRITE_DATA ) - perm |= S_ISGID; - if ( special & FILE_READ_DATA ) - perm |= S_ISVTX; - } - return ( perm ); + perm = 0; + /* build owner permission */ + if (owner) { + if (isdir) { + /* exec if any of list, traverse */ + if (owner & DIR_GEXEC) + perm |= S_IXUSR; + /* write if any of addfile, adddir, delchild */ + if (owner & DIR_GWRITE) + perm |= S_IWUSR; + /* read if any of list */ + if (owner & DIR_GREAD) + perm |= S_IRUSR; + } else { + /* exec if execute or generic execute */ + if (owner & FILE_GEXEC) + perm |= S_IXUSR; + /* write if any of writedata or generic write */ + if (owner & FILE_GWRITE) + perm |= S_IWUSR; + /* read if any of readdata or generic read */ + if (owner & FILE_GREAD) + perm |= S_IRUSR; + } + } + /* build group permission */ + if (group) { + if (isdir) { + /* exec if any of list, traverse */ + if (group & DIR_GEXEC) + perm |= S_IXGRP; + /* write if any of addfile, adddir, delchild */ + if (group & DIR_GWRITE) + perm |= S_IWGRP; + /* read if any of list */ + if (group & DIR_GREAD) + perm |= S_IRGRP; + } else { + /* exec if execute */ + if (group & FILE_GEXEC) + perm |= S_IXGRP; + /* write if any of writedata, appenddata */ + if (group & FILE_GWRITE) + perm |= S_IWGRP; + /* read if any of readdata */ + if (group & FILE_GREAD) + perm |= S_IRGRP; + } + } + /* build world permission */ + if (world) { + if (isdir) { + /* exec if any of list, traverse */ + if (world & DIR_GEXEC) + perm |= S_IXOTH; + /* write if any of addfile, adddir, delchild */ + if (world & DIR_GWRITE) + perm |= S_IWOTH; + /* read if any of list */ + if (world & DIR_GREAD) + perm |= S_IROTH; + } else { + /* exec if execute */ + if (world & FILE_GEXEC) + perm |= S_IXOTH; + /* write if any of writedata, appenddata */ + if (world & FILE_GWRITE) + perm |= S_IWOTH; + /* read if any of readdata */ + if (world & FILE_GREAD) + perm |= S_IROTH; + } + } + /* build special permission flags */ + if (special) { + if (special & FILE_APPEND_DATA) + perm |= S_ISUID; + if (special & FILE_WRITE_DATA) + perm |= S_ISGID; + if (special & FILE_READ_DATA) + perm |= S_ISVTX; + } + return (perm); } #if POSIXACLS /* - * Normalize a Posix ACL either from a sorted raw set of - * access ACEs or default ACEs - * (standard case : different owner, group and administrator) + * Normalize a Posix ACL either from a sorted raw set of + * access ACEs or default ACEs + * (standard case : different owner, group and administrator) */ -static int norm_std_permissions_posix( struct POSIX_SECURITY *posix_desc, - BOOL groupowns, int start, int count, int target ) +static int norm_std_permissions_posix(struct POSIX_SECURITY *posix_desc, + BOOL groupowns, int start, int count, int target) { - int j, k; - s32 id; - u16 tag; - u16 tagsset; - struct POSIX_ACE *pxace; - mode_t grantgrps; - mode_t grantwrld; - mode_t denywrld; - mode_t allow; - mode_t deny; - mode_t perms; - mode_t mode; + int j,k; + s32 id; + u16 tag; + u16 tagsset; + struct POSIX_ACE *pxace; + mode_t grantgrps; + mode_t grantwrld; + mode_t denywrld; + mode_t allow; + mode_t deny; + mode_t perms; + mode_t mode; - mode = 0; - tagsset = 0; - /* - * Determine what is granted to some group or world - * Also get denials to world which are meant to prevent - * execution flags to be inherited by plain files - */ - pxace = posix_desc->acl.ace; - grantgrps = 0; - grantwrld = 0; - denywrld = 0; - for ( j = start; j < ( start + count ); j++ ) - { - if ( pxace[j].perms & POSIX_PERM_DENIAL ) - { - /* deny world exec unless for default */ - if ( ( pxace[j].tag == POSIX_ACL_OTHER ) - && !start ) - denywrld = pxace[j].perms; - } - else - { - switch ( pxace[j].tag ) - { - case POSIX_ACL_GROUP_OBJ : - grantgrps |= pxace[j].perms; - break; - case POSIX_ACL_GROUP : - if ( pxace[j].id ) - grantgrps |= pxace[j].perms; - break; - case POSIX_ACL_OTHER : - grantwrld = pxace[j].perms; - break; - default : - break; - } - } - } - /* - * Collect groups of ACEs related to the same id - * and determine what is granted and what is denied. - * It is important the ACEs have been sorted - */ - j = start; - k = target; - while ( j < ( start + count ) ) - { - tag = pxace[j].tag; - id = pxace[j].id; - if ( pxace[j].perms & POSIX_PERM_DENIAL ) - { - deny = pxace[j].perms | denywrld; - allow = 0; - } - else - { - deny = denywrld; - allow = pxace[j].perms; - } - j++; - while ( ( j < ( start + count ) ) - && ( pxace[j].tag == tag ) - && ( pxace[j].id == id ) ) - { - if ( pxace[j].perms & POSIX_PERM_DENIAL ) - deny |= pxace[j].perms; - else - allow |= pxace[j].perms; - j++; - } - /* - * Build the permissions equivalent to grants and denials - */ - if ( groupowns ) - { - if ( tag == POSIX_ACL_MASK ) - perms = ~deny; - else - perms = allow & ~deny; - } - else - switch ( tag ) - { - case POSIX_ACL_USER_OBJ : - perms = ( allow | grantgrps | grantwrld ) & ~deny; - break; - case POSIX_ACL_USER : - if ( id ) - perms = ( allow | grantgrps | grantwrld ) - & ~deny; - else - perms = allow; - break; - case POSIX_ACL_GROUP_OBJ : - perms = ( allow | grantwrld ) & ~deny; - break; - case POSIX_ACL_GROUP : - if ( id ) - perms = ( allow | grantwrld ) & ~deny; - else - perms = allow; - break; - case POSIX_ACL_MASK : - perms = ~deny; - break; - default : - perms = allow & ~deny; - break; - } - /* - * Store into a Posix ACE - */ - if ( tag != POSIX_ACL_SPECIAL ) - { - pxace[k].tag = tag; - pxace[k].id = id; - pxace[k].perms = perms - & ( POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X ); - tagsset |= tag; - k++; - } - switch ( tag ) - { - case POSIX_ACL_USER_OBJ : - mode |= ( ( perms & 7 ) << 6 ); - break; - case POSIX_ACL_GROUP_OBJ : - case POSIX_ACL_MASK : - mode = ( mode & 07707 ) | ( ( perms & 7 ) << 3 ); - break; - case POSIX_ACL_OTHER : - mode |= perms & 7; - break; - case POSIX_ACL_SPECIAL : - mode |= ( perms & ( S_ISVTX | S_ISUID | S_ISGID ) ); - break; - default : - break; - } - } - if ( !start ) /* not satisfactory */ - { - posix_desc->mode = mode; - posix_desc->tagsset = tagsset; - } - return ( k - target ); + mode = 0; + tagsset = 0; + /* + * Determine what is granted to some group or world + * Also get denials to world which are meant to prevent + * execution flags to be inherited by plain files + */ + pxace = posix_desc->acl.ace; + grantgrps = 0; + grantwrld = 0; + denywrld = 0; + for (j=start; j<(start + count); j++) { + if (pxace[j].perms & POSIX_PERM_DENIAL) { + /* deny world exec unless for default */ + if ((pxace[j].tag == POSIX_ACL_OTHER) + && !start) + denywrld = pxace[j].perms; + } else { + switch (pxace[j].tag) { + case POSIX_ACL_GROUP_OBJ : + grantgrps |= pxace[j].perms; + break; + case POSIX_ACL_GROUP : + if (pxace[j].id) + grantgrps |= pxace[j].perms; + break; + case POSIX_ACL_OTHER : + grantwrld = pxace[j].perms; + break; + default : + break; + } + } + } + /* + * Collect groups of ACEs related to the same id + * and determine what is granted and what is denied. + * It is important the ACEs have been sorted + */ + j = start; + k = target; + while (j < (start + count)) { + tag = pxace[j].tag; + id = pxace[j].id; + if (pxace[j].perms & POSIX_PERM_DENIAL) { + deny = pxace[j].perms | denywrld; + allow = 0; + } else { + deny = denywrld; + allow = pxace[j].perms; + } + j++; + while ((j < (start + count)) + && (pxace[j].tag == tag) + && (pxace[j].id == id)) { + if (pxace[j].perms & POSIX_PERM_DENIAL) + deny |= pxace[j].perms; + else + allow |= pxace[j].perms; + j++; + } + /* + * Build the permissions equivalent to grants and denials + */ + if (groupowns) { + if (tag == POSIX_ACL_MASK) + perms = ~deny; + else + perms = allow & ~deny; + } else + switch (tag) { + case POSIX_ACL_USER_OBJ : + perms = (allow | grantgrps | grantwrld) & ~deny; + break; + case POSIX_ACL_USER : + if (id) + perms = (allow | grantgrps | grantwrld) + & ~deny; + else + perms = allow; + break; + case POSIX_ACL_GROUP_OBJ : + perms = (allow | grantwrld) & ~deny; + break; + case POSIX_ACL_GROUP : + if (id) + perms = (allow | grantwrld) & ~deny; + else + perms = allow; + break; + case POSIX_ACL_MASK : + perms = ~deny; + break; + default : + perms = allow & ~deny; + break; + } + /* + * Store into a Posix ACE + */ + if (tag != POSIX_ACL_SPECIAL) { + pxace[k].tag = tag; + pxace[k].id = id; + pxace[k].perms = perms + & (POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X); + tagsset |= tag; + k++; + } + switch (tag) { + case POSIX_ACL_USER_OBJ : + mode |= ((perms & 7) << 6); + break; + case POSIX_ACL_GROUP_OBJ : + case POSIX_ACL_MASK : + mode = (mode & 07707) | ((perms & 7) << 3); + break; + case POSIX_ACL_OTHER : + mode |= perms & 7; + break; + case POSIX_ACL_SPECIAL : + mode |= (perms & (S_ISVTX | S_ISUID | S_ISGID)); + break; + default : + break; + } + } + if (!start) { /* not satisfactory */ + posix_desc->mode = mode; + posix_desc->tagsset = tagsset; + } + return (k - target); } #endif /* POSIXACLS */ /* - * Interpret an ACL and extract meaningful grants - * (standard case : different owner, group and administrator) + * Interpret an ACL and extract meaningful grants + * (standard case : different owner, group and administrator) */ -static int build_std_permissions( const char *securattr, - const SID *usid, const SID *gsid, BOOL isdir ) +static int build_std_permissions(const char *securattr, + const SID *usid, const SID *gsid, BOOL isdir) { - const SECURITY_DESCRIPTOR_RELATIVE *phead; - const ACL *pacl; - const ACCESS_ALLOWED_ACE *pace; - int offdacl; - int offace; - int acecnt; - int nace; - BOOL noown; - le32 special; - le32 allowown, allowgrp, allowall; - le32 denyown, denygrp, denyall; + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const ACL *pacl; + const ACCESS_ALLOWED_ACE *pace; + int offdacl; + int offace; + int acecnt; + int nace; + BOOL noown; + le32 special; + le32 allowown, allowgrp, allowall; + le32 denyown, denygrp, denyall; - phead = ( const SECURITY_DESCRIPTOR_RELATIVE* )securattr; - offdacl = le32_to_cpu( phead->dacl ); - pacl = ( const ACL* ) & securattr[offdacl]; - special = const_cpu_to_le32( 0 ); - allowown = allowgrp = allowall = const_cpu_to_le32( 0 ); - denyown = denygrp = denyall = const_cpu_to_le32( 0 ); - noown = TRUE; - if ( offdacl ) - { - acecnt = le16_to_cpu( pacl->ace_count ); - offace = offdacl + sizeof( ACL ); - } - else - { - acecnt = 0; - offace = 0; - } - for ( nace = 0; nace < acecnt; nace++ ) - { - pace = ( const ACCESS_ALLOWED_ACE* ) & securattr[offace]; - if ( !( pace->flags & INHERIT_ONLY_ACE ) ) - { - if ( ntfs_same_sid( usid, &pace->sid ) - || ntfs_same_sid( ownersid, &pace->sid ) ) - { - noown = FALSE; - if ( pace->type == ACCESS_ALLOWED_ACE_TYPE ) - allowown |= pace->mask; - else if ( pace->type == ACCESS_DENIED_ACE_TYPE ) - denyown |= pace->mask; - } - else if ( ntfs_same_sid( gsid, &pace->sid ) - && !( pace->mask & WRITE_OWNER ) ) - { - if ( pace->type == ACCESS_ALLOWED_ACE_TYPE ) - allowgrp |= pace->mask; - else if ( pace->type == ACCESS_DENIED_ACE_TYPE ) - denygrp |= pace->mask; - } - else if ( is_world_sid( ( const SID* )&pace->sid ) ) - { - if ( pace->type == ACCESS_ALLOWED_ACE_TYPE ) - allowall |= pace->mask; - else if ( pace->type == ACCESS_DENIED_ACE_TYPE ) - denyall |= pace->mask; - } - else if ( ( ntfs_same_sid( ( const SID* )&pace->sid, nullsid ) ) - && ( pace->type == ACCESS_ALLOWED_ACE_TYPE ) ) - special |= pace->mask; - } - offace += le16_to_cpu( pace->size ); - } - /* - * No indication about owner's rights : grant basic rights - * This happens for files created by Windows in directories - * created by Linux and owned by root, because Windows - * merges the admin ACEs - */ - if ( noown ) - allowown = ( FILE_READ_DATA | FILE_WRITE_DATA | FILE_EXECUTE ); - /* - * Add to owner rights granted to group or world - * unless denied personaly, and add to group rights - * granted to world unless denied specifically - */ - allowown |= ( allowgrp | allowall ); - allowgrp |= allowall; - return ( merge_permissions( isdir, - allowown & ~( denyown | denyall ), - allowgrp & ~( denygrp | denyall ), - allowall & ~denyall, - special ) ); + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; + offdacl = le32_to_cpu(phead->dacl); + pacl = (const ACL*)&securattr[offdacl]; + special = const_cpu_to_le32(0); + allowown = allowgrp = allowall = const_cpu_to_le32(0); + denyown = denygrp = denyall = const_cpu_to_le32(0); + noown = TRUE; + if (offdacl) { + acecnt = le16_to_cpu(pacl->ace_count); + offace = offdacl + sizeof(ACL); + } else { + acecnt = 0; + offace = 0; + } + for (nace = 0; nace < acecnt; nace++) { + pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace]; + if (!(pace->flags & INHERIT_ONLY_ACE)) { + if (ntfs_same_sid(usid, &pace->sid) + || ntfs_same_sid(ownersid, &pace->sid)) { + noown = FALSE; + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) + allowown |= pace->mask; + else if (pace->type == ACCESS_DENIED_ACE_TYPE) + denyown |= pace->mask; + } else + if (ntfs_same_sid(gsid, &pace->sid) + && !(pace->mask & WRITE_OWNER)) { + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) + allowgrp |= pace->mask; + else if (pace->type == ACCESS_DENIED_ACE_TYPE) + denygrp |= pace->mask; + } else + if (is_world_sid((const SID*)&pace->sid)) { + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) + allowall |= pace->mask; + else + if (pace->type == ACCESS_DENIED_ACE_TYPE) + denyall |= pace->mask; + } else + if ((ntfs_same_sid((const SID*)&pace->sid,nullsid)) + && (pace->type == ACCESS_ALLOWED_ACE_TYPE)) + special |= pace->mask; + } + offace += le16_to_cpu(pace->size); + } + /* + * No indication about owner's rights : grant basic rights + * This happens for files created by Windows in directories + * created by Linux and owned by root, because Windows + * merges the admin ACEs + */ + if (noown) + allowown = (FILE_READ_DATA | FILE_WRITE_DATA | FILE_EXECUTE); + /* + * Add to owner rights granted to group or world + * unless denied personaly, and add to group rights + * granted to world unless denied specifically + */ + allowown |= (allowgrp | allowall); + allowgrp |= allowall; + return (merge_permissions(isdir, + allowown & ~(denyown | denyall), + allowgrp & ~(denygrp | denyall), + allowall & ~denyall, + special)); } /* - * Interpret an ACL and extract meaningful grants - * (special case : owner and group are the same, - * and not administrator) + * Interpret an ACL and extract meaningful grants + * (special case : owner and group are the same, + * and not administrator) */ -static int build_owngrp_permissions( const char *securattr, - const SID *usid, BOOL isdir ) +static int build_owngrp_permissions(const char *securattr, + const SID *usid, BOOL isdir) { - const SECURITY_DESCRIPTOR_RELATIVE *phead; - const ACL *pacl; - const ACCESS_ALLOWED_ACE *pace; - int offdacl; - int offace; - int acecnt; - int nace; - le32 special; - BOOL grppresent; - le32 allowown, allowgrp, allowall; - le32 denyown, denygrp, denyall; + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const ACL *pacl; + const ACCESS_ALLOWED_ACE *pace; + int offdacl; + int offace; + int acecnt; + int nace; + le32 special; + BOOL grppresent; + le32 allowown, allowgrp, allowall; + le32 denyown, denygrp, denyall; - phead = ( const SECURITY_DESCRIPTOR_RELATIVE* )securattr; - offdacl = le32_to_cpu( phead->dacl ); - pacl = ( const ACL* ) & securattr[offdacl]; - special = const_cpu_to_le32( 0 ); - allowown = allowgrp = allowall = const_cpu_to_le32( 0 ); - denyown = denygrp = denyall = const_cpu_to_le32( 0 ); - grppresent = FALSE; - if ( offdacl ) - { - acecnt = le16_to_cpu( pacl->ace_count ); - offace = offdacl + sizeof( ACL ); - } - else - acecnt = 0; - for ( nace = 0; nace < acecnt; nace++ ) - { - pace = ( const ACCESS_ALLOWED_ACE* ) & securattr[offace]; - if ( !( pace->flags & INHERIT_ONLY_ACE ) ) - { - if ( ( ntfs_same_sid( usid, &pace->sid ) - || ntfs_same_sid( ownersid, &pace->sid ) ) - && ( pace->mask & WRITE_OWNER ) ) - { - if ( pace->type == ACCESS_ALLOWED_ACE_TYPE ) - allowown |= pace->mask; - } - else if ( ntfs_same_sid( usid, &pace->sid ) - && ( !( pace->mask & WRITE_OWNER ) ) ) - { - if ( pace->type == ACCESS_ALLOWED_ACE_TYPE ) - { - allowgrp |= pace->mask; - grppresent = TRUE; - } - } - else if ( is_world_sid( ( const SID* )&pace->sid ) ) - { - if ( pace->type == ACCESS_ALLOWED_ACE_TYPE ) - allowall |= pace->mask; - else if ( pace->type == ACCESS_DENIED_ACE_TYPE ) - denyall |= pace->mask; - } - else if ( ( ntfs_same_sid( ( const SID* )&pace->sid, nullsid ) ) - && ( pace->type == ACCESS_ALLOWED_ACE_TYPE ) ) - special |= pace->mask; - } - offace += le16_to_cpu( pace->size ); - } - if ( !grppresent ) - allowgrp = allowall; - return ( merge_permissions( isdir, - allowown & ~( denyown | denyall ), - allowgrp & ~( denygrp | denyall ), - allowall & ~denyall, - special ) ); + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; + offdacl = le32_to_cpu(phead->dacl); + pacl = (const ACL*)&securattr[offdacl]; + special = const_cpu_to_le32(0); + allowown = allowgrp = allowall = const_cpu_to_le32(0); + denyown = denygrp = denyall = const_cpu_to_le32(0); + grppresent = FALSE; + if (offdacl) { + acecnt = le16_to_cpu(pacl->ace_count); + offace = offdacl + sizeof(ACL); + } else + acecnt = 0; + for (nace = 0; nace < acecnt; nace++) { + pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace]; + if (!(pace->flags & INHERIT_ONLY_ACE)) { + if ((ntfs_same_sid(usid, &pace->sid) + || ntfs_same_sid(ownersid, &pace->sid)) + && (pace->mask & WRITE_OWNER)) { + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) + allowown |= pace->mask; + } else + if (ntfs_same_sid(usid, &pace->sid) + && (!(pace->mask & WRITE_OWNER))) { + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) { + allowgrp |= pace->mask; + grppresent = TRUE; + } + } else + if (is_world_sid((const SID*)&pace->sid)) { + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) + allowall |= pace->mask; + else + if (pace->type == ACCESS_DENIED_ACE_TYPE) + denyall |= pace->mask; + } else + if ((ntfs_same_sid((const SID*)&pace->sid,nullsid)) + && (pace->type == ACCESS_ALLOWED_ACE_TYPE)) + special |= pace->mask; + } + offace += le16_to_cpu(pace->size); + } + if (!grppresent) + allowgrp = allowall; + return (merge_permissions(isdir, + allowown & ~(denyown | denyall), + allowgrp & ~(denygrp | denyall), + allowall & ~denyall, + special)); } #if POSIXACLS /* - * Normalize a Posix ACL either from a sorted raw set of - * access ACEs or default ACEs - * (special case : owner or/and group is administrator) + * Normalize a Posix ACL either from a sorted raw set of + * access ACEs or default ACEs + * (special case : owner or/and group is administrator) */ -static int norm_ownadmin_permissions_posix( struct POSIX_SECURITY *posix_desc, - int start, int count, int target ) +static int norm_ownadmin_permissions_posix(struct POSIX_SECURITY *posix_desc, + int start, int count, int target) { - int j, k; - s32 id; - u16 tag; - u16 tagsset; - struct POSIX_ACE *pxace; - int acccnt; - mode_t denywrld; - mode_t allow; - mode_t deny; - mode_t perms; - mode_t mode; + int j,k; + s32 id; + u16 tag; + u16 tagsset; + struct POSIX_ACE *pxace; + int acccnt; + mode_t denywrld; + mode_t allow; + mode_t deny; + mode_t perms; + mode_t mode; - mode = 0; - pxace = posix_desc->acl.ace; - acccnt = posix_desc->acccnt; - tagsset = 0; - denywrld = 0; - /* - * Get denials to world which are meant to prevent - * execution flags to be inherited by plain files - */ - for ( j = start; j < ( start + count ); j++ ) - { - if ( pxace[j].perms & POSIX_PERM_DENIAL ) - { - /* deny world exec not for default */ - if ( ( pxace[j].tag == POSIX_ACL_OTHER ) - && !start ) - denywrld = pxace[j].perms; - } - } - /* - * Collect groups of ACEs related to the same id - * and determine what is granted (denials are ignored) - * It is important the ACEs have been sorted - */ - j = start; - k = target; - deny = 0; - while ( j < ( start + count ) ) - { - allow = 0; - tag = pxace[j].tag; - id = pxace[j].id; - if ( tag == POSIX_ACL_MASK ) - { - deny = pxace[j].perms; - j++; - while ( ( j < ( start + count ) ) - && ( pxace[j].tag == POSIX_ACL_MASK ) ) - j++; - } - else - { - if ( !( pxace[j].perms & POSIX_PERM_DENIAL ) ) - allow = pxace[j].perms; - j++; - while ( ( j < ( start + count ) ) - && ( pxace[j].tag == tag ) - && ( pxace[j].id == id ) ) - { - if ( !( pxace[j].perms & POSIX_PERM_DENIAL ) ) - allow |= pxace[j].perms; - j++; - } - } + mode = 0; + pxace = posix_desc->acl.ace; + acccnt = posix_desc->acccnt; + tagsset = 0; + denywrld = 0; + /* + * Get denials to world which are meant to prevent + * execution flags to be inherited by plain files + */ + for (j=start; j<(start + count); j++) { + if (pxace[j].perms & POSIX_PERM_DENIAL) { + /* deny world exec not for default */ + if ((pxace[j].tag == POSIX_ACL_OTHER) + && !start) + denywrld = pxace[j].perms; + } + } + /* + * Collect groups of ACEs related to the same id + * and determine what is granted (denials are ignored) + * It is important the ACEs have been sorted + */ + j = start; + k = target; + deny = 0; + while (j < (start + count)) { + allow = 0; + tag = pxace[j].tag; + id = pxace[j].id; + if (tag == POSIX_ACL_MASK) { + deny = pxace[j].perms; + j++; + while ((j < (start + count)) + && (pxace[j].tag == POSIX_ACL_MASK)) + j++; + } else { + if (!(pxace[j].perms & POSIX_PERM_DENIAL)) + allow = pxace[j].perms; + j++; + while ((j < (start + count)) + && (pxace[j].tag == tag) + && (pxace[j].id == id)) { + if (!(pxace[j].perms & POSIX_PERM_DENIAL)) + allow |= pxace[j].perms; + j++; + } + } - /* - * Store the grants into a Posix ACE - */ - if ( tag == POSIX_ACL_MASK ) - perms = ~deny; - else - perms = allow & ~denywrld; - if ( tag != POSIX_ACL_SPECIAL ) - { - pxace[k].tag = tag; - pxace[k].id = id; - pxace[k].perms = perms - & ( POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X ); - tagsset |= tag; - k++; - } - switch ( tag ) - { - case POSIX_ACL_USER_OBJ : - mode |= ( ( perms & 7 ) << 6 ); - break; - case POSIX_ACL_GROUP_OBJ : - case POSIX_ACL_MASK : - mode = ( mode & 07707 ) | ( ( perms & 7 ) << 3 ); - break; - case POSIX_ACL_OTHER : - mode |= perms & 7; - break; - case POSIX_ACL_SPECIAL : - mode |= perms & ( S_ISVTX | S_ISUID | S_ISGID ); - break; - default : - break; - } - } - if ( !start ) /* not satisfactory */ - { - posix_desc->mode = mode; - posix_desc->tagsset = tagsset; - } - return ( k - target ); + /* + * Store the grants into a Posix ACE + */ + if (tag == POSIX_ACL_MASK) + perms = ~deny; + else + perms = allow & ~denywrld; + if (tag != POSIX_ACL_SPECIAL) { + pxace[k].tag = tag; + pxace[k].id = id; + pxace[k].perms = perms + & (POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X); + tagsset |= tag; + k++; + } + switch (tag) { + case POSIX_ACL_USER_OBJ : + mode |= ((perms & 7) << 6); + break; + case POSIX_ACL_GROUP_OBJ : + case POSIX_ACL_MASK : + mode = (mode & 07707) | ((perms & 7) << 3); + break; + case POSIX_ACL_OTHER : + mode |= perms & 7; + break; + case POSIX_ACL_SPECIAL : + mode |= perms & (S_ISVTX | S_ISUID | S_ISGID); + break; + default : + break; + } + } + if (!start) { /* not satisfactory */ + posix_desc->mode = mode; + posix_desc->tagsset = tagsset; + } + return (k - target); } #endif /* POSIXACLS */ /* - * Interpret an ACL and extract meaningful grants - * (special case : owner or/and group is administrator) + * Interpret an ACL and extract meaningful grants + * (special case : owner or/and group is administrator) */ -static int build_ownadmin_permissions( const char *securattr, - const SID *usid, const SID *gsid, BOOL isdir ) +static int build_ownadmin_permissions(const char *securattr, + const SID *usid, const SID *gsid, BOOL isdir) { - const SECURITY_DESCRIPTOR_RELATIVE *phead; - const ACL *pacl; - const ACCESS_ALLOWED_ACE *pace; - int offdacl; - int offace; - int acecnt; - int nace; - BOOL firstapply; - int isforeign; - le32 special; - le32 allowown, allowgrp, allowall; - le32 denyown, denygrp, denyall; + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const ACL *pacl; + const ACCESS_ALLOWED_ACE *pace; + int offdacl; + int offace; + int acecnt; + int nace; + BOOL firstapply; + int isforeign; + le32 special; + le32 allowown, allowgrp, allowall; + le32 denyown, denygrp, denyall; - phead = ( const SECURITY_DESCRIPTOR_RELATIVE* )securattr; - offdacl = le32_to_cpu( phead->dacl ); - pacl = ( const ACL* ) & securattr[offdacl]; - special = const_cpu_to_le32( 0 ); - allowown = allowgrp = allowall = const_cpu_to_le32( 0 ); - denyown = denygrp = denyall = const_cpu_to_le32( 0 ); - if ( offdacl ) - { - acecnt = le16_to_cpu( pacl->ace_count ); - offace = offdacl + sizeof( ACL ); - } - else - { - acecnt = 0; - offace = 0; - } - firstapply = TRUE; - isforeign = 3; - for ( nace = 0; nace < acecnt; nace++ ) - { - pace = ( const ACCESS_ALLOWED_ACE* ) & securattr[offace]; - if ( !( pace->flags & INHERIT_ONLY_ACE ) - && !( ~pace->mask & ( ROOT_OWNER_UNMARK | ROOT_GROUP_UNMARK ) ) ) - { - if ( ( ntfs_same_sid( usid, &pace->sid ) - || ntfs_same_sid( ownersid, &pace->sid ) ) - && ( ( ( pace->mask & WRITE_OWNER ) && firstapply ) ) ) - { - if ( pace->type == ACCESS_ALLOWED_ACE_TYPE ) - { - allowown |= pace->mask; - isforeign &= ~1; - } - else if ( pace->type == ACCESS_DENIED_ACE_TYPE ) - denyown |= pace->mask; - } - else if ( ntfs_same_sid( gsid, &pace->sid ) - && ( !( pace->mask & WRITE_OWNER ) ) ) - { - if ( pace->type == ACCESS_ALLOWED_ACE_TYPE ) - { - allowgrp |= pace->mask; - isforeign &= ~2; - } - else if ( pace->type == ACCESS_DENIED_ACE_TYPE ) - denygrp |= pace->mask; - } - else if ( is_world_sid( ( const SID* )&pace->sid ) ) - { - if ( pace->type == ACCESS_ALLOWED_ACE_TYPE ) - allowall |= pace->mask; - else if ( pace->type == ACCESS_DENIED_ACE_TYPE ) - denyall |= pace->mask; - } - firstapply = FALSE; - } - else if ( !( pace->flags & INHERIT_ONLY_ACE ) ) - if ( ( ntfs_same_sid( ( const SID* )&pace->sid, nullsid ) ) - && ( pace->type == ACCESS_ALLOWED_ACE_TYPE ) ) - special |= pace->mask; - offace += le16_to_cpu( pace->size ); - } - if ( isforeign ) - { - allowown |= ( allowgrp | allowall ); - allowgrp |= allowall; - } - return ( merge_permissions( isdir, - allowown & ~( denyown | denyall ), - allowgrp & ~( denygrp | denyall ), - allowall & ~denyall, - special ) ); + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; + offdacl = le32_to_cpu(phead->dacl); + pacl = (const ACL*)&securattr[offdacl]; + special = const_cpu_to_le32(0); + allowown = allowgrp = allowall = const_cpu_to_le32(0); + denyown = denygrp = denyall = const_cpu_to_le32(0); + if (offdacl) { + acecnt = le16_to_cpu(pacl->ace_count); + offace = offdacl + sizeof(ACL); + } else { + acecnt = 0; + offace = 0; + } + firstapply = TRUE; + isforeign = 3; + for (nace = 0; nace < acecnt; nace++) { + pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace]; + if (!(pace->flags & INHERIT_ONLY_ACE) + && !(~pace->mask & (ROOT_OWNER_UNMARK | ROOT_GROUP_UNMARK))) { + if ((ntfs_same_sid(usid, &pace->sid) + || ntfs_same_sid(ownersid, &pace->sid)) + && (((pace->mask & WRITE_OWNER) && firstapply))) { + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) { + allowown |= pace->mask; + isforeign &= ~1; + } else + if (pace->type == ACCESS_DENIED_ACE_TYPE) + denyown |= pace->mask; + } else + if (ntfs_same_sid(gsid, &pace->sid) + && (!(pace->mask & WRITE_OWNER))) { + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) { + allowgrp |= pace->mask; + isforeign &= ~2; + } else + if (pace->type == ACCESS_DENIED_ACE_TYPE) + denygrp |= pace->mask; + } else if (is_world_sid((const SID*)&pace->sid)) { + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) + allowall |= pace->mask; + else + if (pace->type == ACCESS_DENIED_ACE_TYPE) + denyall |= pace->mask; + } + firstapply = FALSE; + } else + if (!(pace->flags & INHERIT_ONLY_ACE)) + if ((ntfs_same_sid((const SID*)&pace->sid,nullsid)) + && (pace->type == ACCESS_ALLOWED_ACE_TYPE)) + special |= pace->mask; + offace += le16_to_cpu(pace->size); + } + if (isforeign) { + allowown |= (allowgrp | allowall); + allowgrp |= allowall; + } + return (merge_permissions(isdir, + allowown & ~(denyown | denyall), + allowgrp & ~(denygrp | denyall), + allowall & ~denyall, + special)); } #if OWNERFROMACL /* - * Define the owner of a file as the first user allowed - * to change the owner, instead of the user defined as owner. + * Define the owner of a file as the first user allowed + * to change the owner, instead of the user defined as owner. * - * This produces better approximations for files written by a - * Windows user in an inheritable directory owned by another user, - * as the access rights are inheritable but the ownership is not. + * This produces better approximations for files written by a + * Windows user in an inheritable directory owned by another user, + * as the access rights are inheritable but the ownership is not. * - * An important case is the directories "Documents and Settings/user" - * which the users must have access to, though Windows considers them - * as owned by administrator. + * An important case is the directories "Documents and Settings/user" + * which the users must have access to, though Windows considers them + * as owned by administrator. */ -const SID *ntfs_acl_owner( const char *securattr ) +const SID *ntfs_acl_owner(const char *securattr) { - const SECURITY_DESCRIPTOR_RELATIVE *phead; - const SID *usid; - const ACL *pacl; - const ACCESS_ALLOWED_ACE *pace; - int offdacl; - int offace; - int acecnt; - int nace; - BOOL found; + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const SID *usid; + const ACL *pacl; + const ACCESS_ALLOWED_ACE *pace; + int offdacl; + int offace; + int acecnt; + int nace; + BOOL found; - found = FALSE; - phead = ( const SECURITY_DESCRIPTOR_RELATIVE* )securattr; - offdacl = le32_to_cpu( phead->dacl ); - if ( offdacl ) - { - pacl = ( const ACL* ) & securattr[offdacl]; - acecnt = le16_to_cpu( pacl->ace_count ); - offace = offdacl + sizeof( ACL ); - nace = 0; - do - { - pace = ( const ACCESS_ALLOWED_ACE* ) & securattr[offace]; - if ( ( pace->mask & WRITE_OWNER ) - && ( pace->type == ACCESS_ALLOWED_ACE_TYPE ) - && ntfs_is_user_sid( &pace->sid ) ) - found = TRUE; - offace += le16_to_cpu( pace->size ); - } - while ( !found && ( ++nace < acecnt ) ); - } - if ( found ) - usid = &pace->sid; - else - usid = ( const SID* ) & securattr[le32_to_cpu( phead->owner )]; - return ( usid ); + found = FALSE; + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; + offdacl = le32_to_cpu(phead->dacl); + if (offdacl) { + pacl = (const ACL*)&securattr[offdacl]; + acecnt = le16_to_cpu(pacl->ace_count); + offace = offdacl + sizeof(ACL); + nace = 0; + do { + pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace]; + if ((pace->mask & WRITE_OWNER) + && (pace->type == ACCESS_ALLOWED_ACE_TYPE) + && ntfs_is_user_sid(&pace->sid)) + found = TRUE; + offace += le16_to_cpu(pace->size); + } while (!found && (++nace < acecnt)); + } + if (found) + usid = &pace->sid; + else + usid = (const SID*)&securattr[le32_to_cpu(phead->owner)]; + return (usid); } #else /* - * Special case for files owned by administrator with full - * access granted to a mapped user : consider this user as the tenant - * of the file. + * Special case for files owned by administrator with full + * access granted to a mapped user : consider this user as the tenant + * of the file. * - * This situation cannot be represented with Linux concepts and can - * only be found for files or directories created by Windows. - * Typical situation : directory "Documents and Settings/user" which - * is on the path to user's files and must be given access to user - * only. + * This situation cannot be represented with Linux concepts and can + * only be found for files or directories created by Windows. + * Typical situation : directory "Documents and Settings/user" which + * is on the path to user's files and must be given access to user + * only. * - * Check file is owned by administrator and no user has rights before - * calling. - * Returns the uid of tenant or zero if none + * Check file is owned by administrator and no user has rights before + * calling. + * Returns the uid of tenant or zero if none */ -static uid_t find_tenant( struct MAPPING *const mapping[], - const char *securattr ) +static uid_t find_tenant(struct MAPPING *const mapping[], + const char *securattr) { - const SECURITY_DESCRIPTOR_RELATIVE *phead; - const ACL *pacl; - const ACCESS_ALLOWED_ACE *pace; - int offdacl; - int offace; - int acecnt; - int nace; - uid_t tid; - uid_t xid; + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const ACL *pacl; + const ACCESS_ALLOWED_ACE *pace; + int offdacl; + int offace; + int acecnt; + int nace; + uid_t tid; + uid_t xid; - phead = ( const SECURITY_DESCRIPTOR_RELATIVE* )securattr; - offdacl = le32_to_cpu( phead->dacl ); - pacl = ( const ACL* ) & securattr[offdacl]; - tid = 0; - if ( offdacl ) - { - acecnt = le16_to_cpu( pacl->ace_count ); - offace = offdacl + sizeof( ACL ); - } - else - acecnt = 0; - for ( nace = 0; nace < acecnt; nace++ ) - { - pace = ( const ACCESS_ALLOWED_ACE* ) & securattr[offace]; - if ( ( pace->type == ACCESS_ALLOWED_ACE_TYPE ) - && ( pace->mask & DIR_WRITE ) ) - { - xid = NTFS_FIND_USER( mapping[MAPUSERS], &pace->sid ); - if ( xid ) tid = xid; - } - offace += le16_to_cpu( pace->size ); - } - return ( tid ); + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; + offdacl = le32_to_cpu(phead->dacl); + pacl = (const ACL*)&securattr[offdacl]; + tid = 0; + if (offdacl) { + acecnt = le16_to_cpu(pacl->ace_count); + offace = offdacl + sizeof(ACL); + } else + acecnt = 0; + for (nace = 0; nace < acecnt; nace++) { + pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace]; + if ((pace->type == ACCESS_ALLOWED_ACE_TYPE) + && (pace->mask & DIR_WRITE)) { + xid = NTFS_FIND_USER(mapping[MAPUSERS], &pace->sid); + if (xid) tid = xid; + } + offace += le16_to_cpu(pace->size); + } + return (tid); } #endif /* OWNERFROMACL */ @@ -3704,991 +3423,871 @@ static uid_t find_tenant( struct MAPPING *const mapping[], #if POSIXACLS /* - * Build Posix permissions from an ACL - * returns a pointer to the requested permissions - * or a null pointer (with errno set) if there is a problem + * Build Posix permissions from an ACL + * returns a pointer to the requested permissions + * or a null pointer (with errno set) if there is a problem * - * If the NTFS ACL was created according to our rules, the retrieved - * Posix ACL should be the exact ACL which was set. However if - * the NTFS ACL was built by a different tool, the result could - * be a a poor approximation of what was expected + * If the NTFS ACL was created according to our rules, the retrieved + * Posix ACL should be the exact ACL which was set. However if + * the NTFS ACL was built by a different tool, the result could + * be a a poor approximation of what was expected */ struct POSIX_SECURITY *ntfs_build_permissions_posix( - struct MAPPING *const mapping[], - const char *securattr, - const SID *usid, const SID *gsid, BOOL isdir ) + struct MAPPING *const mapping[], + const char *securattr, + const SID *usid, const SID *gsid, BOOL isdir) { - const SECURITY_DESCRIPTOR_RELATIVE *phead; - struct POSIX_SECURITY *pxdesc; - const ACL *pacl; - const ACCESS_ALLOWED_ACE *pace; - struct POSIX_ACE *pxace; - struct - { - uid_t prevuid; - gid_t prevgid; - int groupmasks; - s16 tagsset; - BOOL gotowner; - BOOL gotownermask; - BOOL gotgroup; - mode_t permswrld; - } ctx[2], *pctx; - int offdacl; - int offace; - int alloccnt; - int acecnt; - uid_t uid; - gid_t gid; - int i, j; - int k, l; - BOOL ignore; - BOOL adminowns; - BOOL groupowns; - BOOL firstinh; - BOOL genericinh; + const SECURITY_DESCRIPTOR_RELATIVE *phead; + struct POSIX_SECURITY *pxdesc; + const ACL *pacl; + const ACCESS_ALLOWED_ACE *pace; + struct POSIX_ACE *pxace; + struct { + uid_t prevuid; + gid_t prevgid; + int groupmasks; + s16 tagsset; + BOOL gotowner; + BOOL gotownermask; + BOOL gotgroup; + mode_t permswrld; + } ctx[2], *pctx; + int offdacl; + int offace; + int alloccnt; + int acecnt; + uid_t uid; + gid_t gid; + int i,j; + int k,l; + BOOL ignore; + BOOL adminowns; + BOOL groupowns; + BOOL firstinh; + BOOL genericinh; - phead = ( const SECURITY_DESCRIPTOR_RELATIVE* )securattr; - offdacl = le32_to_cpu( phead->dacl ); - if ( offdacl ) - { - pacl = ( const ACL* ) & securattr[offdacl]; - acecnt = le16_to_cpu( pacl->ace_count ); - offace = offdacl + sizeof( ACL ); - } - else - { - acecnt = 0; - offace = 0; - } - adminowns = FALSE; - groupowns = ntfs_same_sid( gsid, usid ); - firstinh = FALSE; - genericinh = FALSE; - /* - * Build a raw posix security descriptor - * by just translating permissions and ids - * Add 2 to the count of ACE to be able to insert - * a group ACE later in access and default ACLs - * and add 2 more to be able to insert ACEs for owner - * and 2 more for other - */ - alloccnt = acecnt + 6; - pxdesc = ( struct POSIX_SECURITY* )malloc( - sizeof( struct POSIX_SECURITY ) - + alloccnt*sizeof( struct POSIX_ACE ) ); - k = 0; - l = alloccnt; - for ( i = 0; i < 2; i++ ) - { - pctx = &ctx[i]; - pctx->permswrld = 0; - pctx->prevuid = -1; - pctx->prevgid = -1; - pctx->groupmasks = 0; - pctx->tagsset = 0; - pctx->gotowner = FALSE; - pctx->gotgroup = FALSE; - pctx->gotownermask = FALSE; - } - for ( j = 0; j < acecnt; j++ ) - { - pace = ( const ACCESS_ALLOWED_ACE* ) & securattr[offace]; - if ( pace->flags & INHERIT_ONLY_ACE ) - { - pxace = &pxdesc->acl.ace[l - 1]; - pctx = &ctx[1]; - } - else - { - pxace = &pxdesc->acl.ace[k]; - pctx = &ctx[0]; - } - ignore = FALSE; - /* - * grants for root as a designated user or group - */ - if ( ( ~pace->mask & ( ROOT_OWNER_UNMARK | ROOT_GROUP_UNMARK ) ) - && ( pace->type == ACCESS_ALLOWED_ACE_TYPE ) - && ntfs_same_sid( &pace->sid, adminsid ) ) - { - pxace->tag = ( pace->mask & ROOT_OWNER_UNMARK ? POSIX_ACL_GROUP : POSIX_ACL_USER ); - pxace->id = 0; - if ( ( pace->mask & ( GENERIC_ALL | WRITE_OWNER ) ) - && ( pace->flags & INHERIT_ONLY_ACE ) ) - ignore = genericinh = TRUE; - } - else if ( ntfs_same_sid( usid, &pace->sid ) ) - { - pxace->id = -1; - /* - * Owner has no write-owner right : - * a group was defined same as owner - * or admin was owner or group : - * denials are meant to owner - * and grants are meant to group - */ - if ( !( pace->mask & ( WRITE_OWNER | GENERIC_ALL ) ) - && ( pace->type == ACCESS_ALLOWED_ACE_TYPE ) ) - { - if ( ntfs_same_sid( gsid, usid ) ) - { - pxace->tag = POSIX_ACL_GROUP_OBJ; - pxace->id = -1; - } - else - { - if ( ntfs_same_sid( &pace->sid, usid ) ) - groupowns = TRUE; - gid = NTFS_FIND_GROUP( mapping[MAPGROUPS], &pace->sid ); - if ( gid ) - { - pxace->tag = POSIX_ACL_GROUP; - pxace->id = gid; - pctx->prevgid = gid; - } - else - { - uid = NTFS_FIND_USER( mapping[MAPUSERS], &pace->sid ); - if ( uid ) - { - pxace->tag = POSIX_ACL_USER; - pxace->id = uid; - } - else - ignore = TRUE; - } - } - } - else - { - /* - * when group owns, late denials for owner - * mean group mask - */ - if ( ( pace->type == ACCESS_DENIED_ACE_TYPE ) - && ( pace->mask & WRITE_OWNER ) ) - { - pxace->tag = POSIX_ACL_MASK; - pctx->gotownermask = TRUE; - if ( pctx->gotowner ) - pctx->groupmasks++; - } - else - { - if ( pace->type == ACCESS_ALLOWED_ACE_TYPE ) - pctx->gotowner = TRUE; - if ( pctx->gotownermask && !pctx->gotowner ) - { - uid = NTFS_FIND_USER( mapping[MAPUSERS], &pace->sid ); - pxace->id = uid; - pxace->tag = POSIX_ACL_USER; - } - else - pxace->tag = POSIX_ACL_USER_OBJ; - /* system ignored, and admin */ - /* ignored at first position */ - if ( pace->flags & INHERIT_ONLY_ACE ) - { - if ( ( firstinh && ntfs_same_sid( &pace->sid, adminsid ) ) - || ntfs_same_sid( &pace->sid, systemsid ) ) - ignore = TRUE; - if ( !firstinh ) - { - firstinh = TRUE; - } - } - else - { - if ( ( adminowns && ntfs_same_sid( &pace->sid, adminsid ) ) - || ntfs_same_sid( &pace->sid, systemsid ) ) - ignore = TRUE; - if ( ntfs_same_sid( usid, adminsid ) ) - adminowns = TRUE; - } - } - } - } - else if ( ntfs_same_sid( gsid, &pace->sid ) ) - { - if ( ( pace->type == ACCESS_DENIED_ACE_TYPE ) - && ( pace->mask & WRITE_OWNER ) ) - { - pxace->tag = POSIX_ACL_MASK; - pxace->id = -1; - if ( pctx->gotowner ) - pctx->groupmasks++; - } - else - { - if ( pctx->gotgroup || ( pctx->groupmasks > 1 ) ) - { - gid = NTFS_FIND_GROUP( mapping[MAPGROUPS], &pace->sid ); - if ( gid ) - { - pxace->id = gid; - pxace->tag = POSIX_ACL_GROUP; - pctx->prevgid = gid; - } - else - ignore = TRUE; - } - else - { - pxace->id = -1; - pxace->tag = POSIX_ACL_GROUP_OBJ; - if ( pace->type == ACCESS_ALLOWED_ACE_TYPE ) - pctx->gotgroup = TRUE; - } + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; + offdacl = le32_to_cpu(phead->dacl); + if (offdacl) { + pacl = (const ACL*)&securattr[offdacl]; + acecnt = le16_to_cpu(pacl->ace_count); + offace = offdacl + sizeof(ACL); + } else { + acecnt = 0; + offace = 0; + } + adminowns = FALSE; + groupowns = ntfs_same_sid(gsid,usid); + firstinh = FALSE; + genericinh = FALSE; + /* + * Build a raw posix security descriptor + * by just translating permissions and ids + * Add 2 to the count of ACE to be able to insert + * a group ACE later in access and default ACLs + * and add 2 more to be able to insert ACEs for owner + * and 2 more for other + */ + alloccnt = acecnt + 6; + pxdesc = (struct POSIX_SECURITY*)malloc( + sizeof(struct POSIX_SECURITY) + + alloccnt*sizeof(struct POSIX_ACE)); + k = 0; + l = alloccnt; + for (i=0; i<2; i++) { + pctx = &ctx[i]; + pctx->permswrld = 0; + pctx->prevuid = -1; + pctx->prevgid = -1; + pctx->groupmasks = 0; + pctx->tagsset = 0; + pctx->gotowner = FALSE; + pctx->gotgroup = FALSE; + pctx->gotownermask = FALSE; + } + for (j=0; jflags & INHERIT_ONLY_ACE) { + pxace = &pxdesc->acl.ace[l - 1]; + pctx = &ctx[1]; + } else { + pxace = &pxdesc->acl.ace[k]; + pctx = &ctx[0]; + } + ignore = FALSE; + /* + * grants for root as a designated user or group + */ + if ((~pace->mask & (ROOT_OWNER_UNMARK | ROOT_GROUP_UNMARK)) + && (pace->type == ACCESS_ALLOWED_ACE_TYPE) + && ntfs_same_sid(&pace->sid, adminsid)) { + pxace->tag = (pace->mask & ROOT_OWNER_UNMARK ? POSIX_ACL_GROUP : POSIX_ACL_USER); + pxace->id = 0; + if ((pace->mask & (GENERIC_ALL | WRITE_OWNER)) + && (pace->flags & INHERIT_ONLY_ACE)) + ignore = genericinh = TRUE; + } else + if (ntfs_same_sid(usid, &pace->sid)) { + pxace->id = -1; + /* + * Owner has no write-owner right : + * a group was defined same as owner + * or admin was owner or group : + * denials are meant to owner + * and grants are meant to group + */ + if (!(pace->mask & (WRITE_OWNER | GENERIC_ALL)) + && (pace->type == ACCESS_ALLOWED_ACE_TYPE)) { + if (ntfs_same_sid(gsid,usid)) { + pxace->tag = POSIX_ACL_GROUP_OBJ; + pxace->id = -1; + } else { + if (ntfs_same_sid(&pace->sid,usid)) + groupowns = TRUE; + gid = NTFS_FIND_GROUP(mapping[MAPGROUPS],&pace->sid); + if (gid) { + pxace->tag = POSIX_ACL_GROUP; + pxace->id = gid; + pctx->prevgid = gid; + } else { + uid = NTFS_FIND_USER(mapping[MAPUSERS],&pace->sid); + if (uid) { + pxace->tag = POSIX_ACL_USER; + pxace->id = uid; + } else + ignore = TRUE; + } + } + } else { + /* + * when group owns, late denials for owner + * mean group mask + */ + if ((pace->type == ACCESS_DENIED_ACE_TYPE) + && (pace->mask & WRITE_OWNER)) { + pxace->tag = POSIX_ACL_MASK; + pctx->gotownermask = TRUE; + if (pctx->gotowner) + pctx->groupmasks++; + } else { + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) + pctx->gotowner = TRUE; + if (pctx->gotownermask && !pctx->gotowner) { + uid = NTFS_FIND_USER(mapping[MAPUSERS],&pace->sid); + pxace->id = uid; + pxace->tag = POSIX_ACL_USER; + } else + pxace->tag = POSIX_ACL_USER_OBJ; + /* system ignored, and admin */ + /* ignored at first position */ + if (pace->flags & INHERIT_ONLY_ACE) { + if ((firstinh && ntfs_same_sid(&pace->sid,adminsid)) + || ntfs_same_sid(&pace->sid,systemsid)) + ignore = TRUE; + if (!firstinh) { + firstinh = TRUE; + } + } else { + if ((adminowns && ntfs_same_sid(&pace->sid,adminsid)) + || ntfs_same_sid(&pace->sid,systemsid)) + ignore = TRUE; + if (ntfs_same_sid(usid,adminsid)) + adminowns = TRUE; + } + } + } + } else if (ntfs_same_sid(gsid, &pace->sid)) { + if ((pace->type == ACCESS_DENIED_ACE_TYPE) + && (pace->mask & WRITE_OWNER)) { + pxace->tag = POSIX_ACL_MASK; + pxace->id = -1; + if (pctx->gotowner) + pctx->groupmasks++; + } else { + if (pctx->gotgroup || (pctx->groupmasks > 1)) { + gid = NTFS_FIND_GROUP(mapping[MAPGROUPS],&pace->sid); + if (gid) { + pxace->id = gid; + pxace->tag = POSIX_ACL_GROUP; + pctx->prevgid = gid; + } else + ignore = TRUE; + } else { + pxace->id = -1; + pxace->tag = POSIX_ACL_GROUP_OBJ; + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) + pctx->gotgroup = TRUE; + } - if ( ntfs_same_sid( gsid, adminsid ) - || ntfs_same_sid( gsid, systemsid ) ) - { - if ( pace->mask & ( WRITE_OWNER | GENERIC_ALL ) ) - ignore = TRUE; - if ( ntfs_same_sid( gsid, adminsid ) ) - adminowns = TRUE; - else - genericinh = ignore; - } - } - } - else if ( is_world_sid( ( const SID* )&pace->sid ) ) - { - pxace->id = -1; - pxace->tag = POSIX_ACL_OTHER; - if ( ( pace->type == ACCESS_DENIED_ACE_TYPE ) - && ( pace->flags & INHERIT_ONLY_ACE ) ) - ignore = TRUE; - } - else if ( ntfs_same_sid( ( const SID* )&pace->sid, nullsid ) ) - { - pxace->id = -1; - pxace->tag = POSIX_ACL_SPECIAL; - } - else - { - uid = NTFS_FIND_USER( mapping[MAPUSERS], &pace->sid ); - if ( uid ) - { - if ( ( pace->type == ACCESS_DENIED_ACE_TYPE ) - && ( pace->mask & WRITE_OWNER ) - && ( pctx->prevuid != uid ) ) - { - pxace->id = -1; - pxace->tag = POSIX_ACL_MASK; - } - else - { - pxace->id = uid; - pxace->tag = POSIX_ACL_USER; - } - pctx->prevuid = uid; - } - else - { - gid = NTFS_FIND_GROUP( mapping[MAPGROUPS], &pace->sid ); - if ( gid ) - { - if ( ( pace->type == ACCESS_DENIED_ACE_TYPE ) - && ( pace->mask & WRITE_OWNER ) - && ( pctx->prevgid != gid ) ) - { - pxace->tag = POSIX_ACL_MASK; - pctx->groupmasks++; - } - else - { - pxace->tag = POSIX_ACL_GROUP; - } - pxace->id = gid; - pctx->prevgid = gid; - } - else - { - /* - * do not grant rights to unknown - * people and do not define root as a - * designated user or group - */ - ignore = TRUE; - } - } - } - if ( !ignore ) - { - pxace->perms = 0; - /* specific decoding for vtx/uid/gid */ - if ( pxace->tag == POSIX_ACL_SPECIAL ) - { - if ( pace->mask & FILE_APPEND_DATA ) - pxace->perms |= S_ISUID; - if ( pace->mask & FILE_WRITE_DATA ) - pxace->perms |= S_ISGID; - if ( pace->mask & FILE_READ_DATA ) - pxace->perms |= S_ISVTX; - } - else if ( isdir ) - { - if ( pace->mask & DIR_GEXEC ) - pxace->perms |= POSIX_PERM_X; - if ( pace->mask & DIR_GWRITE ) - pxace->perms |= POSIX_PERM_W; - if ( pace->mask & DIR_GREAD ) - pxace->perms |= POSIX_PERM_R; - if ( ( pace->mask & GENERIC_ALL ) - && ( pace->flags & INHERIT_ONLY_ACE ) ) - pxace->perms |= POSIX_PERM_X - | POSIX_PERM_W - | POSIX_PERM_R; - } - else - { - if ( pace->mask & FILE_GEXEC ) - pxace->perms |= POSIX_PERM_X; - if ( pace->mask & FILE_GWRITE ) - pxace->perms |= POSIX_PERM_W; - if ( pace->mask & FILE_GREAD ) - pxace->perms |= POSIX_PERM_R; - } + if (ntfs_same_sid(gsid,adminsid) + || ntfs_same_sid(gsid,systemsid)) { + if (pace->mask & (WRITE_OWNER | GENERIC_ALL)) + ignore = TRUE; + if (ntfs_same_sid(gsid,adminsid)) + adminowns = TRUE; + else + genericinh = ignore; + } + } + } else if (is_world_sid((const SID*)&pace->sid)) { + pxace->id = -1; + pxace->tag = POSIX_ACL_OTHER; + if ((pace->type == ACCESS_DENIED_ACE_TYPE) + && (pace->flags & INHERIT_ONLY_ACE)) + ignore = TRUE; + } else if (ntfs_same_sid((const SID*)&pace->sid,nullsid)) { + pxace->id = -1; + pxace->tag = POSIX_ACL_SPECIAL; + } else { + uid = NTFS_FIND_USER(mapping[MAPUSERS],&pace->sid); + if (uid) { + if ((pace->type == ACCESS_DENIED_ACE_TYPE) + && (pace->mask & WRITE_OWNER) + && (pctx->prevuid != uid)) { + pxace->id = -1; + pxace->tag = POSIX_ACL_MASK; + } else { + pxace->id = uid; + pxace->tag = POSIX_ACL_USER; + } + pctx->prevuid = uid; + } else { + gid = NTFS_FIND_GROUP(mapping[MAPGROUPS],&pace->sid); + if (gid) { + if ((pace->type == ACCESS_DENIED_ACE_TYPE) + && (pace->mask & WRITE_OWNER) + && (pctx->prevgid != gid)) { + pxace->tag = POSIX_ACL_MASK; + pctx->groupmasks++; + } else { + pxace->tag = POSIX_ACL_GROUP; + } + pxace->id = gid; + pctx->prevgid = gid; + } else { + /* + * do not grant rights to unknown + * people and do not define root as a + * designated user or group + */ + ignore = TRUE; + } + } + } + if (!ignore) { + pxace->perms = 0; + /* specific decoding for vtx/uid/gid */ + if (pxace->tag == POSIX_ACL_SPECIAL) { + if (pace->mask & FILE_APPEND_DATA) + pxace->perms |= S_ISUID; + if (pace->mask & FILE_WRITE_DATA) + pxace->perms |= S_ISGID; + if (pace->mask & FILE_READ_DATA) + pxace->perms |= S_ISVTX; + } else + if (isdir) { + if (pace->mask & DIR_GEXEC) + pxace->perms |= POSIX_PERM_X; + if (pace->mask & DIR_GWRITE) + pxace->perms |= POSIX_PERM_W; + if (pace->mask & DIR_GREAD) + pxace->perms |= POSIX_PERM_R; + if ((pace->mask & GENERIC_ALL) + && (pace->flags & INHERIT_ONLY_ACE)) + pxace->perms |= POSIX_PERM_X + | POSIX_PERM_W + | POSIX_PERM_R; + } else { + if (pace->mask & FILE_GEXEC) + pxace->perms |= POSIX_PERM_X; + if (pace->mask & FILE_GWRITE) + pxace->perms |= POSIX_PERM_W; + if (pace->mask & FILE_GREAD) + pxace->perms |= POSIX_PERM_R; + } - if ( pace->type != ACCESS_ALLOWED_ACE_TYPE ) - pxace->perms |= POSIX_PERM_DENIAL; - else if ( pxace->tag == POSIX_ACL_OTHER ) - pctx->permswrld = pxace->perms; - pctx->tagsset |= pxace->tag; - if ( pace->flags & INHERIT_ONLY_ACE ) - { - l--; - } - else - { - k++; - } - } - offace += le16_to_cpu( pace->size ); - } - /* - * Create world perms if none (both lists) - */ - for ( i = 0; i < 2; i++ ) - if ( ( genericinh || !i ) - && !( ctx[i].tagsset & POSIX_ACL_OTHER ) ) - { - if ( i ) - pxace = &pxdesc->acl.ace[--l]; - else - pxace = &pxdesc->acl.ace[k++]; - pxace->tag = POSIX_ACL_OTHER; - pxace->id = -1; - pxace->perms = 0; - ctx[i].tagsset |= POSIX_ACL_OTHER; - ctx[i].permswrld = 0; - } - /* - * Set basic owner perms if none (both lists) - * This happens for files created by Windows in directories - * created by Linux and owned by root, because Windows - * merges the admin ACEs - */ - for ( i = 0; i < 2; i++ ) - if ( !( ctx[i].tagsset & POSIX_ACL_USER_OBJ ) - && ( ctx[i].tagsset & POSIX_ACL_OTHER ) ) - { - if ( i ) - pxace = &pxdesc->acl.ace[--l]; - else - pxace = &pxdesc->acl.ace[k++]; - pxace->tag = POSIX_ACL_USER_OBJ; - pxace->id = -1; - pxace->perms = POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X; - ctx[i].tagsset |= POSIX_ACL_USER_OBJ; - } - /* - * Duplicate world perms as group_obj perms if none - */ - for ( i = 0; i < 2; i++ ) - if ( ( ctx[i].tagsset & POSIX_ACL_OTHER ) - && !( ctx[i].tagsset & POSIX_ACL_GROUP_OBJ ) ) - { - if ( i ) - pxace = &pxdesc->acl.ace[--l]; - else - pxace = &pxdesc->acl.ace[k++]; - pxace->tag = POSIX_ACL_GROUP_OBJ; - pxace->id = -1; - pxace->perms = ctx[i].permswrld; - ctx[i].tagsset |= POSIX_ACL_GROUP_OBJ; - } - /* - * Also duplicate world perms as group perms if they - * were converted to mask and not followed by a group entry - */ - if ( ctx[0].groupmasks ) - { - for ( j = k - 2; j >= 0; j-- ) - { - if ( ( pxdesc->acl.ace[j].tag == POSIX_ACL_MASK ) - && ( pxdesc->acl.ace[j].id != -1 ) - && ( ( pxdesc->acl.ace[j+1].tag != POSIX_ACL_GROUP ) - || ( pxdesc->acl.ace[j+1].id - != pxdesc->acl.ace[j].id ) ) ) - { - pxace = &pxdesc->acl.ace[k]; - pxace->tag = POSIX_ACL_GROUP; - pxace->id = pxdesc->acl.ace[j].id; - pxace->perms = ctx[0].permswrld; - ctx[0].tagsset |= POSIX_ACL_GROUP; - k++; - } - if ( pxdesc->acl.ace[j].tag == POSIX_ACL_MASK ) - pxdesc->acl.ace[j].id = -1; - } - } - if ( ctx[1].groupmasks ) - { - for ( j = l; j < ( alloccnt - 1 ); j++ ) - { - if ( ( pxdesc->acl.ace[j].tag == POSIX_ACL_MASK ) - && ( pxdesc->acl.ace[j].id != -1 ) - && ( ( pxdesc->acl.ace[j+1].tag != POSIX_ACL_GROUP ) - || ( pxdesc->acl.ace[j+1].id - != pxdesc->acl.ace[j].id ) ) ) - { - pxace = &pxdesc->acl.ace[l - 1]; - pxace->tag = POSIX_ACL_GROUP; - pxace->id = pxdesc->acl.ace[j].id; - pxace->perms = ctx[1].permswrld; - ctx[1].tagsset |= POSIX_ACL_GROUP; - l--; - } - if ( pxdesc->acl.ace[j].tag == POSIX_ACL_MASK ) - pxdesc->acl.ace[j].id = -1; - } - } + if (pace->type != ACCESS_ALLOWED_ACE_TYPE) + pxace->perms |= POSIX_PERM_DENIAL; + else + if (pxace->tag == POSIX_ACL_OTHER) + pctx->permswrld = pxace->perms; + pctx->tagsset |= pxace->tag; + if (pace->flags & INHERIT_ONLY_ACE) { + l--; + } else { + k++; + } + } + offace += le16_to_cpu(pace->size); + } + /* + * Create world perms if none (both lists) + */ + for (i=0; i<2; i++) + if ((genericinh || !i) + && !(ctx[i].tagsset & POSIX_ACL_OTHER)) { + if (i) + pxace = &pxdesc->acl.ace[--l]; + else + pxace = &pxdesc->acl.ace[k++]; + pxace->tag = POSIX_ACL_OTHER; + pxace->id = -1; + pxace->perms = 0; + ctx[i].tagsset |= POSIX_ACL_OTHER; + ctx[i].permswrld = 0; + } + /* + * Set basic owner perms if none (both lists) + * This happens for files created by Windows in directories + * created by Linux and owned by root, because Windows + * merges the admin ACEs + */ + for (i=0; i<2; i++) + if (!(ctx[i].tagsset & POSIX_ACL_USER_OBJ) + && (ctx[i].tagsset & POSIX_ACL_OTHER)) { + if (i) + pxace = &pxdesc->acl.ace[--l]; + else + pxace = &pxdesc->acl.ace[k++]; + pxace->tag = POSIX_ACL_USER_OBJ; + pxace->id = -1; + pxace->perms = POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X; + ctx[i].tagsset |= POSIX_ACL_USER_OBJ; + } + /* + * Duplicate world perms as group_obj perms if none + */ + for (i=0; i<2; i++) + if ((ctx[i].tagsset & POSIX_ACL_OTHER) + && !(ctx[i].tagsset & POSIX_ACL_GROUP_OBJ)) { + if (i) + pxace = &pxdesc->acl.ace[--l]; + else + pxace = &pxdesc->acl.ace[k++]; + pxace->tag = POSIX_ACL_GROUP_OBJ; + pxace->id = -1; + pxace->perms = ctx[i].permswrld; + ctx[i].tagsset |= POSIX_ACL_GROUP_OBJ; + } + /* + * Also duplicate world perms as group perms if they + * were converted to mask and not followed by a group entry + */ + if (ctx[0].groupmasks) { + for (j=k-2; j>=0; j--) { + if ((pxdesc->acl.ace[j].tag == POSIX_ACL_MASK) + && (pxdesc->acl.ace[j].id != -1) + && ((pxdesc->acl.ace[j+1].tag != POSIX_ACL_GROUP) + || (pxdesc->acl.ace[j+1].id + != pxdesc->acl.ace[j].id))) { + pxace = &pxdesc->acl.ace[k]; + pxace->tag = POSIX_ACL_GROUP; + pxace->id = pxdesc->acl.ace[j].id; + pxace->perms = ctx[0].permswrld; + ctx[0].tagsset |= POSIX_ACL_GROUP; + k++; + } + if (pxdesc->acl.ace[j].tag == POSIX_ACL_MASK) + pxdesc->acl.ace[j].id = -1; + } + } + if (ctx[1].groupmasks) { + for (j=l; j<(alloccnt-1); j++) { + if ((pxdesc->acl.ace[j].tag == POSIX_ACL_MASK) + && (pxdesc->acl.ace[j].id != -1) + && ((pxdesc->acl.ace[j+1].tag != POSIX_ACL_GROUP) + || (pxdesc->acl.ace[j+1].id + != pxdesc->acl.ace[j].id))) { + pxace = &pxdesc->acl.ace[l - 1]; + pxace->tag = POSIX_ACL_GROUP; + pxace->id = pxdesc->acl.ace[j].id; + pxace->perms = ctx[1].permswrld; + ctx[1].tagsset |= POSIX_ACL_GROUP; + l--; + } + if (pxdesc->acl.ace[j].tag == POSIX_ACL_MASK) + pxdesc->acl.ace[j].id = -1; + } + } - /* - * Insert default mask if none present and - * there are designated users or groups - * (the space for it has not beed used) - */ - for ( i = 0; i < 2; i++ ) - if ( ( ctx[i].tagsset & ( POSIX_ACL_USER | POSIX_ACL_GROUP ) ) - && !( ctx[i].tagsset & POSIX_ACL_MASK ) ) - { - if ( i ) - pxace = &pxdesc->acl.ace[--l]; - else - pxace = &pxdesc->acl.ace[k++]; - pxace->tag = POSIX_ACL_MASK; - pxace->id = -1; - pxace->perms = POSIX_PERM_DENIAL; - ctx[i].tagsset |= POSIX_ACL_MASK; - } + /* + * Insert default mask if none present and + * there are designated users or groups + * (the space for it has not beed used) + */ + for (i=0; i<2; i++) + if ((ctx[i].tagsset & (POSIX_ACL_USER | POSIX_ACL_GROUP)) + && !(ctx[i].tagsset & POSIX_ACL_MASK)) { + if (i) + pxace = &pxdesc->acl.ace[--l]; + else + pxace = &pxdesc->acl.ace[k++]; + pxace->tag = POSIX_ACL_MASK; + pxace->id = -1; + pxace->perms = POSIX_PERM_DENIAL; + ctx[i].tagsset |= POSIX_ACL_MASK; + } - if ( k > l ) - { - ntfs_log_error( "Posix descriptor is longer than expected\n" ); - errno = EIO; - free( pxdesc ); - pxdesc = ( struct POSIX_SECURITY* )NULL; - } - else - { - pxdesc->acccnt = k; - pxdesc->defcnt = alloccnt - l; - pxdesc->firstdef = l; - pxdesc->tagsset = ctx[0].tagsset; - pxdesc->acl.version = POSIX_VERSION; - pxdesc->acl.flags = 0; - pxdesc->acl.filler = 0; - ntfs_sort_posix( pxdesc ); - if ( adminowns ) - { - k = norm_ownadmin_permissions_posix( pxdesc, - 0, pxdesc->acccnt, 0 ); - pxdesc->acccnt = k; - l = norm_ownadmin_permissions_posix( pxdesc, - pxdesc->firstdef, pxdesc->defcnt, k ); - pxdesc->firstdef = k; - pxdesc->defcnt = l; - } - else - { - k = norm_std_permissions_posix( pxdesc, groupowns, - 0, pxdesc->acccnt, 0 ); - pxdesc->acccnt = k; - l = norm_std_permissions_posix( pxdesc, groupowns, - pxdesc->firstdef, pxdesc->defcnt, k ); - pxdesc->firstdef = k; - pxdesc->defcnt = l; - } - } - if ( pxdesc && !ntfs_valid_posix( pxdesc ) ) - { - ntfs_log_error( "Invalid Posix descriptor built\n" ); - errno = EIO; - free( pxdesc ); - pxdesc = ( struct POSIX_SECURITY* )NULL; - } - return ( pxdesc ); + if (k > l) { + ntfs_log_error("Posix descriptor is longer than expected\n"); + errno = EIO; + free(pxdesc); + pxdesc = (struct POSIX_SECURITY*)NULL; + } else { + pxdesc->acccnt = k; + pxdesc->defcnt = alloccnt - l; + pxdesc->firstdef = l; + pxdesc->tagsset = ctx[0].tagsset; + pxdesc->acl.version = POSIX_VERSION; + pxdesc->acl.flags = 0; + pxdesc->acl.filler = 0; + ntfs_sort_posix(pxdesc); + if (adminowns) { + k = norm_ownadmin_permissions_posix(pxdesc, + 0, pxdesc->acccnt, 0); + pxdesc->acccnt = k; + l = norm_ownadmin_permissions_posix(pxdesc, + pxdesc->firstdef, pxdesc->defcnt, k); + pxdesc->firstdef = k; + pxdesc->defcnt = l; + } else { + k = norm_std_permissions_posix(pxdesc,groupowns, + 0, pxdesc->acccnt, 0); + pxdesc->acccnt = k; + l = norm_std_permissions_posix(pxdesc,groupowns, + pxdesc->firstdef, pxdesc->defcnt, k); + pxdesc->firstdef = k; + pxdesc->defcnt = l; + } + } + if (pxdesc && !ntfs_valid_posix(pxdesc)) { + ntfs_log_error("Invalid Posix descriptor built\n"); + errno = EIO; + free(pxdesc); + pxdesc = (struct POSIX_SECURITY*)NULL; + } + return (pxdesc); } #endif /* POSIXACLS */ /* - * Build unix-style (mode_t) permissions from an ACL - * returns the requested permissions - * or a negative result (with errno set) if there is a problem + * Build unix-style (mode_t) permissions from an ACL + * returns the requested permissions + * or a negative result (with errno set) if there is a problem */ -int ntfs_build_permissions( const char *securattr, - const SID *usid, const SID *gsid, BOOL isdir ) +int ntfs_build_permissions(const char *securattr, + const SID *usid, const SID *gsid, BOOL isdir) { - const SECURITY_DESCRIPTOR_RELATIVE *phead; - int perm; - BOOL adminowns; - BOOL groupowns; + const SECURITY_DESCRIPTOR_RELATIVE *phead; + int perm; + BOOL adminowns; + BOOL groupowns; - phead = ( const SECURITY_DESCRIPTOR_RELATIVE* )securattr; - adminowns = ntfs_same_sid( usid, adminsid ) - || ntfs_same_sid( gsid, adminsid ); - groupowns = !adminowns && ntfs_same_sid( gsid, usid ); - if ( adminowns ) - perm = build_ownadmin_permissions( securattr, usid, gsid, isdir ); - else if ( groupowns ) - perm = build_owngrp_permissions( securattr, usid, isdir ); - else - perm = build_std_permissions( securattr, usid, gsid, isdir ); - return ( perm ); + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; + adminowns = ntfs_same_sid(usid,adminsid) + || ntfs_same_sid(gsid,adminsid); + groupowns = !adminowns && ntfs_same_sid(gsid,usid); + if (adminowns) + perm = build_ownadmin_permissions(securattr, usid, gsid, isdir); + else + if (groupowns) + perm = build_owngrp_permissions(securattr, usid, isdir); + else + perm = build_std_permissions(securattr, usid, gsid, isdir); + return (perm); } /* - * The following must be in some library... + * The following must be in some library... */ -static unsigned long atoul( const char *p ) -{ /* must be somewhere ! */ - unsigned long v; +static unsigned long atoul(const char *p) +{ /* must be somewhere ! */ + unsigned long v; - v = 0; - while ( ( *p >= '0' ) && ( *p <= '9' ) ) - v = v * 10 + ( *p++ ) - '0'; - return ( v ); + v = 0; + while ((*p >= '0') && (*p <= '9')) + v = v * 10 + (*p++) - '0'; + return (v); } /* - * Build an internal representation of a SID - * Returns a copy in allocated memory if it succeeds - * The SID is checked to be a valid user one. + * Build an internal representation of a SID + * Returns a copy in allocated memory if it succeeds + * The SID is checked to be a valid user one. */ -static SID *encodesid( const char *sidstr ) +static SID *encodesid(const char *sidstr) { - SID *sid; - int cnt; - BIGSID bigsid; - SID *bsid; - u32 auth; - const char *p; + SID *sid; + int cnt; + BIGSID bigsid; + SID *bsid; + u32 auth; + const char *p; - sid = ( SID* ) NULL; - if ( !strncmp( sidstr, "S-1-", 4 ) ) - { - bsid = ( SID* ) & bigsid; - bsid->revision = SID_REVISION; - p = &sidstr[4]; - auth = atoul( p ); - bsid->identifier_authority.high_part = const_cpu_to_be16( 0 ); - bsid->identifier_authority.low_part = cpu_to_be32( auth ); - cnt = 0; - p = strchr( p, '-' ); - while ( p && ( cnt < 8 ) ) - { - p++; - auth = atoul( p ); - bsid->sub_authority[cnt] = cpu_to_le32( auth ); - p = strchr( p, '-' ); - cnt++; - } - bsid->sub_authority_count = cnt; - if ( ( cnt > 0 ) && ntfs_valid_sid( bsid ) && ntfs_is_user_sid( bsid ) ) - { - sid = ( SID* ) ntfs_malloc( 4 * cnt + 8 ); - if ( sid ) - memcpy( sid, bsid, 4 * cnt + 8 ); - } - } - return ( sid ); + sid = (SID*) NULL; + if (!strncmp(sidstr, "S-1-", 4)) { + bsid = (SID*)&bigsid; + bsid->revision = SID_REVISION; + p = &sidstr[4]; + auth = atoul(p); + bsid->identifier_authority.high_part = const_cpu_to_be16(0); + bsid->identifier_authority.low_part = cpu_to_be32(auth); + cnt = 0; + p = strchr(p, '-'); + while (p && (cnt < 8)) { + p++; + auth = atoul(p); + bsid->sub_authority[cnt] = cpu_to_le32(auth); + p = strchr(p, '-'); + cnt++; + } + bsid->sub_authority_count = cnt; + if ((cnt > 0) && ntfs_valid_sid(bsid) && ntfs_is_user_sid(bsid)) { + sid = (SID*) ntfs_malloc(4 * cnt + 8); + if (sid) + memcpy(sid, bsid, 4 * cnt + 8); + } + } + return (sid); } /* - * Early logging before the logs are redirected + * Early logging before the logs are redirected * - * (not quite satisfactory : this appears before the ntfs-g banner, - * and with a different pid) + * (not quite satisfactory : this appears before the ntfs-g banner, + * and with a different pid) */ -static void log_early_error( const char *format, ... ) -__attribute__( ( format( printf, 1, 2 ) ) ); +static void log_early_error(const char *format, ...) + __attribute__((format(printf, 1, 2))); -static void log_early_error( const char *format, ... ) +static void log_early_error(const char *format, ...) { - va_list args; + va_list args; - va_start( args, format ); + va_start(args, format); #ifdef HAVE_SYSLOG_H - openlog( "ntfs-3g", LOG_PID, LOG_USER ); - ntfs_log_handler_syslog( NULL, NULL, 0, - NTFS_LOG_LEVEL_ERROR, NULL, - format, args ); + openlog("ntfs-3g", LOG_PID, LOG_USER); + ntfs_log_handler_syslog(NULL, NULL, 0, + NTFS_LOG_LEVEL_ERROR, NULL, + format, args); #else - vfprintf( stderr, format, args ); + vfprintf(stderr,format,args); #endif - va_end( args ); + va_end(args); } /* - * Get a single mapping item from buffer + * Get a single mapping item from buffer * - * Always reads a full line, truncating long lines - * Refills buffer when exhausted - * Returns pointer to item, or NULL when there is no more + * Always reads a full line, truncating long lines + * Refills buffer when exhausted + * Returns pointer to item, or NULL when there is no more */ -static struct MAPLIST *getmappingitem( FILEREADER reader, void *fileid, - off_t *poffs, char *buf, int *psrc, s64 *psize ) +static struct MAPLIST *getmappingitem(FILEREADER reader, void *fileid, + off_t *poffs, char *buf, int *psrc, s64 *psize) { - int src; - int dst; - char *p; - char *q; - char *pu; - char *pg; - int gotend; - struct MAPLIST *item; + int src; + int dst; + char *p; + char *q; + char *pu; + char *pg; + int gotend; + struct MAPLIST *item; - src = *psrc; - dst = 0; - /* allocate and get a full line */ - item = ( struct MAPLIST* )ntfs_malloc( sizeof( struct MAPLIST ) ); - if ( item ) - { - do - { - gotend = 0; - while ( ( src < *psize ) - && ( buf[src] != '\n' ) ) - { - if ( dst < LINESZ ) - item->maptext[dst++] = buf[src]; - src++; - } - if ( src >= *psize ) - { - *poffs += *psize; - *psize = reader( fileid, buf, ( size_t )BUFSZ, *poffs ); - src = 0; - } - else - { - gotend = 1; - src++; - item->maptext[dst] = '\0'; - dst = 0; - } - } - while ( *psize && ( ( item->maptext[0] == '#' ) || !gotend ) ); - if ( gotend ) - { - pu = pg = ( char* )NULL; - /* decompose into uid, gid and sid */ - p = item->maptext; - item->uidstr = item->maptext; - item->gidstr = strchr( item->uidstr, ':' ); - if ( item->gidstr ) - { - pu = item->gidstr++; - item->sidstr = strchr( item->gidstr, ':' ); - if ( item->sidstr ) - { - pg = item->sidstr++; - q = strchr( item->sidstr, ':' ); - if ( q ) *q = 0; - } - } - if ( pu && pg ) - *pu = *pg = '\0'; - else - { - log_early_error( "Bad mapping item \"%s\"\n", - item->maptext ); - free( item ); - item = ( struct MAPLIST* )NULL; - } - } - else - { - free( item ); /* free unused item */ - item = ( struct MAPLIST* )NULL; - } - } - *psrc = src; - return ( item ); + src = *psrc; + dst = 0; + /* allocate and get a full line */ + item = (struct MAPLIST*)ntfs_malloc(sizeof(struct MAPLIST)); + if (item) { + do { + gotend = 0; + while ((src < *psize) + && (buf[src] != '\n')) { + if (dst < LINESZ) + item->maptext[dst++] = buf[src]; + src++; + } + if (src >= *psize) { + *poffs += *psize; + *psize = reader(fileid, buf, (size_t)BUFSZ, *poffs); + src = 0; + } else { + gotend = 1; + src++; + item->maptext[dst] = '\0'; + dst = 0; + } + } while (*psize && ((item->maptext[0] == '#') || !gotend)); + if (gotend) { + pu = pg = (char*)NULL; + /* decompose into uid, gid and sid */ + p = item->maptext; + item->uidstr = item->maptext; + item->gidstr = strchr(item->uidstr, ':'); + if (item->gidstr) { + pu = item->gidstr++; + item->sidstr = strchr(item->gidstr, ':'); + if (item->sidstr) { + pg = item->sidstr++; + q = strchr(item->sidstr, ':'); + if (q) *q = 0; + } + } + if (pu && pg) + *pu = *pg = '\0'; + else { + log_early_error("Bad mapping item \"%s\"\n", + item->maptext); + free(item); + item = (struct MAPLIST*)NULL; + } + } else { + free(item); /* free unused item */ + item = (struct MAPLIST*)NULL; + } + } + *psrc = src; + return (item); } /* - * Read user mapping file and split into their attribute. - * Parameters are kept as text in a chained list until logins - * are converted to uid. - * Returns the head of list, if any + * Read user mapping file and split into their attribute. + * Parameters are kept as text in a chained list until logins + * are converted to uid. + * Returns the head of list, if any * - * If an absolute path is provided, the mapping file is assumed - * to be located in another mounted file system, and plain read() - * are used to get its contents. - * If a relative path is provided, the mapping file is assumed - * to be located on the current file system, and internal IO - * have to be used since we are still mounting and we have not - * entered the fuse loop yet. + * If an absolute path is provided, the mapping file is assumed + * to be located in another mounted file system, and plain read() + * are used to get its contents. + * If a relative path is provided, the mapping file is assumed + * to be located on the current file system, and internal IO + * have to be used since we are still mounting and we have not + * entered the fuse loop yet. */ -struct MAPLIST *ntfs_read_mapping( FILEREADER reader, void *fileid ) +struct MAPLIST *ntfs_read_mapping(FILEREADER reader, void *fileid) { - char buf[BUFSZ]; - struct MAPLIST *item; - struct MAPLIST *firstitem; - struct MAPLIST *lastitem; - int src; - off_t offs; - s64 size; + char buf[BUFSZ]; + struct MAPLIST *item; + struct MAPLIST *firstitem; + struct MAPLIST *lastitem; + int src; + off_t offs; + s64 size; - firstitem = ( struct MAPLIST* )NULL; - lastitem = ( struct MAPLIST* )NULL; - offs = 0; - size = reader( fileid, buf, ( size_t )BUFSZ, ( off_t )0 ); - if ( size > 0 ) - { - src = 0; - do - { - item = getmappingitem( reader, fileid, &offs, - buf, &src, &size ); - if ( item ) - { - item->next = ( struct MAPLIST* )NULL; - if ( lastitem ) - lastitem->next = item; - else - firstitem = item; - lastitem = item; - } - } - while ( item ); - } - return ( firstitem ); + firstitem = (struct MAPLIST*)NULL; + lastitem = (struct MAPLIST*)NULL; + offs = 0; + size = reader(fileid, buf, (size_t)BUFSZ, (off_t)0); + if (size > 0) { + src = 0; + do { + item = getmappingitem(reader, fileid, &offs, + buf, &src, &size); + if (item) { + item->next = (struct MAPLIST*)NULL; + if (lastitem) + lastitem->next = item; + else + firstitem = item; + lastitem = item; + } + } while (item); + } + return (firstitem); } /* - * Free memory used to store the user mapping - * The only purpose is to facilitate the detection of memory leaks + * Free memory used to store the user mapping + * The only purpose is to facilitate the detection of memory leaks */ -void ntfs_free_mapping( struct MAPPING *mapping[] ) +void ntfs_free_mapping(struct MAPPING *mapping[]) { - struct MAPPING *user; - struct MAPPING *group; + struct MAPPING *user; + struct MAPPING *group; - /* free user mappings */ - while ( mapping[MAPUSERS] ) - { - user = mapping[MAPUSERS]; - /* do not free SIDs used for group mappings */ - group = mapping[MAPGROUPS]; - while ( group && ( group->sid != user->sid ) ) - group = group->next; - if ( !group ) - free( user->sid ); - /* free group list if any */ - if ( user->grcnt ) - free( user->groups ); - /* unchain item and free */ - mapping[MAPUSERS] = user->next; - free( user ); - } - /* free group mappings */ - while ( mapping[MAPGROUPS] ) - { - group = mapping[MAPGROUPS]; - free( group->sid ); - /* unchain item and free */ - mapping[MAPGROUPS] = group->next; - free( group ); - } + /* free user mappings */ + while (mapping[MAPUSERS]) { + user = mapping[MAPUSERS]; + /* do not free SIDs used for group mappings */ + group = mapping[MAPGROUPS]; + while (group && (group->sid != user->sid)) + group = group->next; + if (!group) + free(user->sid); + /* free group list if any */ + if (user->grcnt) + free(user->groups); + /* unchain item and free */ + mapping[MAPUSERS] = user->next; + free(user); + } + /* free group mappings */ + while (mapping[MAPGROUPS]) { + group = mapping[MAPGROUPS]; + free(group->sid); + /* unchain item and free */ + mapping[MAPGROUPS] = group->next; + free(group); + } } /* - * Build the user mapping list - * user identification may be given in symbolic or numeric format + * Build the user mapping list + * user identification may be given in symbolic or numeric format * - * ! Note ! : does getpwnam() read /etc/passwd or some other file ? - * if so there is a possible recursion into fuse if this - * file is on NTFS, and fuse is not recursion safe. + * ! Note ! : does getpwnam() read /etc/passwd or some other file ? + * if so there is a possible recursion into fuse if this + * file is on NTFS, and fuse is not recursion safe. */ -struct MAPPING *ntfs_do_user_mapping( struct MAPLIST *firstitem ) +struct MAPPING *ntfs_do_user_mapping(struct MAPLIST *firstitem) { - struct MAPLIST *item; - struct MAPPING *firstmapping; - struct MAPPING *lastmapping; - struct MAPPING *mapping; - struct passwd *pwd; - SID *sid; - int uid; + struct MAPLIST *item; + struct MAPPING *firstmapping; + struct MAPPING *lastmapping; + struct MAPPING *mapping; + struct passwd *pwd; + SID *sid; + int uid; - firstmapping = ( struct MAPPING* )NULL; - lastmapping = ( struct MAPPING* )NULL; - for ( item = firstitem; item; item = item->next ) - { - if ( ( item->uidstr[0] >= '0' ) && ( item->uidstr[0] <= '9' ) ) - uid = atoi( item->uidstr ); - else - { - uid = 0; - if ( item->uidstr[0] ) - { - pwd = getpwnam( item->uidstr ); - if ( pwd ) - uid = pwd->pw_uid; - else - log_early_error( "Invalid user \"%s\"\n", - item->uidstr ); - } - } - /* - * Records with no uid and no gid are inserted - * to define the implicit mapping pattern - */ - if ( uid - || ( !item->uidstr[0] && !item->gidstr[0] ) ) - { - sid = encodesid( item->sidstr ); - if ( sid && !item->uidstr[0] && !item->gidstr[0] - && !ntfs_valid_pattern( sid ) ) - { - ntfs_log_error( "Bad implicit SID pattern %s\n", - item->sidstr ); - sid = ( SID* )NULL; - } - if ( sid ) - { - mapping = - ( struct MAPPING* ) - ntfs_malloc( sizeof( struct MAPPING ) ); - if ( mapping ) - { - mapping->sid = sid; - mapping->xid = uid; - mapping->grcnt = 0; - mapping->next = ( struct MAPPING* )NULL; - if ( lastmapping ) - lastmapping->next = mapping; - else - firstmapping = mapping; - lastmapping = mapping; - } - } - } - } - return ( firstmapping ); + firstmapping = (struct MAPPING*)NULL; + lastmapping = (struct MAPPING*)NULL; + for (item = firstitem; item; item = item->next) { + if ((item->uidstr[0] >= '0') && (item->uidstr[0] <= '9')) + uid = atoi(item->uidstr); + else { + uid = 0; + if (item->uidstr[0]) { + pwd = getpwnam(item->uidstr); + if (pwd) + uid = pwd->pw_uid; + else + log_early_error("Invalid user \"%s\"\n", + item->uidstr); + } + } + /* + * Records with no uid and no gid are inserted + * to define the implicit mapping pattern + */ + if (uid + || (!item->uidstr[0] && !item->gidstr[0])) { + sid = encodesid(item->sidstr); + if (sid && !item->uidstr[0] && !item->gidstr[0] + && !ntfs_valid_pattern(sid)) { + ntfs_log_error("Bad implicit SID pattern %s\n", + item->sidstr); + sid = (SID*)NULL; + } + if (sid) { + mapping = + (struct MAPPING*) + ntfs_malloc(sizeof(struct MAPPING)); + if (mapping) { + mapping->sid = sid; + mapping->xid = uid; + mapping->grcnt = 0; + mapping->next = (struct MAPPING*)NULL; + if (lastmapping) + lastmapping->next = mapping; + else + firstmapping = mapping; + lastmapping = mapping; + } + } + } + } + return (firstmapping); } /* - * Build the group mapping list - * group identification may be given in symbolic or numeric format + * Build the group mapping list + * group identification may be given in symbolic or numeric format * - * gid not associated to a uid are processed first in order - * to favour real groups + * gid not associated to a uid are processed first in order + * to favour real groups * - * ! Note ! : does getgrnam() read /etc/group or some other file ? - * if so there is a possible recursion into fuse if this - * file is on NTFS, and fuse is not recursion safe. + * ! Note ! : does getgrnam() read /etc/group or some other file ? + * if so there is a possible recursion into fuse if this + * file is on NTFS, and fuse is not recursion safe. */ -struct MAPPING *ntfs_do_group_mapping( struct MAPLIST *firstitem ) +struct MAPPING *ntfs_do_group_mapping(struct MAPLIST *firstitem) { - struct MAPLIST *item; - struct MAPPING *firstmapping; - struct MAPPING *lastmapping; - struct MAPPING *mapping; - struct group *grp; - BOOL secondstep; - BOOL ok; - int step; - SID *sid; - int gid; + struct MAPLIST *item; + struct MAPPING *firstmapping; + struct MAPPING *lastmapping; + struct MAPPING *mapping; + struct group *grp; + BOOL secondstep; + BOOL ok; + int step; + SID *sid; + int gid; - firstmapping = ( struct MAPPING* )NULL; - lastmapping = ( struct MAPPING* )NULL; - for ( step = 1; step <= 2; step++ ) - { - for ( item = firstitem; item; item = item->next ) - { - secondstep = ( item->uidstr[0] != '\0' ) - || !item->gidstr[0]; - ok = ( step == 1 ? !secondstep : secondstep ); - if ( ( item->gidstr[0] >= '0' ) - && ( item->gidstr[0] <= '9' ) ) - gid = atoi( item->gidstr ); - else - { - gid = 0; - if ( item->gidstr[0] ) - { - grp = getgrnam( item->gidstr ); - if ( grp ) - gid = grp->gr_gid; - else - log_early_error( "Invalid group \"%s\"\n", - item->gidstr ); - } - } - /* - * Records with no uid and no gid are inserted in the - * second step to define the implicit mapping pattern - */ - if ( ok - && ( gid - || ( !item->uidstr[0] && !item->gidstr[0] ) ) ) - { - sid = encodesid( item->sidstr ); - if ( sid && !item->uidstr[0] && !item->gidstr[0] - && !ntfs_valid_pattern( sid ) ) - { - /* error already logged */ - sid = ( SID* )NULL; - } - if ( sid ) - { - mapping = ( struct MAPPING* ) - ntfs_malloc( sizeof( struct MAPPING ) ); - if ( mapping ) - { - mapping->sid = sid; - mapping->xid = gid; - mapping->grcnt = 0; - mapping->next = ( struct MAPPING* )NULL; - if ( lastmapping ) - lastmapping->next = mapping; - else - firstmapping = mapping; - lastmapping = mapping; - } - } - } - } - } - return ( firstmapping ); + firstmapping = (struct MAPPING*)NULL; + lastmapping = (struct MAPPING*)NULL; + for (step=1; step<=2; step++) { + for (item = firstitem; item; item = item->next) { + secondstep = (item->uidstr[0] != '\0') + || !item->gidstr[0]; + ok = (step == 1 ? !secondstep : secondstep); + if ((item->gidstr[0] >= '0') + && (item->gidstr[0] <= '9')) + gid = atoi(item->gidstr); + else { + gid = 0; + if (item->gidstr[0]) { + grp = getgrnam(item->gidstr); + if (grp) + gid = grp->gr_gid; + else + log_early_error("Invalid group \"%s\"\n", + item->gidstr); + } + } + /* + * Records with no uid and no gid are inserted in the + * second step to define the implicit mapping pattern + */ + if (ok + && (gid + || (!item->uidstr[0] && !item->gidstr[0]))) { + sid = encodesid(item->sidstr); + if (sid && !item->uidstr[0] && !item->gidstr[0] + && !ntfs_valid_pattern(sid)) { + /* error already logged */ + sid = (SID*)NULL; + } + if (sid) { + mapping = (struct MAPPING*) + ntfs_malloc(sizeof(struct MAPPING)); + if (mapping) { + mapping->sid = sid; + mapping->xid = gid; + mapping->grcnt = 0; + mapping->next = (struct MAPPING*)NULL; + if (lastmapping) + lastmapping->next = mapping; + else + firstmapping = mapping; + lastmapping = mapping; + } + } + } + } + } + return (firstmapping); } diff --git a/source/libntfs/acls.h b/source/libntfs/acls.h index 50a6ae6e..8a83d32d 100644 --- a/source/libntfs/acls.h +++ b/source/libntfs/acls.h @@ -25,30 +25,30 @@ #define ACLS_H /* - * JPA configuration modes for security.c / acls.c - * should be moved to some config file + * JPA configuration modes for security.c / acls.c + * should be moved to some config file */ -#define BUFSZ 1024 /* buffer size to read mapping file */ +#define BUFSZ 1024 /* buffer size to read mapping file */ #define MAPPINGFILE ".NTFS-3G/UserMapping" /* default mapping file */ #define LINESZ 120 /* maximum useful size of a mapping line */ #define CACHE_PERMISSIONS_BITS 6 /* log2 of unitary allocation of permissions */ #define CACHE_PERMISSIONS_SIZE 262144 /* max cacheable permissions */ /* - * JPA The following must be in some library... - * but did not found out where + * JPA The following must be in some library... + * but did not found out where */ #define endian_rev16(x) (((x >> 8) & 255) | ((x & 255) << 8)) #define endian_rev32(x) (((x >> 24) & 255) | ((x >> 8) & 0xff00) \ - | ((x & 0xff00) << 8) | ((x & 255) << 24)) + | ((x & 0xff00) << 8) | ((x & 255) << 24)) #define cpu_to_be16(x) endian_rev16(cpu_to_le16(x)) #define cpu_to_be32(x) endian_rev32(cpu_to_le32(x)) /* - * Macro definitions needed to share code with secaudit + * Macro definitions needed to share code with secaudit */ #define NTFS_FIND_USID(map,uid,buf) ntfs_find_usid(map,uid,buf) @@ -58,25 +58,25 @@ /* - * Matching of ntfs permissions to Linux permissions - * these constants are adapted to endianness - * when setting, set them all - * when checking, check one is present + * Matching of ntfs permissions to Linux permissions + * these constants are adapted to endianness + * when setting, set them all + * when checking, check one is present */ -/* flags which are set to mean exec, write or read */ + /* flags which are set to mean exec, write or read */ #define FILE_READ (FILE_READ_DATA) #define FILE_WRITE (FILE_WRITE_DATA | FILE_APPEND_DATA \ - | READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA) + | READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA) #define FILE_EXEC (FILE_EXECUTE) #define DIR_READ FILE_LIST_DIRECTORY #define DIR_WRITE (FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY | FILE_DELETE_CHILD \ - | READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA) + | READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA) #define DIR_EXEC (FILE_TRAVERSE) -/* flags tested for meaning exec, write or read */ -/* tests for write allow for interpretation of a sticky bit */ + /* flags tested for meaning exec, write or read */ + /* tests for write allow for interpretation of a sticky bit */ #define FILE_GREAD (FILE_READ_DATA | GENERIC_READ) #define FILE_GWRITE (FILE_WRITE_DATA | FILE_APPEND_DATA | GENERIC_WRITE) @@ -85,116 +85,115 @@ #define DIR_GWRITE (FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY | GENERIC_WRITE) #define DIR_GEXEC (FILE_TRAVERSE | GENERIC_EXECUTE) -/* standard owner (and administrator) rights */ + /* standard owner (and administrator) rights */ #define OWNER_RIGHTS (DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER \ - | SYNCHRONIZE \ - | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES \ - | FILE_READ_EA | FILE_WRITE_EA) + | SYNCHRONIZE \ + | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES \ + | FILE_READ_EA | FILE_WRITE_EA) -/* standard world rights */ + /* standard world rights */ #define WORLD_RIGHTS (READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_READ_EA \ - | SYNCHRONIZE) + | SYNCHRONIZE) -/* inheritance flags for files and directories */ + /* inheritance flags for files and directories */ #define FILE_INHERITANCE NO_PROPAGATE_INHERIT_ACE #define DIR_INHERITANCE (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE) /* - * To identify NTFS ACL meaning Posix ACL granted to root - * we use rights always granted to anybody, so they have no impact - * either on Windows or on Linux. + * To identify NTFS ACL meaning Posix ACL granted to root + * we use rights always granted to anybody, so they have no impact + * either on Windows or on Linux. */ -#define ROOT_OWNER_UNMARK SYNCHRONIZE /* ACL granted to root as owner */ -#define ROOT_GROUP_UNMARK FILE_READ_EA /* ACL granted to root as group */ +#define ROOT_OWNER_UNMARK SYNCHRONIZE /* ACL granted to root as owner */ +#define ROOT_GROUP_UNMARK FILE_READ_EA /* ACL granted to root as group */ /* - * A type large enough to hold any SID + * A type large enough to hold any SID */ typedef char BIGSID[40]; /* - * Struct to hold the input mapping file - * (private to this module) + * Struct to hold the input mapping file + * (private to this module) */ -struct MAPLIST -{ - struct MAPLIST *next; - char *uidstr; /* uid text from the same record */ - char *gidstr; /* gid text from the same record */ - char *sidstr; /* sid text from the same record */ - char maptext[LINESZ + 1]; +struct MAPLIST { + struct MAPLIST *next; + char *uidstr; /* uid text from the same record */ + char *gidstr; /* gid text from the same record */ + char *sidstr; /* sid text from the same record */ + char maptext[LINESZ + 1]; }; -typedef int ( *FILEREADER )( void *fileid, char *buf, size_t size, off_t pos ); +typedef int (*FILEREADER)(void *fileid, char *buf, size_t size, off_t pos); /* - * Constants defined in acls.c + * Constants defined in acls.c */ extern const SID *adminsid; extern const SID *worldsid; /* - * Functions defined in acls.c + * Functions defined in acls.c */ -BOOL ntfs_valid_descr( const char *securattr, unsigned int attrsz ); -BOOL ntfs_valid_pattern( const SID *sid ); -BOOL ntfs_valid_sid( const SID *sid ); -BOOL ntfs_same_sid( const SID *first, const SID *second ); +BOOL ntfs_valid_descr(const char *securattr, unsigned int attrsz); +BOOL ntfs_valid_pattern(const SID *sid); +BOOL ntfs_valid_sid(const SID *sid); +BOOL ntfs_same_sid(const SID *first, const SID *second); -BOOL ntfs_is_user_sid( const SID *usid ); +BOOL ntfs_is_user_sid(const SID *usid); -int ntfs_sid_size( const SID * sid ); -unsigned int ntfs_attr_size( const char *attr ); +int ntfs_sid_size(const SID * sid); +unsigned int ntfs_attr_size(const char *attr); -const SID *ntfs_find_usid( const struct MAPPING *usermapping, - uid_t uid, SID *pdefsid ); -const SID *ntfs_find_gsid( const struct MAPPING *groupmapping, - gid_t gid, SID *pdefsid ); -uid_t ntfs_find_user( const struct MAPPING *usermapping, const SID *usid ); -gid_t ntfs_find_group( const struct MAPPING *groupmapping, const SID * gsid ); -const SID *ntfs_acl_owner( const char *secattr ); +const SID *ntfs_find_usid(const struct MAPPING *usermapping, + uid_t uid, SID *pdefsid); +const SID *ntfs_find_gsid(const struct MAPPING *groupmapping, + gid_t gid, SID *pdefsid); +uid_t ntfs_find_user(const struct MAPPING *usermapping, const SID *usid); +gid_t ntfs_find_group(const struct MAPPING *groupmapping, const SID * gsid); +const SID *ntfs_acl_owner(const char *secattr); #if POSIXACLS -BOOL ntfs_valid_posix( const struct POSIX_SECURITY *pxdesc ); -void ntfs_sort_posix( struct POSIX_SECURITY *pxdesc ); -int ntfs_merge_mode_posix( struct POSIX_SECURITY *pxdesc, mode_t mode ); +BOOL ntfs_valid_posix(const struct POSIX_SECURITY *pxdesc); +void ntfs_sort_posix(struct POSIX_SECURITY *pxdesc); +int ntfs_merge_mode_posix(struct POSIX_SECURITY *pxdesc, mode_t mode); struct POSIX_SECURITY *ntfs_build_inherited_posix( - const struct POSIX_SECURITY *pxdesc, mode_t mode, - mode_t umask, BOOL isdir ); -struct POSIX_SECURITY *ntfs_replace_acl( const struct POSIX_SECURITY *oldpxdesc, - const struct POSIX_ACL *newacl, int count, BOOL deflt ); + const struct POSIX_SECURITY *pxdesc, mode_t mode, + mode_t umask, BOOL isdir); +struct POSIX_SECURITY *ntfs_replace_acl(const struct POSIX_SECURITY *oldpxdesc, + const struct POSIX_ACL *newacl, int count, BOOL deflt); struct POSIX_SECURITY *ntfs_build_permissions_posix( - struct MAPPING* const mapping[], - const char *securattr, - const SID *usid, const SID *gsid, BOOL isdir ); -struct POSIX_SECURITY *ntfs_merge_descr_posix( const struct POSIX_SECURITY *first, - const struct POSIX_SECURITY *second ); -char *ntfs_build_descr_posix( struct MAPPING* const mapping[], - struct POSIX_SECURITY *pxdesc, - int isdir, const SID *usid, const SID *gsid ); + struct MAPPING* const mapping[], + const char *securattr, + const SID *usid, const SID *gsid, BOOL isdir); +struct POSIX_SECURITY *ntfs_merge_descr_posix(const struct POSIX_SECURITY *first, + const struct POSIX_SECURITY *second); +char *ntfs_build_descr_posix(struct MAPPING* const mapping[], + struct POSIX_SECURITY *pxdesc, + int isdir, const SID *usid, const SID *gsid); #endif /* POSIXACLS */ -int ntfs_inherit_acl( const ACL *oldacl, ACL *newacl, - const SID *usid, const SID *gsid, BOOL fordir ); -int ntfs_build_permissions( const char *securattr, - const SID *usid, const SID *gsid, BOOL isdir ); -char *ntfs_build_descr( mode_t mode, - int isdir, const SID * usid, const SID * gsid ); -struct MAPLIST *ntfs_read_mapping( FILEREADER reader, void *fileid ); -struct MAPPING *ntfs_do_user_mapping( struct MAPLIST *firstitem ); -struct MAPPING *ntfs_do_group_mapping( struct MAPLIST *firstitem ); -void ntfs_free_mapping( struct MAPPING *mapping[] ); +int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl, + const SID *usid, const SID *gsid, BOOL fordir); +int ntfs_build_permissions(const char *securattr, + const SID *usid, const SID *gsid, BOOL isdir); +char *ntfs_build_descr(mode_t mode, + int isdir, const SID * usid, const SID * gsid); +struct MAPLIST *ntfs_read_mapping(FILEREADER reader, void *fileid); +struct MAPPING *ntfs_do_user_mapping(struct MAPLIST *firstitem); +struct MAPPING *ntfs_do_group_mapping(struct MAPLIST *firstitem); +void ntfs_free_mapping(struct MAPPING *mapping[]); #endif /* ACLS_H */ diff --git a/source/libntfs/attrib.c b/source/libntfs/attrib.c index 40c70299..123c9a91 100644 --- a/source/libntfs/attrib.c +++ b/source/libntfs/attrib.c @@ -65,56 +65,54 @@ #include "misc.h" #include "efs.h" -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' ) - }; +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') }; -ntfschar TXF_DATA[] = { const_cpu_to_le16( '$' ), - const_cpu_to_le16( 'T' ), - const_cpu_to_le16( 'X' ), - const_cpu_to_le16( 'F' ), - const_cpu_to_le16( '_' ), - const_cpu_to_le16( 'D' ), - const_cpu_to_le16( 'A' ), - const_cpu_to_le16( 'T' ), - const_cpu_to_le16( 'A' ), - const_cpu_to_le16( '\0' ) - }; +ntfschar TXF_DATA[] = { const_cpu_to_le16('$'), + const_cpu_to_le16('T'), + const_cpu_to_le16('X'), + const_cpu_to_le16('F'), + const_cpu_to_le16('_'), + const_cpu_to_le16('D'), + const_cpu_to_le16('A'), + const_cpu_to_le16('T'), + const_cpu_to_le16('A'), + const_cpu_to_le16('\0') }; -static int NAttrFlag( ntfs_attr *na, FILE_ATTR_FLAGS flag ) +static int NAttrFlag(ntfs_attr *na, FILE_ATTR_FLAGS flag) { - if ( na->type == AT_DATA && na->name == AT_UNNAMED ) - return ( na->ni->flags & flag ); - return 0; + if (na->type == AT_DATA && na->name == AT_UNNAMED) + return (na->ni->flags & flag); + return 0; } -static void NAttrSetFlag( ntfs_attr *na, FILE_ATTR_FLAGS flag ) +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 ); + 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 ) +static void NAttrClearFlag(ntfs_attr *na, FILE_ATTR_FLAGS flag) { - if ( na->type == AT_DATA && na->name == AT_UNNAMED ) - na->ni->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); } \ +#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 ) +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 @@ -124,231 +122,213 @@ GenNAttrIno( Sparse, FILE_ATTR_SPARSE_FILE ) * * Returns: */ -s64 ntfs_get_attribute_value_length( const ATTR_RECORD *a ) +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 ); + 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: + * @vol: + * @a: + * @b: * * Description... * * Returns: */ -s64 ntfs_get_attribute_value( const ntfs_volume *vol, - const ATTR_RECORD *a, u8 *b ) +s64 ntfs_get_attribute_value(const ntfs_volume *vol, + const ATTR_RECORD *a, u8 *b) { - runlist *rl; - s64 total, r; - int i; + 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 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; - } + /* 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 ); - } + 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. */ + /* 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 ) - { + /* 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; - } + 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 ) - { + 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; - } + 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; + 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) + * @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 ) +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; + na->rl = NULL; + na->ni = ni; + na->type = type; + na->name = name; + if (name) + na->name_len = name_len; + else + na->name_len = 0; } /** @@ -366,48 +346,46 @@ static void __ntfs_attr_init( ntfs_attr *na, ntfs_inode *ni, * * 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 ) +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; + 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 ); - } + 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) + * @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 @@ -417,216 +395,202 @@ void ntfs_attr_init( ntfs_attr *na, const BOOL non_resident, * 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 *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_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 ); + 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; + } - 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; - 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; - 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; - a = ctx->attr; + 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. + * + * Also prevent compression on NTFS version < 3.0 + * or cluster size > 4K or compression is disabled + */ + a->flags &= ~ATTR_COMPRESSION_MASK; + if ((ni->flags & FILE_ATTR_COMPRESSED) + && (ni->vol->major_ver >= 3) + && NVolCompression(ni->vol) + && (ni->vol->cluster_size <= MAX_COMPRESSION_CLUSTER_SIZE)) + a->flags |= ATTR_IS_COMPRESSED; + } + + 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 ( !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. - * - * Also prevent compression on NTFS version < 3.0 - * or cluster size > 4K or compression is disabled - */ - a->flags &= ~ATTR_COMPRESSION_MASK; - if ( ( ni->flags & FILE_ATTR_COMPRESSED ) - && ( ni->vol->major_ver >= 3 ) - && NVolCompression( ni->vol ) - && ( ni->vol->cluster_size <= MAX_COMPRESSION_CLUSTER_SIZE ) ) - a->flags |= ATTR_IS_COMPRESSED; - } - - 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 ); + 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; + ntfs_log_leave("\n"); + return na; put_err_out: - ntfs_attr_put_search_ctx( ctx ); + ntfs_attr_put_search_ctx(ctx); err_out: - free( newname ); - free( na ); - na = NULL; - goto out; + free(newname); + free(na); + na = NULL; + goto out; } /** * ntfs_attr_close - free an ntfs attribute structure - * @na: ntfs attribute structure to free + * @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 ) +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 ); + 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 + * @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 ) +int ntfs_attr_map_runlist(ntfs_attr *na, VCN vcn) { - LCN lcn; - ntfs_attr_search_ctx *ctx; + 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 ); + 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; + 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; + 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; + /* 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; + /* 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 + * @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 @@ -636,120 +600,110 @@ int ntfs_attr_map_runlist( ntfs_attr *na, VCN vcn ) * * Return 0 on success and -1 on error with errno set to the error code. */ -int ntfs_attr_map_whole_runlist( ntfs_attr *na ) +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; + 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 ); + ntfs_log_enter("Entering for inode %llu, attr 0x%x.\n", + (unsigned long long)na->ni->mft_no, na->type); - /* avoid multiple full runlist mappings */ - if ( NAttrFullyMapped( na ) ) - { - ret = 0; - goto out; - } - ctx = ntfs_attr_get_search_ctx( na->ni, NULL ); - if ( !ctx ) - goto out; + /* avoid multiple full runlist mappings */ + if (NAttrFullyMapped(na)) { + ret = 0; + goto out; + } + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) + goto out; - /* Map all attribute extents one by one. */ - next_vcn = last_vcn = highest_vcn = 0; - a = NULL; - while ( 1 ) - { - runlist_element *rl; + /* 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; + 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; + if (ntfs_attr_lookup(na->type, na->name, na->name_len, + CASE_SENSITIVE, next_vcn, NULL, 0, ctx)) + break; - a = ctx->attr; + 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; - } + 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; - } + /* 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; + /* 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; - } + /* 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 ) - { - NAttrSetFullyMapped( na ); - ret = 0; - } -err_out: - ntfs_attr_put_search_ctx( ctx ); + /* 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) { + NAttrSetFullyMapped(na); + ret = 0; + } +err_out: + ntfs_attr_put_search_ctx(ctx); out: - ntfs_log_leave( "\n" ); - return ret; + 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 + * @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 @@ -760,48 +714,47 @@ out: * * Since lcns must be >= 0, we use negative return values with special meaning: * - * Return value Meaning / Description + * 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. + * -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 ntfs_attr_vcn_to_lcn(ntfs_attr *na, const VCN vcn) { - LCN lcn; - BOOL is_retry = FALSE; + LCN lcn; + BOOL is_retry = FALSE; - if ( !na || !NAttrNonResident( na ) || vcn < 0 ) - return ( LCN )LCN_EINVAL; + 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 ); + 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; + /* 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 + * @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. @@ -813,321 +766,288 @@ retry: * * 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. + * 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 *ntfs_attr_find_vcn(ntfs_attr *na, const VCN vcn) { - runlist_element *rl; - BOOL is_retry = FALSE; + runlist_element *rl; + BOOL is_retry = FALSE; - if ( !na || !NAttrNonResident( na ) || vcn < 0 ) - { - errno = EINVAL; - return NULL; - } + 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 ); + 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; + 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; + /* 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; } /** * ntfs_attr_pread_i - see description at ntfs_attr_pread() - */ -static s64 ntfs_attr_pread_i( ntfs_attr *na, const s64 pos, s64 count, void *b ) + */ +static s64 ntfs_attr_pread_i(ntfs_attr *na, const s64 pos, s64 count, void *b) { - s64 br, to_read, ofs, total, total2, max_read, max_init; - ntfs_volume *vol; - runlist_element *rl; - u16 efs_padding_length; + s64 br, to_read, ofs, total, total2, max_read, max_init; + ntfs_volume *vol; + runlist_element *rl; + u16 efs_padding_length; - /* Sanity checking arguments is done in ntfs_attr_pread(). */ + /* 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) + == ATTR_IS_COMPRESSED) + return ntfs_compressed_attr_pread(na, pos, count, b); + else { + /* compression mode not supported */ + errno = EOPNOTSUPP; + return -1; + } + } + /* + * Encrypted non-resident attributes are not supported. We return + * access denied, which is what Windows NT4 does, too. + * However, allow if mounted with efs_raw option + */ + vol = na->ni->vol; + if (!vol->efs_raw && NAttrEncrypted(na) && NAttrNonResident(na)) { + errno = EACCES; + return -1; + } + + if (!count) + return 0; + /* + * Truncate reads beyond end of attribute, + * but round to next 512 byte boundary for encrypted + * attributes with efs_raw mount option + */ + max_read = na->data_size; + max_init = na->initialized_size; + if (na->ni->vol->efs_raw + && (na->data_flags & ATTR_IS_ENCRYPTED) + && NAttrNonResident(na)) { + if (na->data_size != na->initialized_size) { + ntfs_log_error("uninitialized encrypted file not supported\n"); + errno = EINVAL; + return -1; + } + max_init = max_read = ((na->data_size + 511) & ~511) + 2; + } + if (pos + count > max_read) { + if (pos >= max_read) + return 0; + count = max_read - pos; + } + /* If it is a resident attribute, get the value from the mft record. */ + if (!NAttrNonResident(na)) { + ntfs_attr_search_ctx *ctx; + char *val; - if ( ( na->data_flags & ATTR_COMPRESSION_MASK ) && NAttrNonResident( na ) ) - { - if ( ( na->data_flags & ATTR_COMPRESSION_MASK ) - == ATTR_IS_COMPRESSED ) - return ntfs_compressed_attr_pread( na, pos, count, b ); - else - { - /* compression mode not supported */ - errno = EOPNOTSUPP; - return -1; - } - } - /* - * Encrypted non-resident attributes are not supported. We return - * access denied, which is what Windows NT4 does, too. - * However, allow if mounted with efs_raw option - */ - vol = na->ni->vol; - if ( !vol->efs_raw && NAttrEncrypted( na ) && NAttrNonResident( na ) ) - { - errno = EACCES; - return -1; - } - - if ( !count ) - return 0; - /* - * Truncate reads beyond end of attribute, - * but round to next 512 byte boundary for encrypted - * attributes with efs_raw mount option - */ - max_read = na->data_size; - max_init = na->initialized_size; - if ( na->ni->vol->efs_raw - && ( na->data_flags & ATTR_IS_ENCRYPTED ) - && NAttrNonResident( na ) ) - { - if ( na->data_size != na->initialized_size ) - { - ntfs_log_error( "uninitialized encrypted file not supported\n" ); - errno = EINVAL; - return -1; - } - max_init = max_read = ( ( na->data_size + 511 ) & ~511 ) + 2; - } - if ( pos + count > max_read ) - { - if ( pos >= max_read ) - return 0; - count = max_read - pos; - } - /* If it is a resident attribute, get the value from the mft record. */ - if ( !NAttrNonResident( na ) ) - { - ntfs_attr_search_ctx *ctx; - char *val; - - 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 ) ) - { + 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)) { res_err_out: - ntfs_attr_put_search_ctx( ctx ); - return -1; - } - 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 res_err_out; - } - memcpy( b, val + pos, count ); - ntfs_attr_put_search_ctx( ctx ); - return count; - } - total = total2 = 0; - /* Zero out reads beyond initialized size. */ - if ( pos + count > max_init ) - { - if ( pos >= max_init ) - { - memset( b, 0, count ); - return count; - } - total2 = pos + count - max_init; - count -= total2; - memset( ( u8* )b + count, 0, total2 ); - } - /* - * 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 - * restored - */ - if ( na->ni->vol->efs_raw && - ( na->data_flags & ATTR_IS_ENCRYPTED ) && - ( ( pos + count ) > max_init - 2 ) ) - { - efs_padding_length = 511 - ( ( na->data_size - 1 ) & 511 ); - if ( pos + count == max_init ) - { - if ( count == 1 ) - { - *( ( u8* )b + count - 1 ) = ( u8 )( efs_padding_length >> 8 ); - count--; - total2++; - } - else - { - *( u16* )( ( u8* )b + count - 2 ) = cpu_to_le16( efs_padding_length ); - count -= 2; - total2 += 2; - } - } - else - { - *( ( u8* )b + count - 1 ) = ( u8 )( efs_padding_length & 0xff ); - count--; - total2++; - } - } - - /* 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 read. - * However, we already truncated the read to the data_size, - * so getting this here is an error. - */ - if ( errno == ENOENT ) - { - errno = EIO; - ntfs_log_perror( "%s: Failed to find VCN #1", __FUNCTION__ ); - } - return -1; - } - /* - * Gather the requested data into the linear destination buffer. Note, - * a partial final vcn is taken care of by the @count capping of read - * length. - */ - ofs = pos - ( rl->vcn << vol->cluster_size_bits ); - for ( ; count; rl++, ofs = 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 #2", - __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 ) - { - if ( rl->lcn != ( LCN )LCN_HOLE ) - { - ntfs_log_perror( "%s: Bad run (%lld)", - __FUNCTION__, - ( long long )rl->lcn ); - goto rl_err_out; - } - /* It is a hole, just zero the matching @b range. */ - to_read = min( count, ( rl->length << - vol->cluster_size_bits ) - ofs ); - memset( b, 0, to_read ); - /* Update progress counters. */ - total += to_read; - count -= to_read; - b = ( u8* )b + to_read; - continue; - } - /* It is a real lcn, read it into @dst. */ - to_read = min( count, ( rl->length << vol->cluster_size_bits ) - - ofs ); + ntfs_attr_put_search_ctx(ctx); + return -1; + } + 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 res_err_out; + } + memcpy(b, val + pos, count); + ntfs_attr_put_search_ctx(ctx); + return count; + } + total = total2 = 0; + /* Zero out reads beyond initialized size. */ + if (pos + count > max_init) { + if (pos >= max_init) { + memset(b, 0, count); + return count; + } + total2 = pos + count - max_init; + count -= total2; + memset((u8*)b + count, 0, total2); + } + /* + * 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 + * restored + */ + if (na->ni->vol->efs_raw && + (na->data_flags & ATTR_IS_ENCRYPTED) && + ((pos + count) > max_init-2)) { + efs_padding_length = 511 - ((na->data_size - 1) & 511); + if (pos+count == max_init) { + if (count == 1) { + *((u8*)b+count-1) = (u8)(efs_padding_length >> 8); + count--; + total2++; + } else { + *(u16*)((u8*)b+count-2) = cpu_to_le16(efs_padding_length); + count -= 2; + total2 +=2; + } + } else { + *((u8*)b+count-1) = (u8)(efs_padding_length & 0xff); + count--; + total2++; + } + } + + /* 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 read. + * However, we already truncated the read to the data_size, + * so getting this here is an error. + */ + if (errno == ENOENT) { + errno = EIO; + ntfs_log_perror("%s: Failed to find VCN #1", __FUNCTION__); + } + return -1; + } + /* + * Gather the requested data into the linear destination buffer. Note, + * a partial final vcn is taken care of by the @count capping of read + * length. + */ + ofs = pos - (rl->vcn << vol->cluster_size_bits); + for (; count; rl++, ofs = 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 #2", + __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) { + if (rl->lcn != (LCN)LCN_HOLE) { + ntfs_log_perror("%s: Bad run (%lld)", + __FUNCTION__, + (long long)rl->lcn); + goto rl_err_out; + } + /* It is a hole, just zero the matching @b range. */ + to_read = min(count, (rl->length << + vol->cluster_size_bits) - ofs); + memset(b, 0, to_read); + /* Update progress counters. */ + total += to_read; + count -= to_read; + b = (u8*)b + to_read; + continue; + } + /* It is a real lcn, read it into @dst. */ + to_read = min(count, (rl->length << vol->cluster_size_bits) - + ofs); retry: - ntfs_log_trace( "Reading %lld bytes from vcn %lld, lcn %lld, ofs" - " %lld.\n", ( long long )to_read, ( long long )rl->vcn, - ( long long )rl->lcn, ( long long )ofs ); - br = ntfs_pread( vol->dev, ( rl->lcn << vol->cluster_size_bits ) + - ofs, to_read, b ); - /* If everything ok, update progress counters and continue. */ - if ( br > 0 ) - { - total += br; - count -= br; - b = ( u8* )b + br; - } - if ( br == to_read ) - continue; - /* If the syscall was interrupted, try again. */ - if ( br == ( s64 ) - 1 && errno == EINTR ) - goto retry; - if ( total ) - return total; - if ( !br ) - errno = EIO; - ntfs_log_perror( "%s: ntfs_pread failed", __FUNCTION__ ); - return -1; - } - /* Finally, return the number of bytes read. */ - return total + total2; + ntfs_log_trace("Reading %lld bytes from vcn %lld, lcn %lld, ofs" + " %lld.\n", (long long)to_read, (long long)rl->vcn, + (long long )rl->lcn, (long long)ofs); + br = ntfs_pread(vol->dev, (rl->lcn << vol->cluster_size_bits) + + ofs, to_read, b); + /* If everything ok, update progress counters and continue. */ + if (br > 0) { + total += br; + count -= br; + b = (u8*)b + br; + } + if (br == to_read) + continue; + /* If the syscall was interrupted, try again. */ + if (br == (s64)-1 && errno == EINTR) + goto retry; + if (total) + return total; + if (!br) + errno = EIO; + ntfs_log_perror("%s: ntfs_pread failed", __FUNCTION__); + return -1; + } + /* Finally, return the number of bytes read. */ + return total + total2; rl_err_out: - if ( total ) - return total; - errno = EIO; - return -1; + if (total) + return total; + errno = EIO; + return -1; } /** * ntfs_attr_pread - read from an attribute specified by an ntfs_attr structure - * @na: ntfs attribute to read from - * @pos: byte position in the attribute to begin reading from - * @count: number of bytes to read - * @b: output data buffer + * @na: ntfs attribute to read from + * @pos: byte position in the attribute to begin reading from + * @count: number of bytes to read + * @b: output data buffer * * This function will read @count bytes starting at offset @pos from the ntfs * attribute @na into the data buffer @b. @@ -1141,634 +1061,563 @@ rl_err_out: * to the return code of ntfs_pread(), or to EINVAL in case of invalid * arguments. */ -s64 ntfs_attr_pread( ntfs_attr *na, const s64 pos, s64 count, void *b ) +s64 ntfs_attr_pread(ntfs_attr *na, const s64 pos, s64 count, void *b) { - s64 ret; + s64 ret; + + if (!na || !na->ni || !na->ni->vol || !b || pos < 0 || count < 0) { + errno = EINVAL; + ntfs_log_perror("%s: na=%p b=%p pos=%lld count=%lld", + __FUNCTION__, na, b, (long long)pos, + (long long)count); + return -1; + } + + ntfs_log_enter("Entering for inode %lld attr 0x%x pos %lld count " + "%lld\n", (unsigned 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: na=%p b=%p pos=%lld count=%lld", - __FUNCTION__, na, b, ( long long )pos, - ( long long )count ); - return -1; - } - - ntfs_log_enter( "Entering for inode %lld attr 0x%x pos %lld count " - "%lld\n", ( unsigned long long )na->ni->mft_no, - na->type, ( long long )pos, ( long long )count ); - - ret = ntfs_attr_pread_i( na, pos, count, b ); - - ntfs_log_leave( "\n" ); - return ret; + ret = ntfs_attr_pread_i(na, pos, count, b); + + ntfs_log_leave("\n"); + return ret; } -static int ntfs_attr_fill_zero( ntfs_attr *na, s64 pos, s64 count ) +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; + 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 ); + 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; + return ret; } -static int ntfs_attr_fill_hole( ntfs_attr *na, s64 count, s64 *ofs, - runlist_element **rl, VCN *update_from ) +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; + 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 ); + 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 + * compression block, as we may need space to decompress + * existing compressed data. + * So allocate the space common to compression block + * and existing hole. + */ + VCN alloc_vcn; - 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 - * compression block, as we may need space to decompress - * existing compressed data. - * So allocate the space common to compression block - * and existing hole. - */ - VCN alloc_vcn; - - if ( ( from_vcn & -na->compression_block_clusters ) <= ( *rl )->vcn ) - alloc_vcn = ( *rl )->vcn; - else - alloc_vcn = from_vcn & -na->compression_block_clusters; - need = ( alloc_vcn | ( na->compression_block_clusters - 1 ) ) - + 1 - alloc_vcn; - if ( need > ( *rl )->length ) - { - ntfs_log_error( "Cannot allocate %lld clusters" - " within a hole of %lld\n", - ( long long )need, - ( long long )( *rl )->length ); - errno = EIO; - goto err_out; - } - rlc = ntfs_cluster_alloc( vol, alloc_vcn, need, - lcn_seek_from, DATA_ZONE ); - } - else - rlc = ntfs_cluster_alloc( vol, from_vcn, need, - lcn_seek_from, DATA_ZONE ); - if ( !rlc ) - goto err_out; - if ( na->data_flags & ( ATTR_COMPRESSION_MASK | ATTR_IS_SPARSE ) ) - na->compressed_size += need << vol->cluster_size_bits; - - *rl = ntfs_runlists_merge( na->rl, rlc ); - /* - * For a compressed attribute, we must be sure there are two - * available entries, so reserve them before it gets too late. - */ - if ( *rl && ( na->data_flags & ATTR_COMPRESSION_MASK ) ) - { - runlist_element *oldrl = na->rl; - na->rl = *rl; - *rl = ntfs_rl_extend( na, *rl, 2 ); - if ( !*rl ) na->rl = oldrl; /* restore to original if failed */ - } - if ( !*rl ) - { - eo = errno; - ntfs_log_perror( "Failed to merge runlists" ); - 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->unused_runs = 2; - na->rl = *rl; - if ( ( *update_from == -1 ) || ( from_vcn < *update_from ) ) - *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; + if ((from_vcn & -na->compression_block_clusters) <= (*rl)->vcn) + alloc_vcn = (*rl)->vcn; + else + alloc_vcn = from_vcn & -na->compression_block_clusters; + need = (alloc_vcn | (na->compression_block_clusters - 1)) + + 1 - alloc_vcn; + if (need > (*rl)->length) { + ntfs_log_error("Cannot allocate %lld clusters" + " within a hole of %lld\n", + (long long)need, + (long long)(*rl)->length); + errno = EIO; + goto err_out; + } + rlc = ntfs_cluster_alloc(vol, alloc_vcn, need, + lcn_seek_from, DATA_ZONE); + } else + rlc = ntfs_cluster_alloc(vol, from_vcn, need, + lcn_seek_from, DATA_ZONE); + if (!rlc) + goto err_out; + if (na->data_flags & (ATTR_COMPRESSION_MASK | ATTR_IS_SPARSE)) + na->compressed_size += need << vol->cluster_size_bits; + + *rl = ntfs_runlists_merge(na->rl, rlc); + /* + * For a compressed attribute, we must be sure there are two + * available entries, so reserve them before it gets too late. + */ + if (*rl && (na->data_flags & ATTR_COMPRESSION_MASK)) { + runlist_element *oldrl = na->rl; + na->rl = *rl; + *rl = ntfs_rl_extend(na,*rl,2); + if (!*rl) na->rl = oldrl; /* restore to original if failed */ + } + if (!*rl) { + eo = errno; + ntfs_log_perror("Failed to merge runlists"); + 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->unused_runs = 2; + na->rl = *rl; + if ((*update_from == -1) || (from_vcn < *update_from)) + *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; + return ret; } -static int stuff_hole( ntfs_attr *na, const s64 pos ); +static int stuff_hole(ntfs_attr *na, const s64 pos); /* - * Split an existing hole for overwriting with data - * The hole may have to be split into two or three parts, so - * that the overwritten part fits within a single compression block + * Split an existing hole for overwriting with data + * The hole may have to be split into two or three parts, so + * that the overwritten part fits within a single compression block * - * No cluster allocation is needed, this will be done later in - * standard hole filling, hence no need to reserve runs for - * future needs. + * No cluster allocation is needed, this will be done later in + * standard hole filling, hence no need to reserve runs for + * future needs. * - * Returns the number of clusters with existing compressed data - * in the compression block to be written to - * (or the full block, if it was a full hole) - * -1 if there were an error + * Returns the number of clusters with existing compressed data + * in the compression block to be written to + * (or the full block, if it was a full hole) + * -1 if there were an error */ -static int split_compressed_hole( ntfs_attr *na, runlist_element **prl, - s64 pos, s64 count, VCN *update_from ) +static int split_compressed_hole(ntfs_attr *na, runlist_element **prl, + s64 pos, s64 count, VCN *update_from) { - int compressed_part; - int cluster_size_bits = na->ni->vol->cluster_size_bits; - runlist_element *rl = *prl; + int compressed_part; + int cluster_size_bits = na->ni->vol->cluster_size_bits; + runlist_element *rl = *prl; - compressed_part - = na->compression_block_clusters; - /* reserve entries in runlist if we have to split */ - if ( rl->length > na->compression_block_clusters ) - { - *prl = ntfs_rl_extend( na, *prl, 2 ); - if ( !*prl ) - { - compressed_part = -1; - } - else - { - rl = *prl; - na->unused_runs = 2; - } - } - if ( *prl && ( rl->length > na->compression_block_clusters ) ) - { - /* - * Locate the update part relative to beginning of - * current run - */ - int beginwrite = ( pos >> cluster_size_bits ) - rl->vcn; - s32 endblock = ( ( ( pos + count - 1 ) >> cluster_size_bits ) - | ( na->compression_block_clusters - 1 ) ) + 1 - rl->vcn; + compressed_part + = na->compression_block_clusters; + /* reserve entries in runlist if we have to split */ + if (rl->length > na->compression_block_clusters) { + *prl = ntfs_rl_extend(na,*prl,2); + if (!*prl) { + compressed_part = -1; + } else { + rl = *prl; + na->unused_runs = 2; + } + } + if (*prl && (rl->length > na->compression_block_clusters)) { + /* + * Locate the update part relative to beginning of + * current run + */ + int beginwrite = (pos >> cluster_size_bits) - rl->vcn; + s32 endblock = (((pos + count - 1) >> cluster_size_bits) + | (na->compression_block_clusters - 1)) + 1 - rl->vcn; - compressed_part = na->compression_block_clusters - - ( rl->length & ( na->compression_block_clusters - 1 ) ); - if ( ( beginwrite + compressed_part ) >= na->compression_block_clusters ) - compressed_part = na->compression_block_clusters; - /* - * if the run ends beyond end of needed block - * we have to split the run - */ - if ( endblock < rl[0].length ) - { - runlist_element *xrl; - int n; + compressed_part = na->compression_block_clusters + - (rl->length & (na->compression_block_clusters - 1)); + if ((beginwrite + compressed_part) >= na->compression_block_clusters) + compressed_part = na->compression_block_clusters; + /* + * if the run ends beyond end of needed block + * we have to split the run + */ + if (endblock < rl[0].length) { + runlist_element *xrl; + int n; - /* - * we have to split into three parts if the run - * does not end within the first compression block. - * This means the hole begins before the - * compression block. - */ - if ( endblock > na->compression_block_clusters ) - { - if ( na->unused_runs < 2 ) - { - ntfs_log_error( "No free run, case 1\n" ); - } - na->unused_runs -= 2; - xrl = rl; - n = 0; - while ( xrl->length ) - { - xrl++; - n++; - } - do - { - xrl[2] = *xrl; - xrl--; - } - while ( xrl != rl ); - rl[1].length = na->compression_block_clusters; - rl[2].length = rl[0].length - endblock; - rl[0].length = endblock - - na->compression_block_clusters; - rl[1].lcn = LCN_HOLE; - rl[2].lcn = LCN_HOLE; - rl[1].vcn = rl[0].vcn + rl[0].length; - rl[2].vcn = rl[1].vcn - + na->compression_block_clusters; - rl = ++( *prl ); - } - else - { - /* - * split into two parts and use the - * first one - */ - if ( !na->unused_runs ) - { - ntfs_log_error( "No free run, case 2\n" ); - } - na->unused_runs--; - xrl = rl; - n = 0; - while ( xrl->length ) - { - xrl++; - n++; - } - do - { - xrl[1] = *xrl; - xrl--; - } - while ( xrl != rl ); - if ( beginwrite < endblock ) - { - /* we will write into the first part of hole */ - rl[1].length = rl[0].length - endblock; - rl[0].length = endblock; - rl[1].vcn = rl[0].vcn + rl[0].length; - rl[1].lcn = LCN_HOLE; - } - else - { - /* we will write into the second part of hole */ + /* + * we have to split into three parts if the run + * does not end within the first compression block. + * This means the hole begins before the + * compression block. + */ + if (endblock > na->compression_block_clusters) { + if (na->unused_runs < 2) { +ntfs_log_error("No free run, case 1\n"); + } + na->unused_runs -= 2; + xrl = rl; + n = 0; + while (xrl->length) { + xrl++; + n++; + } + do { + xrl[2] = *xrl; + xrl--; + } while (xrl != rl); + rl[1].length = na->compression_block_clusters; + rl[2].length = rl[0].length - endblock; + rl[0].length = endblock + - na->compression_block_clusters; + rl[1].lcn = LCN_HOLE; + rl[2].lcn = LCN_HOLE; + rl[1].vcn = rl[0].vcn + rl[0].length; + rl[2].vcn = rl[1].vcn + + na->compression_block_clusters; + rl = ++(*prl); + } else { + /* + * split into two parts and use the + * first one + */ + if (!na->unused_runs) { +ntfs_log_error("No free run, case 2\n"); + } + na->unused_runs--; + xrl = rl; + n = 0; + while (xrl->length) { + xrl++; + n++; + } + do { + xrl[1] = *xrl; + xrl--; + } while (xrl != rl); + if (beginwrite < endblock) { + /* we will write into the first part of hole */ + rl[1].length = rl[0].length - endblock; + rl[0].length = endblock; + rl[1].vcn = rl[0].vcn + rl[0].length; + rl[1].lcn = LCN_HOLE; + } else { + /* we will write into the second part of hole */ // impossible ? - rl[1].length = rl[0].length - endblock; - rl[0].length = endblock; - rl[1].vcn = rl[0].vcn + rl[0].length; - rl[1].lcn = LCN_HOLE; - rl = ++( *prl ); - } - } - } - else - { - if ( rl[1].length ) - { - runlist_element *xrl; - int n; + rl[1].length = rl[0].length - endblock; + rl[0].length = endblock; + rl[1].vcn = rl[0].vcn + rl[0].length; + rl[1].lcn = LCN_HOLE; + rl = ++(*prl); + } + } + } else { + if (rl[1].length) { + runlist_element *xrl; + int n; - /* - * split into two parts and use the - * last one - */ - if ( !na->unused_runs ) - { - ntfs_log_error( "No free run, case 4\n" ); - } - na->unused_runs--; - xrl = rl; - n = 0; - while ( xrl->length ) - { - xrl++; - n++; - } - do - { - xrl[1] = *xrl; - xrl--; - } - while ( xrl != rl ); - } - else - { - rl[2].lcn = rl[1].lcn; - rl[2].vcn = rl[1].vcn; - rl[2].length = rl[1].length; - } - rl[1].vcn -= na->compression_block_clusters; - rl[1].lcn = LCN_HOLE; - rl[1].length = na->compression_block_clusters; - rl[0].length -= na->compression_block_clusters; - if ( pos >= ( rl[1].vcn << cluster_size_bits ) ) - { - rl = ++( *prl ); - } - } - if ( ( *update_from == -1 ) || ( ( *prl )->vcn < *update_from ) ) - *update_from = ( *prl )->vcn; - } - return ( compressed_part ); + /* + * split into two parts and use the + * last one + */ + if (!na->unused_runs) { +ntfs_log_error("No free run, case 4\n"); + } + na->unused_runs--; + xrl = rl; + n = 0; + while (xrl->length) { + xrl++; + n++; + } + do { + xrl[1] = *xrl; + xrl--; + } while (xrl != rl); + } else { + rl[2].lcn = rl[1].lcn; + rl[2].vcn = rl[1].vcn; + rl[2].length = rl[1].length; + } + rl[1].vcn -= na->compression_block_clusters; + rl[1].lcn = LCN_HOLE; + rl[1].length = na->compression_block_clusters; + rl[0].length -= na->compression_block_clusters; + if (pos >= (rl[1].vcn << cluster_size_bits)) { + rl = ++(*prl); + } + } + if ((*update_from == -1) || ((*prl)->vcn < *update_from)) + *update_from = (*prl)->vcn; + } + return (compressed_part); } /* - * Borrow space from adjacent hole for appending data - * The hole may have to be split so that the end of hole is not - * affected by cluster allocation and overwriting - * Cluster allocation is needed for the overwritten compression block + * Borrow space from adjacent hole for appending data + * The hole may have to be split so that the end of hole is not + * affected by cluster allocation and overwriting + * Cluster allocation is needed for the overwritten compression block * - * Must always leave two unused entries in the runlist + * Must always leave two unused entries in the runlist * - * Returns the number of clusters with existing compressed data - * in the compression block to be written to - * -1 if there were an error + * Returns the number of clusters with existing compressed data + * in the compression block to be written to + * -1 if there were an error */ -static int borrow_from_hole( ntfs_attr *na, runlist_element **prl, - s64 pos, s64 count, VCN *update_from, BOOL wasnonresident ) +static int borrow_from_hole(ntfs_attr *na, runlist_element **prl, + s64 pos, s64 count, VCN *update_from, BOOL wasnonresident) { - int compressed_part = 0; - int cluster_size_bits = na->ni->vol->cluster_size_bits; - runlist_element *rl = *prl; - s32 endblock; - long long allocated; - runlist_element *zrl; - int irl; - BOOL undecided; - BOOL nothole; + int compressed_part = 0; + int cluster_size_bits = na->ni->vol->cluster_size_bits; + runlist_element *rl = *prl; + s32 endblock; + long long allocated; + runlist_element *zrl; + int irl; + BOOL undecided; + BOOL nothole; - /* check whether the compression block is fully allocated */ - endblock = ( ( ( pos + count - 1 ) >> cluster_size_bits ) | ( na->compression_block_clusters - 1 ) ) + 1 - rl->vcn; - allocated = 0; - zrl = rl; - irl = 0; - while ( zrl->length && ( zrl->lcn >= 0 ) && ( allocated < endblock ) ) - { - allocated += zrl->length; - zrl++; - irl++; - } + /* check whether the compression block is fully allocated */ + endblock = (((pos + count - 1) >> cluster_size_bits) | (na->compression_block_clusters - 1)) + 1 - rl->vcn; + allocated = 0; + zrl = rl; + irl = 0; + while (zrl->length && (zrl->lcn >= 0) && (allocated < endblock)) { + allocated += zrl->length; + zrl++; + irl++; + } - undecided = ( allocated < endblock ) && ( zrl->lcn == LCN_RL_NOT_MAPPED ); - nothole = ( allocated >= endblock ) || ( zrl->lcn != LCN_HOLE ); + undecided = (allocated < endblock) && (zrl->lcn == LCN_RL_NOT_MAPPED); + nothole = (allocated >= endblock) || (zrl->lcn != LCN_HOLE); - if ( undecided || nothole ) - { - runlist_element *orl = na->rl; - s64 olcn = ( *prl )->lcn; - /* - * Map the full runlist (needed to compute the - * compressed size), unless the runlist has not - * yet been created (data just made non-resident) - */ - irl = *prl - na->rl; - if ( !NAttrBeingNonResident( na ) - && ntfs_attr_map_whole_runlist( na ) ) - { - rl = ( runlist_element* )NULL; - } - else - { - /* - * Mapping the runlist may cause its relocation, - * and relocation may be at the same place with - * relocated contents. - * Have to find the current run again when this - * happens. - */ - if ( ( na->rl != orl ) || ( ( *prl )->lcn != olcn ) ) - { - zrl = &na->rl[irl]; - while ( zrl->length && ( zrl->lcn != olcn ) ) - zrl++; - *prl = zrl; - } - if ( !( *prl )->length ) - { - ntfs_log_error( "Mapped run not found," - " inode %lld lcn 0x%llx\n", - ( long long )na->ni->mft_no, - ( long long )olcn ); - rl = ( runlist_element* )NULL; - } - else - { - rl = ntfs_rl_extend( na, *prl, 2 ); - na->unused_runs = 2; - } - } - *prl = rl; - if ( rl && undecided ) - { - allocated = 0; - zrl = rl; - irl = 0; - while ( zrl->length && ( zrl->lcn >= 0 ) - && ( allocated < endblock ) ) - { - allocated += zrl->length; - zrl++; - irl++; - } - } - } - /* - * compression block not fully allocated and followed - * by a hole : we must allocate in the hole. - */ - if ( rl && ( allocated < endblock ) && ( zrl->lcn == LCN_HOLE ) ) - { - s64 xofs; + if (undecided || nothole) { + runlist_element *orl = na->rl; + s64 olcn = (*prl)->lcn; + /* + * Map the full runlist (needed to compute the + * compressed size), unless the runlist has not + * yet been created (data just made non-resident) + */ + irl = *prl - na->rl; + if (!NAttrBeingNonResident(na) + && ntfs_attr_map_whole_runlist(na)) { + rl = (runlist_element*)NULL; + } else { + /* + * Mapping the runlist may cause its relocation, + * and relocation may be at the same place with + * relocated contents. + * Have to find the current run again when this + * happens. + */ + if ((na->rl != orl) || ((*prl)->lcn != olcn)) { + zrl = &na->rl[irl]; + while (zrl->length && (zrl->lcn != olcn)) + zrl++; + *prl = zrl; + } + if (!(*prl)->length) { + ntfs_log_error("Mapped run not found," + " inode %lld lcn 0x%llx\n", + (long long)na->ni->mft_no, + (long long)olcn); + rl = (runlist_element*)NULL; + } else { + rl = ntfs_rl_extend(na,*prl,2); + na->unused_runs = 2; + } + } + *prl = rl; + if (rl && undecided) { + allocated = 0; + zrl = rl; + irl = 0; + while (zrl->length && (zrl->lcn >= 0) + && (allocated < endblock)) { + allocated += zrl->length; + zrl++; + irl++; + } + } + } + /* + * compression block not fully allocated and followed + * by a hole : we must allocate in the hole. + */ + if (rl && (allocated < endblock) && (zrl->lcn == LCN_HOLE)) { + s64 xofs; - /* - * split the hole if not fully needed - */ - if ( ( allocated + zrl->length ) > endblock ) - { - runlist_element *xrl; + /* + * split the hole if not fully needed + */ + if ((allocated + zrl->length) > endblock) { + runlist_element *xrl; - *prl = ntfs_rl_extend( na, *prl, 1 ); - if ( *prl ) - { - /* beware : rl was reallocated */ - rl = *prl; - zrl = &rl[irl]; - na->unused_runs = 0; - xrl = zrl; - while ( xrl->length ) xrl++; - do - { - xrl[1] = *xrl; - } - while ( xrl-- != zrl ); - zrl->length = endblock - allocated; - zrl[1].length -= zrl->length; - zrl[1].vcn = zrl->vcn + zrl->length; - } - } - if ( *prl ) - { - if ( wasnonresident ) - compressed_part = na->compression_block_clusters - - zrl->length; - xofs = 0; - if ( ntfs_attr_fill_hole( na, - zrl->length << cluster_size_bits, - &xofs, &zrl, update_from ) ) - compressed_part = -1; - else - { - /* go back to initial cluster, now reallocated */ - while ( zrl->vcn > ( pos >> cluster_size_bits ) ) - zrl--; - *prl = zrl; - } - } - } - if ( !*prl ) - { - ntfs_log_error( "No elements to borrow from a hole\n" ); - compressed_part = -1; - } - else if ( ( *update_from == -1 ) || ( ( *prl )->vcn < *update_from ) ) - *update_from = ( *prl )->vcn; - return ( compressed_part ); + *prl = ntfs_rl_extend(na,*prl,1); + if (*prl) { + /* beware : rl was reallocated */ + rl = *prl; + zrl = &rl[irl]; + na->unused_runs = 0; + xrl = zrl; + while (xrl->length) xrl++; + do { + xrl[1] = *xrl; + } while (xrl-- != zrl); + zrl->length = endblock - allocated; + zrl[1].length -= zrl->length; + zrl[1].vcn = zrl->vcn + zrl->length; + } + } + if (*prl) { + if (wasnonresident) + compressed_part = na->compression_block_clusters + - zrl->length; + xofs = 0; + if (ntfs_attr_fill_hole(na, + zrl->length << cluster_size_bits, + &xofs, &zrl, update_from)) + compressed_part = -1; + else { + /* go back to initial cluster, now reallocated */ + while (zrl->vcn > (pos >> cluster_size_bits)) + zrl--; + *prl = zrl; + } + } + } + if (!*prl) { + ntfs_log_error("No elements to borrow from a hole\n"); + compressed_part = -1; + } else + if ((*update_from == -1) || ((*prl)->vcn < *update_from)) + *update_from = (*prl)->vcn; + return (compressed_part); } /** * ntfs_attr_pwrite - positioned write to an ntfs attribute - * @na: ntfs attribute to write to - * @pos: position in the attribute to write to - * @count: number of bytes to write - * @b: data buffer to write to disk + * @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. @@ -1782,780 +1631,706 @@ static int borrow_from_hole( ntfs_attr *na, runlist_element **prl, * 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 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 wasnonresident = FALSE; - BOOL compressed; - BOOL updatemap; + 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 wasnonresident = FALSE; + BOOL compressed; + BOOL updatemap; - ntfs_log_enter( "Entering for inode %lld, attr 0x%x, pos 0x%llx, count " - "0x%llx.\n", ( long long )na->ni->mft_no, na->type, - ( long long )pos, ( long long )count ); + 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); + na->unused_runs = 0; /* prepare overflow checks */ + /* + * Encrypted attributes are only supported in raw mode. We return + * access denied, which is what Windows NT4 does, too. + * 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; + /* + * Compression is restricted to data streams and + * only ATTR_IS_COMPRESSED compression mode is supported. + */ + if (compressed + && ((na->type != AT_DATA) + || ((na->data_flags & ATTR_COMPRESSION_MASK) + != ATTR_IS_COMPRESSED))) { + 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 = (pos | (na->compression_block_size - 1)) + 1 - 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; - 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 ); - na->unused_runs = 0; /* prepare overflow checks */ - /* - * Encrypted attributes are only supported in raw mode. We return - * access denied, which is what Windows NT4 does, too. - * 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; - /* - * Compression is restricted to data streams and - * only ATTR_IS_COMPRESSED compression mode is supported. - */ - if ( compressed - && ( ( na->type != AT_DATA ) - || ( ( na->data_flags & ATTR_COMPRESSION_MASK ) - != ATTR_IS_COMPRESSED ) ) ) - { - errno = EOPNOTSUPP; - goto errno_set; - } + 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 ( !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 = ( pos | ( na->compression_block_size - 1 ) ) + 1 - 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, na->rl, 2 ); - if ( !na->rl ) - goto err_out; - na->unused_runs = 2; - } - /* Set initialized_size to @pos + @count. */ - ctx = ntfs_attr_get_search_ctx( na->ni, NULL ); - 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 ) - { - na->data_size = pos + count; - ctx->attr->data_size = ctx->attr->initialized_size; - } - if ( ntfs_mft_record_write( vol, ctx->ntfs_ino->mft_no, - ctx->mrec ) ) - { - /* - * 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; + 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,na->rl,2); + if (!na->rl) + goto err_out; + na->unused_runs = 2; + } + /* Set initialized_size to @pos + @count. */ + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + 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) { + na->data_size = pos + count; + ctx->attr->data_size = ctx->attr->initialized_size; + } + if (ntfs_mft_record_write(vol, ctx->ntfs_ino->mft_no, + ctx->mrec)) { + /* + * 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; #if CACHE_NIDATA_SIZE - if ( na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY - ? na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30 - : na->type == AT_DATA && na->name == AT_UNNAMED ) - { - na->ni->data_size = na->data_size; - if ( ( compressed || NAttrSparse( na ) ) - && NAttrNonResident( na ) ) - na->ni->allocated_size = na->compressed_size; - else - na->ni->allocated_size = na->allocated_size; - set_nino_flag( na->ni, KnownSize ); - } + if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY + ? na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30 + : na->type == AT_DATA && na->name == AT_UNNAMED) { + na->ni->data_size = na->data_size; + if ((compressed || NAttrSparse(na)) + && NAttrNonResident(na)) + na->ni->allocated_size = na->compressed_size; + else + na->ni->allocated_size = na->allocated_size; + set_nino_flag(na->ni,KnownSize); + } #endif - ntfs_attr_put_search_ctx( ctx ); - ctx = NULL; - /* - * 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; - } - /* - * 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 ) - /* - * the needed block is in a hole smaller - * than the compression block : we can use - * it fully - */ - compressed_part - = na->compression_block_clusters - - rl->length; - else - { - /* - * the needed block is in a hole bigger - * than the compression block : we must - * split the hole and use it partially - */ - compressed_part = split_compressed_hole( na, - &rl, pos, count, &update_from ); - } - } - else - { - if ( rl->lcn >= 0 ) - { - /* - * the needed block contains data, make - * sure the full compression block is - * allocated. Borrow from hole if needed - */ - compressed_part = borrow_from_hole( na, - &rl, pos, count, &update_from, - wasnonresident ); - } - } + 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; + } + /* + * 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) + /* + * the needed block is in a hole smaller + * than the compression block : we can use + * it fully + */ + compressed_part + = na->compression_block_clusters + - rl->length; + else { + /* + * the needed block is in a hole bigger + * than the compression block : we must + * split the hole and use it partially + */ + compressed_part = split_compressed_hole(na, + &rl, pos, count, &update_from); + } + } else { + if (rl->lcn >= 0) { + /* + * the needed block contains data, make + * sure the full compression block is + * allocated. Borrow from hole if needed + */ + compressed_part = borrow_from_hole(na, + &rl, pos, count, &update_from, + wasnonresident); + } + } - if ( compressed_part < 0 ) - goto err_out; + if (compressed_part < 0) + goto err_out; - /* just making non-resident, so not yet compressed */ - if ( NAttrBeingNonResident( na ) - && ( compressed_part < na->compression_block_clusters ) ) - compressed_part = 0; - } - ofs = pos - ( rl->vcn << vol->cluster_size_bits ); - /* - * Scatter the data from the linear data buffer to the volume. Note, a - * partial final vcn is taken care of by the @count capping of write - * length. - */ - for ( hole_end = 0; count; rl++, ofs = 0 ) - { - 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; + /* just making non-resident, so not yet compressed */ + if (NAttrBeingNonResident(na) + && (compressed_part < na->compression_block_clusters)) + compressed_part = 0; + } + ofs = pos - (rl->vcn << vol->cluster_size_bits); + /* + * Scatter the data from the linear data buffer to the volume. Note, a + * partial final vcn is taken care of by the @count capping of write + * length. + */ + for (hole_end = 0; count; rl++, ofs = 0) { + 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++; - } - } + 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 ); + /* 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, cb, compressed_part, - &update_from ); - } - 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, - &update_from ); - } - 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; - } + 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, cb, compressed_part, + &update_from); + } 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, + &update_from); + } 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. - * For a compressed file, we try to make a partial update - * of the mapping list. This makes a difference only if - * inode extents were needed. - */ - updatemap = ( compressed - ? NAttrFullyMapped( na ) != 0 : update_from != -1 ); - if ( updatemap ) - if ( ntfs_attr_update_mapping_pairs( na, - ( update_from < 0 ? 0 : update_from ) ) ) - { - /* - * FIXME: trying to recover by goto rl_err_out; - * could cause driver hang by infinite looping. - */ - total = -1; - goto out; - } -out: - ntfs_log_leave( "\n" ); - return total; + if (ctx) + ntfs_attr_put_search_ctx(ctx); + /* + * Update mapping pairs if needed. + * For a compressed file, we try to make a partial update + * of the mapping list. This makes a difference only if + * inode extents were needed. + */ + updatemap = (compressed + ? NAttrFullyMapped(na) != 0 : update_from != -1); + if (updatemap) + if (ntfs_attr_update_mapping_pairs(na, + (update_from < 0 ? 0 : update_from))) { + /* + * FIXME: trying to recover by goto rl_err_out; + * could cause driver hang by infinite looping. + */ + 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; + 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; + 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. */ - updatemap = ( compressed - ? NAttrFullyMapped( na ) != 0 : update_from != -1 ); - if ( updatemap ) - ntfs_attr_update_mapping_pairs( na, 0 ); - /* Restore original data_size if needed. */ - if ( need_to.undo_data_size && ntfs_attr_truncate( na, old_data_size ) ) - ntfs_log_perror( "Failed to restore data_size" ); - errno = eo; + 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. */ + updatemap = (compressed + ? NAttrFullyMapped(na) != 0 : update_from != -1); + if (updatemap) + ntfs_attr_update_mapping_pairs(na, 0); + /* Restore original data_size if needed. */ + if (need_to.undo_data_size && ntfs_attr_truncate(na, old_data_size)) + ntfs_log_perror("Failed to restore data_size"); + errno = eo; errno_set: - total = -1; - goto out; + total = -1; + goto out; } -int ntfs_attr_pclose( ntfs_attr *na ) +int ntfs_attr_pclose(ntfs_attr *na) { - s64 ofs; - int failed; - 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; + s64 ofs; + int failed; + 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 ); + 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; + na->unused_runs = 0; + 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; - if ( !na || !na->ni || !na->ni->vol ) - { - errno = EINVAL; - ntfs_log_perror( "%s", __FUNCTION__ ); - goto errno_set; - } - vol = na->ni->vol; - na->unused_runs = 0; - 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; + /* safety check : no recursion on close */ + if (NAttrComprClosing(na)) { + errno = EIO; + ntfs_log_error("Bad ntfs_attr_pclose" + " recursion on inode %lld\n", + (long long)na->ni->mft_no); + goto out; + } + NAttrSetComprClosing(na); + /* + * For a compressed attribute, we must be sure there are two + * available entries, so reserve them before it gets too late. + */ + if (ntfs_attr_map_whole_runlist(na)) + goto err_out; + na->rl = ntfs_rl_extend(na,na->rl,2); + if (!na->rl) + goto err_out; + na->unused_runs = 2; + /* Find the runlist element containing the terminal vcn. */ + rl = ntfs_attr_find_vcn(na, (na->initialized_size - 1) >> vol->cluster_size_bits); + if (!rl) { + /* + * 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) { + runlist_element *xrl; - /* safety check : no recursion on close */ - if ( NAttrComprClosing( na ) ) - { - errno = EIO; - ntfs_log_error( "Bad ntfs_attr_pclose" - " recursion on inode %lld\n", - ( long long )na->ni->mft_no ); - goto out; - } - NAttrSetComprClosing( na ); - /* - * For a compressed attribute, we must be sure there are two - * available entries, so reserve them before it gets too late. - */ - if ( ntfs_attr_map_whole_runlist( na ) ) - goto err_out; - na->rl = ntfs_rl_extend( na, na->rl, 2 ); - if ( !na->rl ) - goto err_out; - na->unused_runs = 2; - /* Find the runlist element containing the terminal vcn. */ - rl = ntfs_attr_find_vcn( na, ( na->initialized_size - 1 ) >> vol->cluster_size_bits ); - if ( !rl ) - { - /* - * 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 ) - { - runlist_element *xrl; + xrl = rl; + do { + xrl++; + } while (xrl->lcn >= 0); + compressed_part = (-xrl->length) + & (na->compression_block_clusters - 1); + } else + if (rl->lcn == (LCN)LCN_HOLE) { + if (rl->length < na->compression_block_clusters) + compressed_part + = 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; - xrl = rl; - do - { - xrl++; - } - while ( xrl->lcn >= 0 ); - compressed_part = ( -xrl->length ) - & ( na->compression_block_clusters - 1 ); - } - else if ( rl->lcn == ( LCN )LCN_HOLE ) - { - if ( rl->length < na->compression_block_clusters ) - compressed_part - = 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); - 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++; - } + 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: - failed = 0; - if ( update_from < 0 ) update_from = 0; - if ( !NVolReadOnly( vol ) ) - { - failed = ntfs_compressed_close( na, rl, ofs, &update_from ); + failed = 0; + if (update_from < 0) update_from = 0; + if (!NVolReadOnly(vol)) { + failed = ntfs_compressed_close(na, rl, ofs, &update_from); #if CACHE_NIDATA_SIZE - if ( na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY - ? na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30 - : na->type == AT_DATA && na->name == AT_UNNAMED ) - { - na->ni->data_size = na->data_size; - na->ni->allocated_size = na->compressed_size; - set_nino_flag( na->ni, KnownSize ); - } + if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY + ? na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30 + : na->type == AT_DATA && na->name == AT_UNNAMED) { + na->ni->data_size = na->data_size; + na->ni->allocated_size = na->compressed_size; + set_nino_flag(na->ni,KnownSize); + } #endif - } - if ( failed ) - { - /* If the syscall was interrupted, try again. */ - if ( errno == EINTR ) - goto retry; - else - goto rl_err_out; - } - if ( ctx ) - ntfs_attr_put_search_ctx( ctx ); - /* Update mapping pairs if needed. */ - if ( NAttrFullyMapped( na ) ) - if ( ntfs_attr_update_mapping_pairs( na, update_from ) ) - { - /* - * FIXME: trying to recover by goto rl_err_out; - * could cause driver hang by infinite looping. - */ - ok = FALSE; - goto out; - } -out: - ntfs_log_leave( "\n" ); - return ( !ok ); + } + if (failed) { + /* If the syscall was interrupted, try again. */ + if (errno == EINTR) + goto retry; + else + goto rl_err_out; + } + if (ctx) + ntfs_attr_put_search_ctx(ctx); + /* Update mapping pairs if needed. */ + if (NAttrFullyMapped(na)) + if (ntfs_attr_update_mapping_pairs(na, update_from)) { + /* + * FIXME: trying to recover by goto rl_err_out; + * could cause driver hang by infinite looping. + */ + 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. - */ + /* + * 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. */ - if ( NAttrFullyMapped( na ) ) - ntfs_attr_update_mapping_pairs( na, 0 ); - errno = eo; + eo = errno; + if (ctx) + ntfs_attr_put_search_ctx(ctx); + /* Update mapping pairs if needed. */ + if (NAttrFullyMapped(na)) + ntfs_attr_update_mapping_pairs(na, 0); + errno = eo; errno_set: - ok = FALSE; - goto out; + 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 + * @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. @@ -2578,39 +2353,38 @@ errno_set: * 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 ntfs_attr_mst_pread(ntfs_attr *na, const s64 pos, const s64 bk_cnt, + const u32 bk_size, void *dst) { - s64 br; - u8 *end; + 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_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 + * @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 @@ -2634,64 +2408,60 @@ s64 ntfs_attr_mst_pread( ntfs_attr *na, const s64 pos, const s64 bk_cnt, * 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 ntfs_attr_mst_pwrite(ntfs_attr *na, const s64 pos, s64 bk_cnt, + const u32 bk_size, void *src) { - s64 written, i; + 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; + 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; + 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 + * @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. * @@ -2751,187 +2521,165 @@ s64 ntfs_attr_mst_pwrite( ntfs_attr *na, const s64 pos, s64 bk_cnt, * 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! + * 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 ) +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; + ATTR_RECORD *a; + ntfs_volume *vol; + ntfschar *upcase; + u32 upcase_len; - ntfs_log_trace( "attribute type 0x%x.\n", type ); + 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 - { - register int rc; - if ( name && ( ( rc = ntfs_names_full_collate( name, - name_len, ( ntfschar* )( ( char* )a + - le16_to_cpu( a->name_offset ) ), - a->name_length, ic, - upcase, upcase_len ) ) ) ) - { - /* - * If @name collates before a->name, - * there is no matching attribute. - */ - if ( rc < 0 ) - { - errno = ENOENT; - return -1; - } - /* If the strings are not equal, continue search. */ - 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; + 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 { + register int rc; + if (name && ((rc = ntfs_names_full_collate(name, + name_len, (ntfschar*)((char*)a + + le16_to_cpu(a->name_offset)), + a->name_length, ic, + upcase, upcase_len)))) { + /* + * If @name collates before a->name, + * there is no matching attribute. + */ + if (rc < 0) { + errno = ENOENT; + return -1; + } + /* If the strings are not equal, continue search. */ + 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; + 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 ) +void ntfs_attr_name_free(char **name) { - if ( *name ) - { - free( *name ); - *name = NULL; - } + if (*name) { + free(*name); + *name = NULL; + } } -char *ntfs_attr_name_get( const ntfschar *uname, const int uname_len ) +char *ntfs_attr_name_get(const ntfschar *uname, const int uname_len) { - char *name = NULL; - int name_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; + 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; + } else if (name_len > 0) + return name; - ntfs_attr_name_free( &name ); - return NULL; + 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 + * @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. @@ -2987,370 +2735,345 @@ char *ntfs_attr_name_get( const ntfschar *uname, const int uname_len ) * @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. + * 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 ) +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; + 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; + 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; - } + /* 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 ) ); + /* 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 ); + /* 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; + /* + * 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; + /* Got it. Done. */ + if (!rc) + return 0; - /* Error! If other than not found return it. */ - if ( errno != ENOENT ) - return rc; + /* 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 - { - int 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 { + int rc; - if ( name && ( ( rc = ntfs_names_full_collate( name, - name_len, al_name, al_name_len, ic, - vol->upcase, vol->upcase_len ) ) ) ) - { + if (name && ((rc = ntfs_names_full_collate(name, + name_len, al_name, al_name_len, ic, + vol->upcase, vol->upcase_len)))) { - /* - * If @name collates before al_name, - * there is no matching attribute. - */ - if ( rc < 0 ) - goto not_found; - /* If the strings are not equal, continue search. */ - continue; - } - } - /* - * The names match or @name not present and attribute is - * 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; + /* + * If @name collates before al_name, + * there is no matching attribute. + */ + if (rc < 0) + goto not_found; + /* If the strings are not equal, continue search. */ + continue; + } + } + /* + * The names match or @name not present and attribute is + * 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. - */ + 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; - } + 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; + /* 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; + /* + * 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; - } + 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 + * @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 @@ -3403,49 +3126,48 @@ not_found: * * * 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. + * 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 ) +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_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 ); + 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_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 + * @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. @@ -3454,83 +3176,80 @@ out: * 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. + * 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 ) +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; + 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 + * @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 ) +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; + 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 + * @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 ) +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; + 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 + * @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. @@ -3541,38 +3260,37 @@ void ntfs_attr_reinit_search_ctx( ntfs_attr_search_ctx *ctx ) * 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 *ntfs_attr_get_search_ctx(ntfs_inode *ni, MFT_RECORD *mrec) { - ntfs_attr_search_ctx *ctx; + 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; + 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 + * @ctx: attribute search context to free * * Release the attribute search context @ctx. */ -void ntfs_attr_put_search_ctx( ntfs_attr_search_ctx *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 ); + // 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 + * @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. @@ -3580,102 +3298,97 @@ void ntfs_attr_put_search_ctx( ntfs_attr_search_ctx *ctx ) * 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). + * 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 *ntfs_attr_find_in_attrdef(const ntfs_volume *vol, + const ATTR_TYPES type) { - ATTR_DEF *ad; + 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; + 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 + * @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). + * 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 ) +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; + 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; - } + 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; - } + /* + * $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; + 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 to check - * @name: attribute name to check - * @name_len: attribute name length + * @vol: ntfs volume to which the attribute belongs + * @type: attribute type to check + * @name: attribute name to check + * @name_len: attribute name length * * Check whether the attribute of @type and @name with name length @name_len on * the ntfs volume @vol is allowed to be non-resident. This information is @@ -3685,51 +3398,49 @@ int ntfs_attr_size_bounds_check( const ntfs_volume *vol, const ATTR_TYPES type, * 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). + * 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). */ -static int ntfs_attr_can_be_non_resident( const ntfs_volume *vol, const ATTR_TYPES type, - const ntfschar *name, int name_len ) +static int ntfs_attr_can_be_non_resident(const ntfs_volume *vol, const ATTR_TYPES type, + const ntfschar *name, int name_len) { - ATTR_DEF *ad; - BOOL allowed; + ATTR_DEF *ad; + BOOL allowed; - /* - * Microsoft has decreed that $LOGGED_UTILITY_STREAM attributes with a - * name of $TXF_DATA must be resident despite the entry for - * $LOGGED_UTILITY_STREAM in $AttrDef allowing them to be non-resident. - * Failure to obey this on the root directory mft record of a volume - * causes Windows Vista and later to see the volume as a RAW volume and - * thus cannot mount it at all. - */ - if ( ( type == AT_LOGGED_UTILITY_STREAM ) - && name - && ntfs_names_are_equal( TXF_DATA, 9, name, name_len, - CASE_SENSITIVE, vol->upcase, vol->upcase_len ) ) - allowed = FALSE; - else - { - /* 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. */ - allowed = !( ad->flags & ATTR_DEF_RESIDENT ); - } - if ( !allowed ) - { - errno = EPERM; - ntfs_log_trace( "Attribute can't be non-resident\n" ); - return -1; - } - return 0; + /* + * Microsoft has decreed that $LOGGED_UTILITY_STREAM attributes with a + * name of $TXF_DATA must be resident despite the entry for + * $LOGGED_UTILITY_STREAM in $AttrDef allowing them to be non-resident. + * Failure to obey this on the root directory mft record of a volume + * causes Windows Vista and later to see the volume as a RAW volume and + * thus cannot mount it at all. + */ + if ((type == AT_LOGGED_UTILITY_STREAM) + && name + && ntfs_names_are_equal(TXF_DATA, 9, name, name_len, + CASE_SENSITIVE, vol->upcase, vol->upcase_len)) + allowed = FALSE; + else { + /* 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. */ + allowed = !(ad->flags & ATTR_DEF_RESIDENT); + } + if (!allowed) { + 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 + * @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 @@ -3740,530 +3451,496 @@ static int ntfs_attr_can_be_non_resident( const ntfs_volume *vol, const ATTR_TYP * 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). + * 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. + * 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 ) +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; + 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 + * @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. + * 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 ) +int ntfs_make_room_for_attr(MFT_RECORD *m, u8 *pos, u32 size) { - u32 biu; + u32 biu; - ntfs_log_trace( "Entering for pos 0x%d, size %u.\n", - ( int )( pos - ( u8* )m ), ( unsigned ) size ); + 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; + /* 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; + /* 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; + 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 + * @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. + * 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 ) +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_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 ); + 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 (!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; - } + 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; + /* 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; - } + /* 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 ( ni->mrec->flags & MFT_RECORD_IS_DIRECTORY - ? type == AT_INDEX_ROOT && name == NTFS_INDEX_I30 - : type == AT_DATA && name == AT_UNNAMED ) - { - ni->data_size = size; - ni->allocated_size = ( size + 7 ) & ~7; - set_nino_flag( ni, KnownSize ); - } - ntfs_inode_mark_dirty( ni ); - ntfs_attr_put_search_ctx( ctx ); - return offset; + /* 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 (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY + ? type == AT_INDEX_ROOT && name == NTFS_INDEX_I30 + : type == AT_DATA && name == AT_UNNAMED) { + ni->data_size = size; + ni->allocated_size = (size + 7) & ~7; + set_nino_flag(ni,KnownSize); + } + ntfs_inode_mark_dirty(ni); + ntfs_attr_put_search_ctx(ctx); + return offset; put_err_out: - ntfs_attr_put_search_ctx( ctx ); - errno = err; - return -1; + 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 + * @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. + * 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 ) +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_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 ); + 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 (!ni || dataruns_size <= 0 || (!name && name_len)) { + errno = EINVAL; + return -1; + } - if ( ntfs_attr_can_be_non_resident( ni->vol, type, name, name_len ) ) - { - 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; - } + if (ntfs_attr_can_be_non_resident(ni->vol, type, name, name_len)) { + 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; + /* 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; - } + /* 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; + /* 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; + } + 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_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 + * @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. + * EINVAL - Invalid arguments passed to function. + * EIO - I/O error occurred or damaged filesystem. */ -int ntfs_attr_record_rm( ntfs_attr_search_ctx *ctx ) +int ntfs_attr_record_rm(ntfs_attr_search_ctx *ctx) { - ntfs_inode *base_ni, *ni; - ATTR_TYPES type; + ntfs_inode *base_ni, *ni; + ATTR_TYPES type; - if ( !ctx || !ctx->ntfs_ino || !ctx->mrec || !ctx->attr ) - { - errno = EINVAL; - return -1; - } + 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; + 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" ); - errno = EIO; - return -1; - } - ntfs_inode_mark_dirty( ni ); + /* 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"); + errno = 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; - } - } + /* + * 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 ); - } + /* 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; - } + /* 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; + 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; + /* 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; + 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) + * @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 @@ -4282,522 +3959,484 @@ int ntfs_attr_record_rm( ntfs_attr_search_ctx *ctx ) * * 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 ) +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; + 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; - } + 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 ); + 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; + 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; - } + /* 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, name, name_len ) ) - { - 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; + /* Sanity checks for always resident attributes. */ + if (ntfs_attr_can_be_non_resident(ni->vol, type, name, name_len)) { + 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; + /* + * 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; - } + /* + * 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; - } + /* 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; - } + /* 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 ) - && ( ni->vol->major_ver >= 3 ) - && NVolCompression( ni->vol ) - && ( ni->vol->cluster_size <= MAX_COMPRESSION_CLUSTER_SIZE ) - && ( ( type == AT_DATA ) - || ( ( type == AT_INDEX_ROOT ) && ( name == NTFS_INDEX_I30 ) ) ) ) - data_flags = ATTR_IS_COMPRESSED; - 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; - } + if ((ni->flags & FILE_ATTR_COMPRESSED) + && (ni->vol->major_ver >= 3) + && NVolCompression(ni->vol) + && (ni->vol->cluster_size <= MAX_COMPRESSION_CLUSTER_SIZE) + && ((type == AT_DATA) + || ((type == AT_INDEX_ROOT) && (name == NTFS_INDEX_I30)))) + data_flags = ATTR_IS_COMPRESSED; + 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; - } + /* 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; + /* 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; + /* 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" ); + /* 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" ); + /* 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; + errno = err; + return -1; } /* - * Change an attribute flag + * 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 ) +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; + 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 ); + 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 + * @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 ) +int ntfs_attr_rm(ntfs_attr *na) { - ntfs_attr_search_ctx *ctx; - int ret = 0; + ntfs_attr_search_ctx *ctx; + int ret = 0; - if ( !na ) - { - ntfs_log_trace( "Invalid arguments passed.\n" ); - errno = EINVAL; - return -1; - } + 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 ); + 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; - } - } + /* 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; - } + /* 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; + 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 + * @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. + * 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! + * are interested in the data may be overwritten! */ -int ntfs_attr_record_resize( MFT_RECORD *m, ATTR_RECORD *a, u32 new_size ) +int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size) { - u32 old_size, alloc_size, attr_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); - old_size = le32_to_cpu( m->bytes_in_use ); - alloc_size = le32_to_cpu( m->bytes_allocated ); - attr_size = le32_to_cpu( a->length ); + /* 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; + } - 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; + 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 + * @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. + * 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 ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a, + const u32 new_size) { - int ret; + int ret; + + ntfs_log_trace("Entering for new size %u.\n", (unsigned)new_size); - 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; + /* 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 + * @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 ) +int ntfs_attr_record_move_to(ntfs_attr_search_ctx *ctx, ntfs_inode *ni) { - ntfs_attr_search_ctx *nctx; - ATTR_RECORD *a; - int err; + 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; - } + 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 ); + 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->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; - } + 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; + /* 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; - } + /* + * 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 ); + /* 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 ); + /* 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; + ntfs_attr_put_search_ctx(nctx); + return 0; put_err_out: - ntfs_attr_put_search_ctx( nctx ); - errno = err; - return -1; + 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 + * @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. @@ -4807,925 +4446,863 @@ put_err_out: * * 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 ) +int ntfs_attr_record_move_away(ntfs_attr_search_ctx *ctx, int extra) { - ntfs_inode *base_ni, *ni; - MFT_RECORD *m; - int i; + 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; - } + 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 ); + 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 (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 (!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; - } + 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; + /* 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 (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; + 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; - } + /* + * 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; + /* + * 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 + * @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... + * 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. + * 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. + * 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 ) +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; + 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 ); + 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; - } + /* 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, na->name, na->name_len ) ) - return -1; + /* Check that the attribute is allowed to be non-resident. */ + if (ntfs_attr_can_be_non_resident(vol, na->type, na->name, na->name_len)) + return -1; - new_allocated_size = ( le32_to_cpu( a->value_length ) + vol->cluster_size - - 1 ) & ~( vol->cluster_size - 1 ); + new_allocated_size = (le32_to_cpu(a->value_length) + vol->cluster_size + - 1) & ~(vol->cluster_size - 1); - if ( new_allocated_size > 0 ) - { - if ( ( a->flags & ATTR_COMPRESSION_MASK ) - == ATTR_IS_COMPRESSED ) - { - /* must allocate full compression blocks */ - new_allocated_size = ( ( new_allocated_size - 1 ) - | ( ( 1L << ( STANDARD_COMPRESSION_UNIT - + vol->cluster_size_bits ) ) - 1 ) ) + 1; - } - /* Start by allocating clusters to hold the attribute value. */ - rl = ntfs_cluster_alloc( vol, 0, new_allocated_size >> - vol->cluster_size_bits, -1, DATA_ZONE ); - 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 ); - NAttrSetBeingNonResident( 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 (new_allocated_size > 0) { + if ((a->flags & ATTR_COMPRESSION_MASK) + == ATTR_IS_COMPRESSED) { + /* must allocate full compression blocks */ + new_allocated_size = ((new_allocated_size - 1) + | ((1L << (STANDARD_COMPRESSION_UNIT + + vol->cluster_size_bits)) - 1)) + 1; + } + /* Start by allocating clusters to hold the attribute value. */ + rl = ntfs_cluster_alloc(vol, 0, new_allocated_size >> + vol->cluster_size_bits, -1, DATA_ZONE); + 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); + NAttrSetBeingNonResident(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; + 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; - } + /* 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; + /* + * 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 ); + /* 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 ); + /* 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 ); + 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; - } + /* + * 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 ) ); + 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 ); + 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; - } + /* 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; + /* 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; + 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 ); +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 + * @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. * Can also be used to force an attribute non-resident. In this case, the * size cannot be changed. * - * On success return 0 + * On success return 0 * On error return values are: - * STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT - * STATUS_ERROR - otherwise + * 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. + * 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, - BOOL force_non_resident ) +static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize, + BOOL force_non_resident) { - ntfs_attr_search_ctx *ctx; - ntfs_volume *vol; - ntfs_inode *ni; - int err, ret = STATUS_ERROR; + 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 ); + 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 ) && !force_non_resident ) - { - /* 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->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY - ? na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30 - : na->type == AT_DATA && na->name == AT_UNNAMED ) - { - na->ni->data_size = na->data_size; - if ( ( ( na->data_flags & ATTR_COMPRESSION_MASK ) - || NAttrSparse( na ) ) - && NAttrNonResident( na ) ) - na->ni->allocated_size - = na->compressed_size; - else - na->ni->allocated_size - = na->allocated_size; - set_nino_flag( na->ni, KnownSize ); - if ( na->type == AT_DATA ) - NInoFileNameSetDirty( na->ni ); - } - goto resize_done; - } - /* 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. */ + /* 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) && !force_non_resident) { + /* 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->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY + ? na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30 + : na->type == AT_DATA && na->name == AT_UNNAMED) { + na->ni->data_size = na->data_size; + if (((na->data_flags & ATTR_COMPRESSION_MASK) + || NAttrSparse(na)) + && NAttrNonResident(na)) + na->ni->allocated_size + = na->compressed_size; + else + na->ni->allocated_size + = na->allocated_size; + set_nino_flag(na->ni,KnownSize); + if (na->type == AT_DATA) + NInoFileNameSetDirty(na->ni); + } + goto resize_done; + } + /* 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 ); - /* - * do not truncate when forcing non-resident, this - * could cause the attribute to be made resident again, - * so size changes are not allowed. - */ - if ( force_non_resident ) - { - ret = 0; - if ( newsize != na->data_size ) - { - ntfs_log_error( "Cannot change size when" - " forcing non-resident\n" ); - errno = EIO; - ret = STATUS_ERROR; - } - return ( ret ); - } - /* Resize non-resident attribute */ - return ntfs_attr_truncate( na, newsize ); - } - else if ( errno != ENOSPC && errno != EPERM ) - { - err = errno; - ntfs_log_perror( "Failed to make attribute non-resident" ); - goto put_err_out; - } + /* 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); + /* + * do not truncate when forcing non-resident, this + * could cause the attribute to be made resident again, + * so size changes are not allowed. + */ + if (force_non_resident) { + ret = 0; + if (newsize != na->data_size) { + ntfs_log_error("Cannot change size when" + " forcing non-resident\n"); + errno = EIO; + ret = STATUS_ERROR; + } + return (ret); + } + /* Resize non-resident attribute */ + return ntfs_attr_truncate(na, newsize); + } else if (errno != ENOSPC && errno != EPERM) { + 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; + /* 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; + 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; + /* + * 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; - } - if ( ( ( tna->data_flags & ATTR_COMPRESSION_MASK ) - == ATTR_IS_COMPRESSED ) - && ntfs_attr_pclose( tna ) ) - { - err = errno; - ntfs_attr_close( tna ); - goto put_err_out; - } - ntfs_inode_mark_dirty( tna->ni ); - ntfs_attr_close( tna ); - ntfs_attr_put_search_ctx( ctx ); - return ntfs_resident_attr_resize_i( na, newsize, force_non_resident ); - } - /* Check whether error occurred. */ - if ( errno != ENOENT ) - { - err = errno; - ntfs_log_perror( "%s: Attribute lookup failed 1", __FUNCTION__ ); - goto put_err_out; - } + 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; + } + if (((tna->data_flags & ATTR_COMPRESSION_MASK) + == ATTR_IS_COMPRESSED) + && ntfs_attr_pclose(tna)) { + err = errno; + ntfs_attr_close(tna); + goto put_err_out; + } + ntfs_inode_mark_dirty(tna->ni); + ntfs_attr_close(tna); + ntfs_attr_put_search_ctx(ctx); + return ntfs_resident_attr_resize_i(na, newsize, force_non_resident); + } + /* 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_i(na, newsize, force_non_resident); + } - /* - * 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_i( na, newsize, force_non_resident ); - } + /* + * Move the attribute to a new mft record, creating an attribute list + * attribute or modifying it if it is already present. + */ - /* - * 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; + } - /* 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; + } - /* - * 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_i(na, newsize, force_non_resident); + } + /* 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; - /* 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_i( na, newsize, force_non_resident ); - } - /* 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_i( na, newsize, force_non_resident ); + ntfs_attr_put_search_ctx(ctx); + /* Try to perform resize once again. */ + return ntfs_resident_attr_resize_i(na, newsize, force_non_resident); 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; + /* + * 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; + ntfs_attr_put_search_ctx(ctx); + errno = err; + return ret; } -static int ntfs_resident_attr_resize( ntfs_attr *na, const s64 newsize ) +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, FALSE ); - ntfs_log_leave( "\n" ); - return ret; + int ret; + + ntfs_log_enter("Entering\n"); + ret = ntfs_resident_attr_resize_i(na, newsize, FALSE); + ntfs_log_leave("\n"); + return ret; } /* - * Force an attribute to be made non-resident without - * changing its size. + * Force an attribute to be made non-resident without + * changing its size. * - * This is particularly needed when the attribute has no data, - * as the non-resident variant requires more space in the MFT - * record, and may imply expelling some other attribute. + * This is particularly needed when the attribute has no data, + * as the non-resident variant requires more space in the MFT + * record, and may imply expelling some other attribute. * - * As a consequence the existing ntfs_attr_search_ctx's have to - * be closed or reinitialized. + * As a consequence the existing ntfs_attr_search_ctx's have to + * be closed or reinitialized. * - * returns 0 if successful, - * < 0 if failed, with errno telling why + * returns 0 if successful, + * < 0 if failed, with errno telling why */ -int ntfs_attr_force_non_resident( ntfs_attr *na ) +int ntfs_attr_force_non_resident(ntfs_attr *na) { - int res; + int res; - res = ntfs_resident_attr_resize_i( na, na->data_size, TRUE ); - if ( !res && !NAttrNonResident( na ) ) - { - res = -1; - errno = EIO; - ntfs_log_error( "Failed to force non-resident\n" ); - } - return ( res ); + res = ntfs_resident_attr_resize_i(na, na->data_size, TRUE); + if (!res && !NAttrNonResident(na)) { + res = -1; + errno = EIO; + ntfs_log_error("Failed to force non-resident\n"); + } + return (res); } /** * ntfs_attr_make_resident - convert a non-resident to a resident attribute - * @na: open ntfs attribute to make resident - * @ctx: ntfs search context describing the 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. + * 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. + * 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 ) +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_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 ); + 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" ); - errno = EINVAL; - return -1; - } + /* 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"); + errno = 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; - } + /* 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; - } + /* 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; + /* 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; - } + 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; + /* 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; - } + /* 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; + /* 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 ); + /* 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; - } + /* 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->vol->major_ver >= 3 ) - && NVolCompression( na->ni->vol ) - && ( na->ni->vol->cluster_size <= MAX_COMPRESSION_CLUSTER_SIZE ) - && ( na->ni->flags & FILE_ATTR_COMPRESSED ) ) - { - a->flags |= ATTR_IS_COMPRESSED; - na->data_flags = a->flags; - } - /* - * 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; + /* 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->vol->major_ver >= 3) + && NVolCompression(na->ni->vol) + && (na->ni->vol->cluster_size <= MAX_COMPRESSION_CLUSTER_SIZE) + && (na->ni->flags & FILE_ATTR_COMPRESSED)) { + a->flags |= ATTR_IS_COMPRESSED; + na->data_flags = a->flags; + } + /* + * 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; + /* 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; - } + /* 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 ); + /* 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" ); - } + /* + * 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; + /* 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; + /* 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 ) +static int ntfs_attr_update_meta(ATTR_RECORD *a, ntfs_attr *na, MFT_RECORD *m, + ntfs_attr_search_ctx *ctx) { - int sparse, ret = 0; + 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; - ntfs_log_trace( "Entering for inode 0x%llx, attr 0x%x\n", - ( unsigned long long )na->ni->mft_no, na->type ); + a->allocated_size = cpu_to_sle64(na->allocated_size); - if ( a->lowest_vcn ) - goto out; + /* Update sparse bit. */ + sparse = ntfs_rl_sparse(na->rl); + if (sparse == -1) { + errno = EIO; + goto error; + } - a->allocated_size = cpu_to_sle64( na->allocated_size ); + /* 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))) { - /* Update sparse bit. */ - sparse = ntfs_rl_sparse( na->rl ); - if ( sparse == -1 ) - { - errno = EIO; - goto error; - } + 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)); - /* 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 ) ) ) - { + 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); + } - 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; - } + /* 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); - NAttrSetSparse( na ); - a->flags |= ATTR_IS_SPARSE; - a->compression_unit = STANDARD_COMPRESSION_UNIT; /* Windows - set it so, even if attribute is not actually compressed. */ + a->mapping_pairs_offset = + cpu_to_le16(le16_to_cpu(a->mapping_pairs_offset) - 8); + } - memmove( ( u8* )a + le16_to_cpu( a->name_offset ) + 8, - ( u8* )a + le16_to_cpu( a->name_offset ), - a->name_length * sizeof( ntfschar ) ); + /* Update compressed size if required. */ + if (sparse || (na->data_flags & ATTR_COMPRESSION_MASK)) { + s64 new_compr_size; - 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->data_flags & ATTR_COMPRESSION_MASK ) ) - na->ni->allocated_size = na->compressed_size; - else - na->ni->allocated_size = na->allocated_size; - NInoFileNameSetDirty( na->ni ); - } + 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->data_flags & ATTR_COMPRESSION_MASK)) + 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; + return ret; +leave: ret = -1; goto out; /* return -1 */ +retry: ret = -2; goto out; error: ret = -3; goto out; } @@ -5733,369 +5310,337 @@ error: ret = -3; goto out; /** * 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 ) +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; - BOOL first_updated = FALSE; + 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; + BOOL first_updated = FALSE; retry: - if ( !na || !na->rl ) - { - errno = EINVAL; - ntfs_log_perror( "%s: na=%p", __FUNCTION__, na ); - return -1; - } + if (!na || !na->rl) { + 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 ); + 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 (!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; + 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; + 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 ( !a->lowest_vcn ) - first_updated = TRUE; - /* - * If runlist is updating not from the beginning, then set - * @stop_vcn properly, i.e. to the lowest vcn of record that - * 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; + /* 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 (!a->lowest_vcn) + first_updated = TRUE; + /* + * If runlist is updating not from the beginning, then set + * @stop_vcn properly, i.e. to the lowest vcn of record that + * 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; - } + 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; - } + /* + * 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; - } + 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; - } + /* + * 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; - } + /* 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; - } + /* + * 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; - } - } + /* 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 ); - } + /* 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; - } - /* - * If the base extent was skipped in the above process, - * we still may have to update the sizes. - */ - if ( !first_updated ) - { - le16 spcomp; + /* + * 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; + } + /* + * If the base extent was skipped in the above process, + * we still may have to update the sizes. + */ + if (!first_updated) { + le16 spcomp; - ntfs_attr_reinit_search_ctx( ctx ); - if ( !ntfs_attr_lookup( na->type, na->name, na->name_len, - CASE_SENSITIVE, 0, NULL, 0, ctx ) ) - { - a = ctx->attr; - a->allocated_size = cpu_to_sle64( na->allocated_size ); - spcomp = na->data_flags - & ( ATTR_IS_COMPRESSED | ATTR_IS_SPARSE ); - if ( spcomp ) - a->compressed_size = cpu_to_sle64( na->compressed_size ); - if ( ( na->type == AT_DATA ) && ( na->name == AT_UNNAMED ) ) - { - na->ni->allocated_size - = ( spcomp - ? na->compressed_size - : na->allocated_size ); - NInoFileNameSetDirty( na->ni ); - } - } - else - { - ntfs_log_error( "Failed to update sizes in base extent\n" ); - goto put_err_out; - } - } + ntfs_attr_reinit_search_ctx(ctx); + if (!ntfs_attr_lookup(na->type, na->name, na->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + a = ctx->attr; + a->allocated_size = cpu_to_sle64(na->allocated_size); + spcomp = na->data_flags + & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE); + if (spcomp) + a->compressed_size = cpu_to_sle64(na->compressed_size); + if ((na->type == AT_DATA) && (na->name == AT_UNNAMED)) { + na->ni->allocated_size + = (spcomp + ? na->compressed_size + : na->allocated_size); + NInoFileNameSetDirty(na->ni); + } + } else { + ntfs_log_error("Failed to update sizes in base extent\n"); + goto put_err_out; + } + } - /* Deallocate not used attribute extents and return with success. */ - if ( finished_build ) - { - 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; + /* 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 ); + /* 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; - } + 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; + ret = 0; out: - return ret; + return ret; put_err_out: - if ( ctx ) - ntfs_attr_put_search_ctx( ctx ); - goto 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 + * @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 @@ -6112,446 +5657,403 @@ put_err_out: * * 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. + * 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 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; + 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 + * @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. + * 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 ) +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_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 ); + 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; + 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; - } + /* + * 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. */ - if ( na->data_flags & ATTR_COMPRESSION_MASK ) - /* - * For compressed files we must keep full compressions blocks, - * but currently we do not decompress/recompress the last - * block to truncate the data, so we may leave more allocated - * clusters than really needed. - */ - first_free_vcn = ( ( ( newsize - 1 ) - | ( na->compression_block_size - 1 ) ) + 1 ) - >> vol->cluster_size_bits; - else - first_free_vcn = ( newsize + vol->cluster_size - 1 ) >> - vol->cluster_size_bits; - /* - * Compare the new allocation with the old one and only deallocate - * clusters if there is a change. - */ - 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; - } + /* The first cluster outside the new allocation. */ + if (na->data_flags & ATTR_COMPRESSION_MASK) + /* + * For compressed files we must keep full compressions blocks, + * but currently we do not decompress/recompress the last + * block to truncate the data, so we may leave more allocated + * clusters than really needed. + */ + first_free_vcn = (((newsize - 1) + | (na->compression_block_size - 1)) + 1) + >> vol->cluster_size_bits; + else + first_free_vcn = (newsize + vol->cluster_size - 1) >> + vol->cluster_size_bits; + /* + * Compare the new allocation with the old one and only deallocate + * clusters if there is a change. + */ + 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; - } + /* 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; - } - } + /* 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; + /* 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; - } + 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->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ) - { - if ( na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30 ) - { - na->ni->data_size = na->data_size; - na->ni->allocated_size = na->allocated_size; - set_nino_flag( na->ni, KnownSize ); - } - } - else - { - if ( na->type == AT_DATA && na->name == AT_UNNAMED ) - { - na->ni->data_size = na->data_size; - NInoFileNameSetDirty( na->ni ); - } - } + /* 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->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + if (na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30) { + na->ni->data_size = na->data_size; + na->ni->allocated_size = na->allocated_size; + set_nino_flag(na->ni,KnownSize); + } + } else { + if (na->type == AT_DATA && na->name == AT_UNNAMED) { + na->ni->data_size = na->data_size; + NInoFileNameSetDirty(na->ni); + } + } - /* If the attribute now has zero size, make it resident. */ - 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" ); - } - } + /* 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; + /* 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_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 + * @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. + * 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 ) +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; + 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 ); + 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; + 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; - } + /* + * 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; - } + /* 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; + /* + * 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[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; + } + } - 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; - /* 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; + } + } - /* 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; + } - 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; + } - 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->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ) - { - if ( na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30 ) - { - na->ni->data_size = na->data_size; - na->ni->allocated_size = na->allocated_size; - set_nino_flag( na->ni, KnownSize ); - } - } - else - { - if ( na->type == AT_DATA && na->name == AT_UNNAMED ) - { - na->ni->data_size = na->data_size; - NInoFileNameSetDirty( na->ni ); - } - } - /* Set the inode dirty so it is written out later. */ - ntfs_inode_mark_dirty( ctx->ntfs_ino ); - /* Done! */ - ntfs_attr_put_search_ctx( ctx ); - return 0; + /* Update data size. */ + na->data_size = newsize; + ctx->attr->data_size = cpu_to_sle64(newsize); + /* Update data size in the index. */ + if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + if (na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30) { + na->ni->data_size = na->data_size; + na->ni->allocated_size = na->allocated_size; + set_nino_flag(na->ni,KnownSize); + } + } else { + if (na->type == AT_DATA && na->name == AT_UNNAMED) { + na->ni->data_size = na->data_size; + NInoFileNameSetDirty(na->ni); + } + } + /* Set the inode dirty so it is written out later. */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + /* 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; + /* 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; + ntfs_attr_put_search_ctx(ctx); + errno = err; + return -1; } -static int ntfs_non_resident_attr_expand( ntfs_attr *na, const s64 newsize ) +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; + 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 + * @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 @@ -6561,361 +6063,339 @@ static int ntfs_non_resident_attr_expand( ntfs_attr *na, const s64 newsize ) * * On success return 0. * On error return values are: - * STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT - * STATUS_ERROR - otherwise + * 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. + * 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 ntfs_attr_truncate(ntfs_attr *na, const s64 newsize) { - int ret = STATUS_ERROR; - s64 fullsize; - BOOL compressed; + 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; - } + 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 ); + 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_trace( "Cannot truncate encrypted attribute\n" ); - 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 ) ) - { - 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 know the size of compression - * block until the attribute has been made non-resident. - * Moreover we can only process a single compression - * block at a time (from where we are about to write), - * so we silently do not allocate more. - * - * Note : do not request upsizing of compressed files - * unless being able to face the consequences ! - */ - if ( compressed && newsize && ( newsize > na->data_size ) ) - 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; + 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_trace("Cannot truncate encrypted attribute\n"); + 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)) { + 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 know the size of compression + * block until the attribute has been made non-resident. + * Moreover we can only process a single compression + * block at a time (from where we are about to write), + * so we silently do not allocate more. + * + * Note : do not request upsizing of compressed files + * unless being able to face the consequences ! + */ + if (compressed && newsize && (newsize > na->data_size)) + 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 + * 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. + * 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) + * Returns 0 if succeeded, + * -1 if it failed (as explained in errno) */ -static int stuff_hole( ntfs_attr *na, const s64 pos ) +static int stuff_hole(ntfs_attr *na, const s64 pos) { - s64 size; - s64 begin_size; - s64 end_size; - char *buf; - int ret; + 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 ); + 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 + * @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. + * 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 + * 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 ) +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; + 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 ); + ntfs_attr_close(na); err_exit: - ntfs_log_leave( "\n" ); - return ret; + ntfs_log_leave("\n"); + return ret; } -int ntfs_attr_exist( ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name, - u32 name_len ) +int ntfs_attr_exist(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name, + u32 name_len) { - ntfs_attr_search_ctx *ctx; - int ret; + 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_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; + ntfs_attr_put_search_ctx(ctx); + + return !ret; } -int ntfs_attr_remove( ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name, - u32 name_len ) +int ntfs_attr_remove(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name, + u32 name_len) { - ntfs_attr *na; - int ret; + 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; + 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)) + (((x) >> 2) & 0x33333333) - \ + (((x) >> 3) & 0x11111111)) #define BITCOUNT(x) (((BCX(x) + (BCX(x) >> 4)) & 0x0F0F0F0F) % 255) -static u8 *ntfs_init_lut256( void ) +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; + 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 ) +s64 ntfs_attr_get_free_bits(ntfs_attr *na) { - u8 *buf, *lut; - s64 br = 0; - s64 total = 0; - s64 nr_free = 0; + u8 *buf, *lut; + s64 br = 0; + s64 total = 0; + s64 nr_free = 0; - lut = ntfs_init_lut256(); - if ( !lut ) - return -1; + lut = ntfs_init_lut256(); + if (!lut) + return -1; + + buf = ntfs_malloc(65536); + if (!buf) + goto out; - 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 ); + 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; + free(lut); + if (!total || br < 0) + return -1; + return nr_free; } diff --git a/source/libntfs/attrib.h b/source/libntfs/attrib.h index bc9205c0..43ab7f53 100644 --- a/source/libntfs/attrib.h +++ b/source/libntfs/attrib.h @@ -49,20 +49,19 @@ extern ntfschar TXF_DATA[10]; * * TODO: Describe them. */ -typedef enum -{ - LCN_HOLE = -1, /* Keep this as highest value or die! */ - LCN_RL_NOT_MAPPED = -2, - LCN_ENOENT = -3, - LCN_EINVAL = -4, - LCN_EIO = -5, +typedef enum { + LCN_HOLE = -1, /* Keep this as highest value or die! */ + LCN_RL_NOT_MAPPED = -2, + LCN_ENOENT = -3, + LCN_EINVAL = -4, + LCN_EIO = -5, } ntfs_lcn_special_values; /** * struct ntfs_attr_search_ctx - search context used in attribute search functions - * @mrec: buffer containing mft record to search - * @attr: attribute record in @mrec where to begin/continue search - * @is_first: if true lookup_attr() begins search with @attr, else after @attr + * @mrec: buffer containing mft record to search + * @attr: attribute record in @mrec where to begin/continue search + * @is_first: if true lookup_attr() begins search with @attr, else after @attr * * Structure must be initialized to zero before the first call to one of the * attribute search functions. Initialize @mrec to point to the mft record to @@ -76,36 +75,35 @@ typedef enum * any modification of the search context, to automagically get the next * matching attribute. */ -struct _ntfs_attr_search_ctx -{ - MFT_RECORD *mrec; - ATTR_RECORD *attr; - BOOL is_first; - ntfs_inode *ntfs_ino; - ATTR_LIST_ENTRY *al_entry; - ntfs_inode *base_ntfs_ino; - MFT_RECORD *base_mrec; - ATTR_RECORD *base_attr; +struct _ntfs_attr_search_ctx { + MFT_RECORD *mrec; + ATTR_RECORD *attr; + BOOL is_first; + ntfs_inode *ntfs_ino; + ATTR_LIST_ENTRY *al_entry; + ntfs_inode *base_ntfs_ino; + MFT_RECORD *base_mrec; + ATTR_RECORD *base_attr; }; -extern void ntfs_attr_reinit_search_ctx( ntfs_attr_search_ctx *ctx ); -extern ntfs_attr_search_ctx *ntfs_attr_get_search_ctx( ntfs_inode *ni, - MFT_RECORD *mrec ); -extern void ntfs_attr_put_search_ctx( ntfs_attr_search_ctx *ctx ); +extern void ntfs_attr_reinit_search_ctx(ntfs_attr_search_ctx *ctx); +extern ntfs_attr_search_ctx *ntfs_attr_get_search_ctx(ntfs_inode *ni, + MFT_RECORD *mrec); +extern void ntfs_attr_put_search_ctx(ntfs_attr_search_ctx *ctx); -extern 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 ); +extern 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); -extern int ntfs_attr_position( const ATTR_TYPES type, ntfs_attr_search_ctx *ctx ); +extern int ntfs_attr_position(const ATTR_TYPES type, ntfs_attr_search_ctx *ctx); -extern ATTR_DEF *ntfs_attr_find_in_attrdef( const ntfs_volume *vol, - const ATTR_TYPES type ); +extern ATTR_DEF *ntfs_attr_find_in_attrdef(const ntfs_volume *vol, + const ATTR_TYPES type); /** * ntfs_attrs_walk - syntactic sugar for walking all attributes in an inode - * @ctx: initialised attribute search context + * @ctx: initialised attribute search context * * Syntactic sugar for walking attributes in an inode. * @@ -113,42 +111,42 @@ extern ATTR_DEF *ntfs_attr_find_in_attrdef( const ntfs_volume *vol, * ntfs_attr_lookup(). * * Example: When you want to enumerate all attributes in an open ntfs inode - * @ni, you can simply do: + * @ni, you can simply do: * - * int err; - * ntfs_attr_search_ctx *ctx = ntfs_attr_get_search_ctx(ni, NULL); - * if (!ctx) - * // Error code is in errno. Handle this case. - * while (!(err = ntfs_attrs_walk(ctx))) { - * ATTR_RECORD *attr = ctx->attr; - * // attr now contains the next attribute. Do whatever you want - * // with it and then just continue with the while loop. - * } - * if (err && errno != ENOENT) - * // Ooops. An error occurred! You should handle this case. - * // Now finished with all attributes in the inode. + * int err; + * ntfs_attr_search_ctx *ctx = ntfs_attr_get_search_ctx(ni, NULL); + * if (!ctx) + * // Error code is in errno. Handle this case. + * while (!(err = ntfs_attrs_walk(ctx))) { + * ATTR_RECORD *attr = ctx->attr; + * // attr now contains the next attribute. Do whatever you want + * // with it and then just continue with the while loop. + * } + * if (err && errno != ENOENT) + * // Ooops. An error occurred! You should handle this case. + * // Now finished with all attributes in the inode. */ -static __inline__ int ntfs_attrs_walk( ntfs_attr_search_ctx *ctx ) +static __inline__ int ntfs_attrs_walk(ntfs_attr_search_ctx *ctx) { - return ntfs_attr_lookup( AT_UNUSED, NULL, 0, CASE_SENSITIVE, 0, - NULL, 0, ctx ); + return ntfs_attr_lookup(AT_UNUSED, NULL, 0, CASE_SENSITIVE, 0, + NULL, 0, ctx); } /** * struct ntfs_attr - ntfs in memory non-resident attribute structure - * @rl: if not NULL, the decompressed runlist - * @ni: base ntfs inode to which this attribute belongs - * @type: attribute type - * @name: Unicode name of the attribute - * @name_len: length of @name in Unicode characters - * @state: NTFS attribute specific flags describing this attribute - * @allocated_size: copy from the attribute record - * @data_size: copy from the attribute record - * @initialized_size: copy from the attribute record - * @compressed_size: copy from the attribute record - * @compression_block_size: size of a compression block (cb) - * @compression_block_size_bits: log2 of the size of a cb - * @compression_block_clusters: number of clusters per cb + * @rl: if not NULL, the decompressed runlist + * @ni: base ntfs inode to which this attribute belongs + * @type: attribute type + * @name: Unicode name of the attribute + * @name_len: length of @name in Unicode characters + * @state: NTFS attribute specific flags describing this attribute + * @allocated_size: copy from the attribute record + * @data_size: copy from the attribute record + * @initialized_size: copy from the attribute record + * @compressed_size: copy from the attribute record + * @compression_block_size: size of a compression block (cb) + * @compression_block_size_bits: log2 of the size of a cb + * @compression_block_clusters: number of clusters per cb * * This structure exists purely to provide a mechanism of caching the runlist * of an attribute. If you want to operate on a particular attribute extent, @@ -176,70 +174,68 @@ static __inline__ int ntfs_attrs_walk( ntfs_attr_search_ctx *ctx ) * @state contains NTFS attribute specific flags describing this attribute * structure. See ntfs_attr_state_bits above. */ -struct _ntfs_attr -{ - runlist_element *rl; - ntfs_inode *ni; - ATTR_TYPES type; - ATTR_FLAGS data_flags; - ntfschar *name; - u32 name_len; - unsigned long state; - s64 allocated_size; - s64 data_size; - s64 initialized_size; - s64 compressed_size; - u32 compression_block_size; - u8 compression_block_size_bits; - u8 compression_block_clusters; - s8 unused_runs; /* pre-reserved entries available */ +struct _ntfs_attr { + runlist_element *rl; + ntfs_inode *ni; + ATTR_TYPES type; + ATTR_FLAGS data_flags; + ntfschar *name; + u32 name_len; + unsigned long state; + s64 allocated_size; + s64 data_size; + s64 initialized_size; + s64 compressed_size; + u32 compression_block_size; + u8 compression_block_size_bits; + u8 compression_block_clusters; + s8 unused_runs; /* pre-reserved entries available */ }; /** * enum ntfs_attr_state_bits - bits for the state field in the ntfs_attr * structure */ -typedef enum -{ - NA_Initialized, /* 1: structure is initialized. */ - NA_NonResident, /* 1: Attribute is not resident. */ - NA_BeingNonResident, /* 1: Attribute is being made not resident. */ - NA_FullyMapped, /* 1: Attribute has been fully mapped */ - NA_ComprClosing, /* 1: Compressed attribute is being closed */ +typedef enum { + NA_Initialized, /* 1: structure is initialized. */ + NA_NonResident, /* 1: Attribute is not resident. */ + NA_BeingNonResident, /* 1: Attribute is being made not resident. */ + NA_FullyMapped, /* 1: Attribute has been fully mapped */ + NA_ComprClosing, /* 1: Compressed attribute is being closed */ } ntfs_attr_state_bits; -#define test_nattr_flag(na, flag) test_bit(NA_##flag, (na)->state) -#define set_nattr_flag(na, flag) set_bit(NA_##flag, (na)->state) -#define clear_nattr_flag(na, flag) clear_bit(NA_##flag, (na)->state) +#define test_nattr_flag(na, flag) test_bit(NA_##flag, (na)->state) +#define set_nattr_flag(na, flag) set_bit(NA_##flag, (na)->state) +#define clear_nattr_flag(na, flag) clear_bit(NA_##flag, (na)->state) -#define NAttrInitialized(na) test_nattr_flag(na, Initialized) -#define NAttrSetInitialized(na) set_nattr_flag(na, Initialized) -#define NAttrClearInitialized(na) clear_nattr_flag(na, Initialized) +#define NAttrInitialized(na) test_nattr_flag(na, Initialized) +#define NAttrSetInitialized(na) set_nattr_flag(na, Initialized) +#define NAttrClearInitialized(na) clear_nattr_flag(na, Initialized) -#define NAttrNonResident(na) test_nattr_flag(na, NonResident) -#define NAttrSetNonResident(na) set_nattr_flag(na, NonResident) -#define NAttrClearNonResident(na) clear_nattr_flag(na, NonResident) +#define NAttrNonResident(na) test_nattr_flag(na, NonResident) +#define NAttrSetNonResident(na) set_nattr_flag(na, NonResident) +#define NAttrClearNonResident(na) clear_nattr_flag(na, NonResident) -#define NAttrBeingNonResident(na) test_nattr_flag(na, BeingNonResident) -#define NAttrSetBeingNonResident(na) set_nattr_flag(na, BeingNonResident) -#define NAttrClearBeingNonResident(na) clear_nattr_flag(na, BeingNonResident) +#define NAttrBeingNonResident(na) test_nattr_flag(na, BeingNonResident) +#define NAttrSetBeingNonResident(na) set_nattr_flag(na, BeingNonResident) +#define NAttrClearBeingNonResident(na) clear_nattr_flag(na, BeingNonResident) -#define NAttrFullyMapped(na) test_nattr_flag(na, FullyMapped) -#define NAttrSetFullyMapped(na) set_nattr_flag(na, FullyMapped) -#define NAttrClearFullyMapped(na) clear_nattr_flag(na, FullyMapped) +#define NAttrFullyMapped(na) test_nattr_flag(na, FullyMapped) +#define NAttrSetFullyMapped(na) set_nattr_flag(na, FullyMapped) +#define NAttrClearFullyMapped(na) clear_nattr_flag(na, FullyMapped) -#define NAttrComprClosing(na) test_nattr_flag(na, ComprClosing) -#define NAttrSetComprClosing(na) set_nattr_flag(na, ComprClosing) -#define NAttrClearComprClosing(na) clear_nattr_flag(na, ComprClosing) +#define NAttrComprClosing(na) test_nattr_flag(na, ComprClosing) +#define NAttrSetComprClosing(na) set_nattr_flag(na, ComprClosing) +#define NAttrClearComprClosing(na) clear_nattr_flag(na, ComprClosing) -#define GenNAttrIno(func_name, flag) \ -extern int NAttr##func_name(ntfs_attr *na); \ -extern void NAttrSet##func_name(ntfs_attr *na); \ +#define GenNAttrIno(func_name, flag) \ +extern int NAttr##func_name(ntfs_attr *na); \ +extern void NAttrSet##func_name(ntfs_attr *na); \ extern void NAttrClear##func_name(ntfs_attr *na); -GenNAttrIno( Compressed, FILE_ATTR_COMPRESSED ) -GenNAttrIno( Encrypted, FILE_ATTR_ENCRYPTED ) -GenNAttrIno( Sparse, FILE_ATTR_SPARSE_FILE ) +GenNAttrIno(Compressed, FILE_ATTR_COMPRESSED) +GenNAttrIno(Encrypted, FILE_ATTR_ENCRYPTED) +GenNAttrIno(Sparse, FILE_ATTR_SPARSE_FILE) #undef GenNAttrIno /** @@ -247,100 +243,99 @@ GenNAttrIno( Sparse, FILE_ATTR_SPARSE_FILE ) * * For convenience. Used in the attr structure. */ -typedef union -{ - u8 _default; /* Unnamed u8 to serve as default when just using - a_val without specifying any of the below. */ - STANDARD_INFORMATION std_inf; - ATTR_LIST_ENTRY al_entry; - FILE_NAME_ATTR filename; - OBJECT_ID_ATTR obj_id; - SECURITY_DESCRIPTOR_ATTR sec_desc; - VOLUME_NAME vol_name; - VOLUME_INFORMATION vol_inf; - DATA_ATTR data; - INDEX_ROOT index_root; - INDEX_BLOCK index_blk; - BITMAP_ATTR bmp; - REPARSE_POINT reparse; - EA_INFORMATION ea_inf; - EA_ATTR ea; - PROPERTY_SET property_set; - LOGGED_UTILITY_STREAM logged_util_stream; - EFS_ATTR_HEADER efs; +typedef union { + u8 _default; /* Unnamed u8 to serve as default when just using + a_val without specifying any of the below. */ + STANDARD_INFORMATION std_inf; + ATTR_LIST_ENTRY al_entry; + FILE_NAME_ATTR filename; + OBJECT_ID_ATTR obj_id; + SECURITY_DESCRIPTOR_ATTR sec_desc; + VOLUME_NAME vol_name; + VOLUME_INFORMATION vol_inf; + DATA_ATTR data; + INDEX_ROOT index_root; + INDEX_BLOCK index_blk; + BITMAP_ATTR bmp; + REPARSE_POINT reparse; + EA_INFORMATION ea_inf; + EA_ATTR ea; + PROPERTY_SET property_set; + LOGGED_UTILITY_STREAM logged_util_stream; + EFS_ATTR_HEADER efs; } attr_val; -extern 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 ); +extern 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); -/* warning : in the following "name" has to be freeable */ -/* or one of constants AT_UNNAMED, NTFS_INDEX_I30 or STREAM_SDS */ -extern ntfs_attr *ntfs_attr_open( ntfs_inode *ni, const ATTR_TYPES type, - ntfschar *name, u32 name_len ); -extern void ntfs_attr_close( ntfs_attr *na ); + /* warning : in the following "name" has to be freeable */ + /* or one of constants AT_UNNAMED, NTFS_INDEX_I30 or STREAM_SDS */ +extern ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, + ntfschar *name, u32 name_len); +extern void ntfs_attr_close(ntfs_attr *na); -extern s64 ntfs_attr_pread( ntfs_attr *na, const s64 pos, s64 count, - void *b ); -extern s64 ntfs_attr_pwrite( ntfs_attr *na, const s64 pos, s64 count, - const void *b ); -extern int ntfs_attr_pclose( ntfs_attr *na ); +extern s64 ntfs_attr_pread(ntfs_attr *na, const s64 pos, s64 count, + void *b); +extern s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, + const void *b); +extern int ntfs_attr_pclose(ntfs_attr *na); -extern void *ntfs_attr_readall( ntfs_inode *ni, const ATTR_TYPES type, - ntfschar *name, u32 name_len, s64 *data_size ); +extern void *ntfs_attr_readall(ntfs_inode *ni, const ATTR_TYPES type, + ntfschar *name, u32 name_len, s64 *data_size); -extern s64 ntfs_attr_mst_pread( ntfs_attr *na, const s64 pos, - const s64 bk_cnt, const u32 bk_size, void *dst ); -extern s64 ntfs_attr_mst_pwrite( ntfs_attr *na, const s64 pos, - s64 bk_cnt, const u32 bk_size, void *src ); +extern s64 ntfs_attr_mst_pread(ntfs_attr *na, const s64 pos, + const s64 bk_cnt, const u32 bk_size, void *dst); +extern s64 ntfs_attr_mst_pwrite(ntfs_attr *na, const s64 pos, + s64 bk_cnt, const u32 bk_size, void *src); -extern int ntfs_attr_map_runlist( ntfs_attr *na, VCN vcn ); -extern int ntfs_attr_map_whole_runlist( ntfs_attr *na ); +extern int ntfs_attr_map_runlist(ntfs_attr *na, VCN vcn); +extern int ntfs_attr_map_whole_runlist(ntfs_attr *na); -extern LCN ntfs_attr_vcn_to_lcn( ntfs_attr *na, const VCN vcn ); -extern runlist_element *ntfs_attr_find_vcn( ntfs_attr *na, const VCN vcn ); +extern LCN ntfs_attr_vcn_to_lcn(ntfs_attr *na, const VCN vcn); +extern runlist_element *ntfs_attr_find_vcn(ntfs_attr *na, const VCN vcn); -extern int ntfs_attr_size_bounds_check( const ntfs_volume *vol, - const ATTR_TYPES type, const s64 size ); -extern int ntfs_attr_can_be_resident( const ntfs_volume *vol, - const ATTR_TYPES type ); -int ntfs_attr_make_non_resident( ntfs_attr *na, - ntfs_attr_search_ctx *ctx ); -int ntfs_attr_force_non_resident( ntfs_attr *na ); -extern int ntfs_make_room_for_attr( MFT_RECORD *m, u8 *pos, u32 size ); +extern int ntfs_attr_size_bounds_check(const ntfs_volume *vol, + const ATTR_TYPES type, const s64 size); +extern int ntfs_attr_can_be_resident(const ntfs_volume *vol, + const ATTR_TYPES type); +int ntfs_attr_make_non_resident(ntfs_attr *na, + ntfs_attr_search_ctx *ctx); +int ntfs_attr_force_non_resident(ntfs_attr *na); +extern int ntfs_make_room_for_attr(MFT_RECORD *m, u8 *pos, u32 size); -extern int ntfs_resident_attr_record_add( ntfs_inode *ni, ATTR_TYPES type, - ntfschar *name, u8 name_len, u8 *val, u32 size, - ATTR_FLAGS flags ); -extern 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 ); -extern int ntfs_attr_record_rm( ntfs_attr_search_ctx *ctx ); +extern int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, + ntfschar *name, u8 name_len, u8 *val, u32 size, + ATTR_FLAGS flags); +extern 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); +extern int ntfs_attr_record_rm(ntfs_attr_search_ctx *ctx); -extern int ntfs_attr_add( ntfs_inode *ni, ATTR_TYPES type, - ntfschar *name, u8 name_len, u8 *val, s64 size ); -extern int ntfs_attr_set_flags( ntfs_inode *ni, ATTR_TYPES type, - ntfschar *name, u8 name_len, ATTR_FLAGS flags, ATTR_FLAGS mask ); -extern int ntfs_attr_rm( ntfs_attr *na ); +extern int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type, + ntfschar *name, u8 name_len, u8 *val, s64 size); +extern int ntfs_attr_set_flags(ntfs_inode *ni, ATTR_TYPES type, + ntfschar *name, u8 name_len, ATTR_FLAGS flags, ATTR_FLAGS mask); +extern int ntfs_attr_rm(ntfs_attr *na); -extern int ntfs_attr_record_resize( MFT_RECORD *m, ATTR_RECORD *a, u32 new_size ); +extern int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size); -extern int ntfs_resident_attr_value_resize( MFT_RECORD *m, ATTR_RECORD *a, - const u32 new_size ); +extern int ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a, + const u32 new_size); -extern int ntfs_attr_record_move_to( ntfs_attr_search_ctx *ctx, ntfs_inode *ni ); -extern int ntfs_attr_record_move_away( ntfs_attr_search_ctx *ctx, int extra ); +extern int ntfs_attr_record_move_to(ntfs_attr_search_ctx *ctx, ntfs_inode *ni); +extern int ntfs_attr_record_move_away(ntfs_attr_search_ctx *ctx, int extra); -extern int ntfs_attr_update_mapping_pairs( ntfs_attr *na, VCN from_vcn ); +extern int ntfs_attr_update_mapping_pairs(ntfs_attr *na, VCN from_vcn); -extern int ntfs_attr_truncate( ntfs_attr *na, const s64 newsize ); +extern int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize); /** * get_attribute_value_length - return the length of the value of an attribute - * @a: pointer to a buffer containing the attribute record + * @a: pointer to a buffer containing the attribute record * * Return the byte size of the attribute value of the attribute @a (as it * would be after eventual decompression and filling in of holes if sparse). @@ -349,13 +344,13 @@ extern int ntfs_attr_truncate( ntfs_attr *na, const s64 newsize ); * * FIXME: Describe possible errnos. */ -extern s64 ntfs_get_attribute_value_length( const ATTR_RECORD *a ); +extern s64 ntfs_get_attribute_value_length(const ATTR_RECORD *a); /** * get_attribute_value - return the attribute value of an attribute - * @vol: volume on which the attribute is present - * @a: attribute to get the value of - * @b: destination buffer for the attribute value + * @vol: volume on which the attribute is present + * @a: attribute to get the value of + * @b: destination buffer for the attribute value * * Make a copy of the attribute value of the attribute @a into the destination * buffer @b. Note, that the size of @b has to be at least equal to the value @@ -365,16 +360,16 @@ extern s64 ntfs_get_attribute_value_length( const ATTR_RECORD *a ); * then nothing was read due to a zero-length attribute value, otherwise * errno describes the error. */ -extern s64 ntfs_get_attribute_value( const ntfs_volume *vol, - const ATTR_RECORD *a, u8 *b ); +extern s64 ntfs_get_attribute_value(const ntfs_volume *vol, + const ATTR_RECORD *a, u8 *b); -extern void ntfs_attr_name_free( char **name ); -extern char *ntfs_attr_name_get( const ntfschar *uname, const int uname_len ); -extern int ntfs_attr_exist( ntfs_inode *ni, const ATTR_TYPES type, - ntfschar *name, u32 name_len ); -extern int ntfs_attr_remove( ntfs_inode *ni, const ATTR_TYPES type, - ntfschar *name, u32 name_len ); -extern s64 ntfs_attr_get_free_bits( ntfs_attr *na ); +extern void ntfs_attr_name_free(char **name); +extern char *ntfs_attr_name_get(const ntfschar *uname, const int uname_len); +extern int ntfs_attr_exist(ntfs_inode *ni, const ATTR_TYPES type, + ntfschar *name, u32 name_len); +extern int ntfs_attr_remove(ntfs_inode *ni, const ATTR_TYPES type, + ntfschar *name, u32 name_len); +extern s64 ntfs_attr_get_free_bits(ntfs_attr *na); #endif /* defined _NTFS_ATTRIB_H */ diff --git a/source/libntfs/attrlist.c b/source/libntfs/attrlist.c index dce3eea3..9c62f316 100644 --- a/source/libntfs/attrlist.c +++ b/source/libntfs/attrlist.c @@ -1,6 +1,6 @@ /** * attrlist.c - Attribute list attribute handling code. Originated from the Linux-NTFS - * project. + * project. * * Copyright (c) 2004-2005 Anton Altaparmakov * Copyright (c) 2004-2005 Yura Pakhuchiy @@ -47,7 +47,7 @@ /** * ntfs_attrlist_need - check whether inode need attribute list - * @ni: opened ntfs inode for which perform check + * @ni: opened ntfs inode for which perform check * * Check whether all are attributes belong to one MFT record, in that case * attribute list is not needed. @@ -55,278 +55,260 @@ * Return 1 if inode need attribute list, 0 if not, -1 on error with errno set * to the error code. If function succeed errno set to 0. The following error * codes are defined: - * EINVAL - Invalid arguments passed to function or attribute haven't got - * attribute list. + * EINVAL - Invalid arguments passed to function or attribute haven't got + * attribute list. */ -int ntfs_attrlist_need( ntfs_inode *ni ) +int ntfs_attrlist_need(ntfs_inode *ni) { - ATTR_LIST_ENTRY *ale; + ATTR_LIST_ENTRY *ale; - if ( !ni ) - { - ntfs_log_trace( "Invalid arguments.\n" ); - errno = EINVAL; - return -1; - } + if (!ni) { + ntfs_log_trace("Invalid arguments.\n"); + errno = EINVAL; + return -1; + } - ntfs_log_trace( "Entering for inode 0x%llx.\n", ( long long ) ni->mft_no ); + ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no); - if ( !NInoAttrList( ni ) ) - { - ntfs_log_trace( "Inode haven't got attribute list.\n" ); - errno = EINVAL; - return -1; - } + if (!NInoAttrList(ni)) { + ntfs_log_trace("Inode haven't got attribute list.\n"); + errno = EINVAL; + return -1; + } - if ( !ni->attr_list ) - { - ntfs_log_trace( "Corrupt in-memory struct.\n" ); - errno = EINVAL; - return -1; - } + if (!ni->attr_list) { + ntfs_log_trace("Corrupt in-memory struct.\n"); + errno = EINVAL; + return -1; + } - errno = 0; - ale = ( ATTR_LIST_ENTRY * )ni->attr_list; - while ( ( u8* )ale < ni->attr_list + ni->attr_list_size ) - { - if ( MREF_LE( ale->mft_reference ) != ni->mft_no ) - return 1; - ale = ( ATTR_LIST_ENTRY * )( ( u8* )ale + le16_to_cpu( ale->length ) ); - } - return 0; + errno = 0; + ale = (ATTR_LIST_ENTRY *)ni->attr_list; + while ((u8*)ale < ni->attr_list + ni->attr_list_size) { + if (MREF_LE(ale->mft_reference) != ni->mft_no) + return 1; + ale = (ATTR_LIST_ENTRY *)((u8*)ale + le16_to_cpu(ale->length)); + } + return 0; } /** * ntfs_attrlist_entry_add - add an attribute list attribute entry - * @ni: opened ntfs inode, which contains that attribute - * @attr: attribute record to add to attribute list + * @ni: opened ntfs inode, which contains that attribute + * @attr: attribute record to add to attribute list * * 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 to function. - * ENOMEM - Not enough memory to allocate necessary buffers. - * EIO - I/O error occurred or damaged filesystem. - * EEXIST - Such attribute already present in attribute list. + * EINVAL - Invalid arguments passed to function. + * ENOMEM - Not enough memory to allocate necessary buffers. + * EIO - I/O error occurred or damaged filesystem. + * EEXIST - Such attribute already present in attribute list. */ -int ntfs_attrlist_entry_add( ntfs_inode *ni, ATTR_RECORD *attr ) +int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr) { - ATTR_LIST_ENTRY *ale; - MFT_REF mref; - ntfs_attr *na = NULL; - ntfs_attr_search_ctx *ctx; - u8 *new_al; - int entry_len, entry_offset, err; + ATTR_LIST_ENTRY *ale; + MFT_REF mref; + ntfs_attr *na = NULL; + ntfs_attr_search_ctx *ctx; + u8 *new_al; + int entry_len, entry_offset, err; - ntfs_log_trace( "Entering for inode 0x%llx, attr 0x%x.\n", - ( long long ) ni->mft_no, - ( unsigned ) le32_to_cpu( attr->type ) ); + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", + (long long) ni->mft_no, + (unsigned) le32_to_cpu(attr->type)); - if ( !ni || !attr ) - { - ntfs_log_trace( "Invalid arguments.\n" ); - errno = EINVAL; - return -1; - } + if (!ni || !attr) { + ntfs_log_trace("Invalid arguments.\n"); + errno = EINVAL; + return -1; + } - mref = MK_LE_MREF( ni->mft_no, le16_to_cpu( ni->mrec->sequence_number ) ); + mref = MK_LE_MREF(ni->mft_no, le16_to_cpu(ni->mrec->sequence_number)); - if ( ni->nr_extents == -1 ) - ni = ni->base_ni; + if (ni->nr_extents == -1) + ni = ni->base_ni; - if ( !NInoAttrList( ni ) ) - { - ntfs_log_trace( "Attribute list isn't present.\n" ); - errno = ENOENT; - return -1; - } + if (!NInoAttrList(ni)) { + ntfs_log_trace("Attribute list isn't present.\n"); + errno = ENOENT; + return -1; + } - /* Determine size and allocate memory for new attribute list. */ - entry_len = ( sizeof( ATTR_LIST_ENTRY ) + sizeof( ntfschar ) * - attr->name_length + 7 ) & ~7; - new_al = ntfs_calloc( ni->attr_list_size + entry_len ); - if ( !new_al ) - return -1; + /* Determine size and allocate memory for new attribute list. */ + entry_len = (sizeof(ATTR_LIST_ENTRY) + sizeof(ntfschar) * + attr->name_length + 7) & ~7; + new_al = ntfs_calloc(ni->attr_list_size + entry_len); + if (!new_al) + return -1; - /* Find place for the new entry. */ - ctx = ntfs_attr_get_search_ctx( ni, NULL ); - if ( !ctx ) - { - err = errno; - goto err_out; - } - if ( !ntfs_attr_lookup( attr->type, ( attr->name_length ) ? ( ntfschar* ) - ( ( u8* )attr + le16_to_cpu( attr->name_offset ) ) : - AT_UNNAMED, attr->name_length, CASE_SENSITIVE, - ( attr->non_resident ) ? le64_to_cpu( attr->lowest_vcn ) : - 0, ( attr->non_resident ) ? NULL : ( ( u8* )attr + - le16_to_cpu( attr->value_offset ) ), ( attr->non_resident ) ? - 0 : le32_to_cpu( attr->value_length ), ctx ) ) - { - /* Found some extent, check it to be before new extent. */ - if ( ctx->al_entry->lowest_vcn == attr->lowest_vcn ) - { - err = EEXIST; - ntfs_log_trace( "Such attribute already present in the " - "attribute list.\n" ); - ntfs_attr_put_search_ctx( ctx ); - goto err_out; - } - /* Add new entry after this extent. */ - ale = ( ATTR_LIST_ENTRY* )( ( u8* )ctx->al_entry + - le16_to_cpu( ctx->al_entry->length ) ); - } - else - { - /* Check for real errors. */ - if ( errno != ENOENT ) - { - err = errno; - ntfs_log_trace( "Attribute lookup failed.\n" ); - ntfs_attr_put_search_ctx( ctx ); - goto err_out; - } - /* No previous extents found. */ - ale = ctx->al_entry; - } - /* Don't need it anymore, @ctx->al_entry points to @ni->attr_list. */ - ntfs_attr_put_search_ctx( ctx ); + /* Find place for the new entry. */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) { + err = errno; + goto err_out; + } + if (!ntfs_attr_lookup(attr->type, (attr->name_length) ? (ntfschar*) + ((u8*)attr + le16_to_cpu(attr->name_offset)) : + AT_UNNAMED, attr->name_length, CASE_SENSITIVE, + (attr->non_resident) ? le64_to_cpu(attr->lowest_vcn) : + 0, (attr->non_resident) ? NULL : ((u8*)attr + + le16_to_cpu(attr->value_offset)), (attr->non_resident) ? + 0 : le32_to_cpu(attr->value_length), ctx)) { + /* Found some extent, check it to be before new extent. */ + if (ctx->al_entry->lowest_vcn == attr->lowest_vcn) { + err = EEXIST; + ntfs_log_trace("Such attribute already present in the " + "attribute list.\n"); + ntfs_attr_put_search_ctx(ctx); + goto err_out; + } + /* Add new entry after this extent. */ + ale = (ATTR_LIST_ENTRY*)((u8*)ctx->al_entry + + le16_to_cpu(ctx->al_entry->length)); + } else { + /* Check for real errors. */ + if (errno != ENOENT) { + err = errno; + ntfs_log_trace("Attribute lookup failed.\n"); + ntfs_attr_put_search_ctx(ctx); + goto err_out; + } + /* No previous extents found. */ + ale = ctx->al_entry; + } + /* Don't need it anymore, @ctx->al_entry points to @ni->attr_list. */ + ntfs_attr_put_search_ctx(ctx); - /* Determine new entry offset. */ - entry_offset = ( ( u8 * )ale - ni->attr_list ); - /* Set pointer to new entry. */ - ale = ( ATTR_LIST_ENTRY * )( new_al + entry_offset ); - /* Zero it to fix valgrind warning. */ - memset( ale, 0, entry_len ); - /* Form new entry. */ - ale->type = attr->type; - ale->length = cpu_to_le16( entry_len ); - ale->name_length = attr->name_length; - ale->name_offset = offsetof( ATTR_LIST_ENTRY, name ); - if ( attr->non_resident ) - ale->lowest_vcn = attr->lowest_vcn; - else - ale->lowest_vcn = 0; - ale->mft_reference = mref; - ale->instance = attr->instance; - memcpy( ale->name, ( u8 * )attr + le16_to_cpu( attr->name_offset ), - attr->name_length * sizeof( ntfschar ) ); + /* Determine new entry offset. */ + entry_offset = ((u8 *)ale - ni->attr_list); + /* Set pointer to new entry. */ + ale = (ATTR_LIST_ENTRY *)(new_al + entry_offset); + /* Zero it to fix valgrind warning. */ + memset(ale, 0, entry_len); + /* Form new entry. */ + ale->type = attr->type; + ale->length = cpu_to_le16(entry_len); + ale->name_length = attr->name_length; + ale->name_offset = offsetof(ATTR_LIST_ENTRY, name); + if (attr->non_resident) + ale->lowest_vcn = attr->lowest_vcn; + else + ale->lowest_vcn = 0; + ale->mft_reference = mref; + ale->instance = attr->instance; + memcpy(ale->name, (u8 *)attr + le16_to_cpu(attr->name_offset), + attr->name_length * sizeof(ntfschar)); - /* Resize $ATTRIBUTE_LIST to new length. */ - na = ntfs_attr_open( ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0 ); - if ( !na ) - { - err = errno; - ntfs_log_trace( "Failed to open $ATTRIBUTE_LIST attribute.\n" ); - goto err_out; - } - if ( ntfs_attr_truncate( na, ni->attr_list_size + entry_len ) ) - { - err = errno; - ntfs_log_trace( "$ATTRIBUTE_LIST resize failed.\n" ); - goto err_out; - } + /* Resize $ATTRIBUTE_LIST to new length. */ + na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); + if (!na) { + err = errno; + ntfs_log_trace("Failed to open $ATTRIBUTE_LIST attribute.\n"); + goto err_out; + } + if (ntfs_attr_truncate(na, ni->attr_list_size + entry_len)) { + err = errno; + ntfs_log_trace("$ATTRIBUTE_LIST resize failed.\n"); + goto err_out; + } - /* Copy entries from old attribute list to new. */ - memcpy( new_al, ni->attr_list, entry_offset ); - memcpy( new_al + entry_offset + entry_len, ni->attr_list + - entry_offset, ni->attr_list_size - entry_offset ); + /* Copy entries from old attribute list to new. */ + memcpy(new_al, ni->attr_list, entry_offset); + memcpy(new_al + entry_offset + entry_len, ni->attr_list + + entry_offset, ni->attr_list_size - entry_offset); - /* Set new runlist. */ - free( ni->attr_list ); - ni->attr_list = new_al; - ni->attr_list_size = ni->attr_list_size + entry_len; - NInoAttrListSetDirty( ni ); - /* Done! */ - ntfs_attr_close( na ); - return 0; + /* Set new runlist. */ + free(ni->attr_list); + ni->attr_list = new_al; + ni->attr_list_size = ni->attr_list_size + entry_len; + NInoAttrListSetDirty(ni); + /* Done! */ + ntfs_attr_close(na); + return 0; err_out: - if ( na ) - ntfs_attr_close( na ); - free( new_al ); - errno = err; - return -1; + if (na) + ntfs_attr_close(na); + free(new_al); + errno = err; + return -1; } /** * ntfs_attrlist_entry_rm - remove an attribute list attribute entry - * @ctx: attribute search context describing the attribute list entry + * @ctx: attribute search context describing the attribute list entry * * Remove the attribute list entry @ctx->al_entry from the attribute list. * * Return 0 on success and -1 on error with errno set to the error code. */ -int ntfs_attrlist_entry_rm( ntfs_attr_search_ctx *ctx ) +int ntfs_attrlist_entry_rm(ntfs_attr_search_ctx *ctx) { - u8 *new_al; - int new_al_len; - ntfs_inode *base_ni; - ntfs_attr *na; - ATTR_LIST_ENTRY *ale; - int err; + u8 *new_al; + int new_al_len; + ntfs_inode *base_ni; + ntfs_attr *na; + ATTR_LIST_ENTRY *ale; + int err; - if ( !ctx || !ctx->ntfs_ino || !ctx->al_entry ) - { - ntfs_log_trace( "Invalid arguments.\n" ); - errno = EINVAL; - return -1; - } + if (!ctx || !ctx->ntfs_ino || !ctx->al_entry) { + ntfs_log_trace("Invalid arguments.\n"); + errno = EINVAL; + return -1; + } - if ( ctx->base_ntfs_ino ) - base_ni = ctx->base_ntfs_ino; - else - base_ni = ctx->ntfs_ino; - ale = ctx->al_entry; + if (ctx->base_ntfs_ino) + base_ni = ctx->base_ntfs_ino; + else + base_ni = ctx->ntfs_ino; + ale = ctx->al_entry; - ntfs_log_trace( "Entering for inode 0x%llx, attr 0x%x, lowest_vcn %lld.\n", - ( long long ) ctx->ntfs_ino->mft_no, - ( unsigned ) le32_to_cpu( ctx->al_entry->type ), - ( long long ) le64_to_cpu( ctx->al_entry->lowest_vcn ) ); + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, lowest_vcn %lld.\n", + (long long) ctx->ntfs_ino->mft_no, + (unsigned) le32_to_cpu(ctx->al_entry->type), + (long long) le64_to_cpu(ctx->al_entry->lowest_vcn)); - if ( !NInoAttrList( base_ni ) ) - { - ntfs_log_trace( "Attribute list isn't present.\n" ); - errno = ENOENT; - return -1; - } + if (!NInoAttrList(base_ni)) { + ntfs_log_trace("Attribute list isn't present.\n"); + errno = ENOENT; + return -1; + } - /* Allocate memory for new attribute list. */ - new_al_len = base_ni->attr_list_size - le16_to_cpu( ale->length ); - new_al = ntfs_calloc( new_al_len ); - if ( !new_al ) - return -1; + /* Allocate memory for new attribute list. */ + new_al_len = base_ni->attr_list_size - le16_to_cpu(ale->length); + new_al = ntfs_calloc(new_al_len); + if (!new_al) + return -1; - /* Reisze $ATTRIBUTE_LIST to new length. */ - na = ntfs_attr_open( base_ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0 ); - if ( !na ) - { - err = errno; - ntfs_log_trace( "Failed to open $ATTRIBUTE_LIST attribute.\n" ); - goto err_out; - } - if ( ntfs_attr_truncate( na, new_al_len ) ) - { - err = errno; - ntfs_log_trace( "$ATTRIBUTE_LIST resize failed.\n" ); - goto err_out; - } + /* Reisze $ATTRIBUTE_LIST to new length. */ + na = ntfs_attr_open(base_ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); + if (!na) { + err = errno; + ntfs_log_trace("Failed to open $ATTRIBUTE_LIST attribute.\n"); + goto err_out; + } + if (ntfs_attr_truncate(na, new_al_len)) { + err = errno; + ntfs_log_trace("$ATTRIBUTE_LIST resize failed.\n"); + goto err_out; + } - /* Copy entries from old attribute list to new. */ - memcpy( new_al, base_ni->attr_list, ( u8* )ale - base_ni->attr_list ); - memcpy( new_al + ( ( u8* )ale - base_ni->attr_list ), ( u8* )ale + le16_to_cpu( - ale->length ), new_al_len - ( ( u8* )ale - base_ni->attr_list ) ); + /* Copy entries from old attribute list to new. */ + memcpy(new_al, base_ni->attr_list, (u8*)ale - base_ni->attr_list); + memcpy(new_al + ((u8*)ale - base_ni->attr_list), (u8*)ale + le16_to_cpu( + ale->length), new_al_len - ((u8*)ale - base_ni->attr_list)); - /* Set new runlist. */ - free( base_ni->attr_list ); - base_ni->attr_list = new_al; - base_ni->attr_list_size = new_al_len; - NInoAttrListSetDirty( base_ni ); - /* Done! */ - ntfs_attr_close( na ); - return 0; + /* Set new runlist. */ + free(base_ni->attr_list); + base_ni->attr_list = new_al; + base_ni->attr_list_size = new_al_len; + NInoAttrListSetDirty(base_ni); + /* Done! */ + ntfs_attr_close(na); + return 0; err_out: - if ( na ) - ntfs_attr_close( na ); - free( new_al ); - errno = err; - return -1; + if (na) + ntfs_attr_close(na); + free(new_al); + errno = err; + return -1; } diff --git a/source/libntfs/attrlist.h b/source/libntfs/attrlist.h index b1e513d9..2952e48b 100644 --- a/source/libntfs/attrlist.h +++ b/source/libntfs/attrlist.h @@ -1,6 +1,6 @@ /* - * attrlist.h - Exports for attribute list attribute handling. - * Originated from Linux-NTFS project. + * attrlist.h - Exports for attribute list attribute handling. + * Originated from Linux-NTFS project. * * Copyright (c) 2004 Anton Altaparmakov * Copyright (c) 2004 Yura Pakhuchiy @@ -26,26 +26,26 @@ #include "attrib.h" -extern int ntfs_attrlist_need( ntfs_inode *ni ); +extern int ntfs_attrlist_need(ntfs_inode *ni); -extern int ntfs_attrlist_entry_add( ntfs_inode *ni, ATTR_RECORD *attr ); -extern int ntfs_attrlist_entry_rm( ntfs_attr_search_ctx *ctx ); +extern int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr); +extern int ntfs_attrlist_entry_rm(ntfs_attr_search_ctx *ctx); /** * ntfs_attrlist_mark_dirty - set the attribute list dirty - * @ni: ntfs inode which base inode contain dirty attribute list + * @ni: ntfs inode which base inode contain dirty attribute list * * Set the attribute list dirty so it is written out later (at the latest at * ntfs_inode_close() time). * * This function cannot fail. */ -static __inline__ void ntfs_attrlist_mark_dirty( ntfs_inode *ni ) +static __inline__ void ntfs_attrlist_mark_dirty(ntfs_inode *ni) { - if ( ni->nr_extents == -1 ) - NInoAttrListSetDirty( ni->base_ni ); - else - NInoAttrListSetDirty( ni ); + if (ni->nr_extents == -1) + NInoAttrListSetDirty(ni->base_ni); + else + NInoAttrListSetDirty(ni); } #endif /* defined _NTFS_ATTRLIST_H */ diff --git a/source/libntfs/bit_ops.h b/source/libntfs/bit_ops.h index e43202a4..762be0b3 100644 --- a/source/libntfs/bit_ops.h +++ b/source/libntfs/bit_ops.h @@ -3,7 +3,7 @@ Functions for dealing with conversion of data between types Copyright (c) 2006 Michael "Chishm" Chisholm - + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -34,28 +34,24 @@ /*----------------------------------------------------------------- Functions to deal with little endian values stored in uint8_t arrays -----------------------------------------------------------------*/ -static inline uint16_t u8array_to_u16 ( const uint8_t* item, int offset ) -{ - return ( item[offset] | ( item[offset + 1] << 8 ) ); +static inline uint16_t u8array_to_u16 (const uint8_t* item, int offset) { + return ( item[offset] | (item[offset + 1] << 8)); } -static inline uint32_t u8array_to_u32 ( const uint8_t* item, int offset ) -{ - return ( item[offset] | ( item[offset + 1] << 8 ) | ( item[offset + 2] << 16 ) | ( item[offset + 3] << 24 ) ); +static inline uint32_t u8array_to_u32 (const uint8_t* item, int offset) { + return ( item[offset] | (item[offset + 1] << 8) | (item[offset + 2] << 16) | (item[offset + 3] << 24)); } -static inline void u16_to_u8array ( uint8_t* item, int offset, uint16_t value ) -{ - item[offset] = ( uint8_t ) value; - item[offset + 1] = ( uint8_t )( value >> 8 ); +static inline void u16_to_u8array (uint8_t* item, int offset, uint16_t value) { + item[offset] = (uint8_t) value; + item[offset + 1] = (uint8_t)(value >> 8); } -static inline void u32_to_u8array ( uint8_t* item, int offset, uint32_t value ) -{ - item[offset] = ( uint8_t ) value; - item[offset + 1] = ( uint8_t )( value >> 8 ); - item[offset + 2] = ( uint8_t )( value >> 16 ); - item[offset + 3] = ( uint8_t )( value >> 24 ); +static inline void u32_to_u8array (uint8_t* item, int offset, uint32_t value) { + item[offset] = (uint8_t) value; + item[offset + 1] = (uint8_t)(value >> 8); + item[offset + 2] = (uint8_t)(value >> 16); + item[offset + 3] = (uint8_t)(value >> 24); } #endif // _BIT_OPS_H diff --git a/source/libntfs/bitmap.c b/source/libntfs/bitmap.c index 6bbe18b8..65162a29 100644 --- a/source/libntfs/bitmap.c +++ b/source/libntfs/bitmap.c @@ -47,268 +47,254 @@ /** * ntfs_bit_set - set a bit in a field of bits - * @bitmap: field of bits - * @bit: bit to set - * @new_value: value to set bit to (0 or 1) + * @bitmap: field of bits + * @bit: bit to set + * @new_value: value to set bit to (0 or 1) * * Set the bit @bit in the @bitmap to @new_value. Ignore all errors. */ -void ntfs_bit_set( u8 *bitmap, const u64 bit, const u8 new_value ) +void ntfs_bit_set(u8 *bitmap, const u64 bit, const u8 new_value) { - if ( !bitmap || new_value > 1 ) - return; - if ( !new_value ) - bitmap[bit >> 3] &= ~( 1 << ( bit & 7 ) ); - else - bitmap[bit >> 3] |= ( 1 << ( bit & 7 ) ); + if (!bitmap || new_value > 1) + return; + if (!new_value) + bitmap[bit >> 3] &= ~(1 << (bit & 7)); + else + bitmap[bit >> 3] |= (1 << (bit & 7)); } /** * ntfs_bit_get - get value of a bit in a field of bits - * @bitmap: field of bits - * @bit: bit to get + * @bitmap: field of bits + * @bit: bit to get * * Get and return the value of the bit @bit in @bitmap (0 or 1). * Return -1 on error. */ -char ntfs_bit_get( const u8 *bitmap, const u64 bit ) +char ntfs_bit_get(const u8 *bitmap, const u64 bit) { - if ( !bitmap ) - return -1; - return ( bitmap[bit >> 3] >> ( bit & 7 ) ) & 1; + if (!bitmap) + return -1; + return (bitmap[bit >> 3] >> (bit & 7)) & 1; } /** * ntfs_bit_get_and_set - get value of a bit in a field of bits and set it - * @bitmap: field of bits - * @bit: bit to get/set - * @new_value: value to set bit to (0 or 1) + * @bitmap: field of bits + * @bit: bit to get/set + * @new_value: value to set bit to (0 or 1) * * Return the value of the bit @bit and set it to @new_value (0 or 1). * Return -1 on error. */ -char ntfs_bit_get_and_set( u8 *bitmap, const u64 bit, const u8 new_value ) +char ntfs_bit_get_and_set(u8 *bitmap, const u64 bit, const u8 new_value) { - register u8 old_bit, shift; + register u8 old_bit, shift; - if ( !bitmap || new_value > 1 ) - return -1; - shift = bit & 7; - old_bit = ( bitmap[bit >> 3] >> shift ) & 1; - if ( new_value != old_bit ) - bitmap[bit >> 3] ^= 1 << shift; - return old_bit; + if (!bitmap || new_value > 1) + return -1; + shift = bit & 7; + old_bit = (bitmap[bit >> 3] >> shift) & 1; + if (new_value != old_bit) + bitmap[bit >> 3] ^= 1 << shift; + return old_bit; } /** * ntfs_bitmap_set_bits_in_run - set a run of bits in a bitmap to a value - * @na: attribute containing the bitmap - * @start_bit: first bit to set - * @count: number of bits to set - * @value: value to set the bits to (i.e. 0 or 1) + * @na: attribute containing the bitmap + * @start_bit: first bit to set + * @count: number of bits to set + * @value: value to set the bits to (i.e. 0 or 1) * * Set @count bits starting at bit @start_bit in the bitmap described by the * attribute @na to @value, where @value is either 0 or 1. * * On success return 0 and on error return -1 with errno set to the error code. */ -static int ntfs_bitmap_set_bits_in_run( ntfs_attr *na, s64 start_bit, - s64 count, int value ) +static int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit, + s64 count, int value) { - s64 bufsize, br; - u8 *buf, *lastbyte_buf; - int bit, firstbyte, lastbyte, lastbyte_pos, tmp, ret = -1; + s64 bufsize, br; + u8 *buf, *lastbyte_buf; + int bit, firstbyte, lastbyte, lastbyte_pos, tmp, ret = -1; - if ( !na || start_bit < 0 || count < 0 ) - { - errno = EINVAL; - ntfs_log_perror( "%s: Invalid argument (%p, %lld, %lld)", - __FUNCTION__, na, ( long long )start_bit, ( long long )count ); - return -1; - } + if (!na || start_bit < 0 || count < 0) { + errno = EINVAL; + ntfs_log_perror("%s: Invalid argument (%p, %lld, %lld)", + __FUNCTION__, na, (long long)start_bit, (long long)count); + return -1; + } - bit = start_bit & 7; - if ( bit ) - firstbyte = 1; - else - firstbyte = 0; + bit = start_bit & 7; + if (bit) + firstbyte = 1; + else + firstbyte = 0; - /* Calculate the required buffer size in bytes, capping it at 8kiB. */ - bufsize = ( ( count - ( bit ? 8 - bit : 0 ) + 7 ) >> 3 ) + firstbyte; - if ( bufsize > 8192 ) - bufsize = 8192; + /* Calculate the required buffer size in bytes, capping it at 8kiB. */ + bufsize = ((count - (bit ? 8 - bit : 0) + 7) >> 3) + firstbyte; + if (bufsize > 8192) + bufsize = 8192; - buf = ntfs_malloc( bufsize ); - if ( !buf ) - return -1; + buf = ntfs_malloc(bufsize); + if (!buf) + return -1; + + /* Depending on @value, zero or set all bits in the allocated buffer. */ + memset(buf, value ? 0xff : 0, bufsize); - /* Depending on @value, zero or set all bits in the allocated buffer. */ - memset( buf, value ? 0xff : 0, bufsize ); + /* If there is a first partial byte... */ + if (bit) { + /* read it in... */ + br = ntfs_attr_pread(na, start_bit >> 3, 1, buf); + if (br != 1) { + if (br >= 0) + errno = EIO; + goto free_err_out; + } + /* and set or clear the appropriate bits in it. */ + while ((bit & 7) && count--) { + if (value) + *buf |= 1 << bit++; + else + *buf &= ~(1 << bit++); + } + /* Update @start_bit to the new position. */ + start_bit = (start_bit + 7) & ~7; + } - /* If there is a first partial byte... */ - if ( bit ) - { - /* read it in... */ - br = ntfs_attr_pread( na, start_bit >> 3, 1, buf ); - if ( br != 1 ) - { - if ( br >= 0 ) - errno = EIO; - goto free_err_out; - } - /* and set or clear the appropriate bits in it. */ - while ( ( bit & 7 ) && count-- ) - { - if ( value ) - *buf |= 1 << bit++; - else - *buf &= ~( 1 << bit++ ); - } - /* Update @start_bit to the new position. */ - start_bit = ( start_bit + 7 ) & ~7; - } + /* Loop until @count reaches zero. */ + lastbyte = 0; + lastbyte_buf = NULL; + bit = count & 7; + do { + /* If there is a last partial byte... */ + if (count > 0 && bit) { + lastbyte_pos = ((count + 7) >> 3) + firstbyte; + if (!lastbyte_pos) { + // FIXME: Eeek! BUG! + ntfs_log_error("Lastbyte is zero. Leaving " + "inconsistent metadata.\n"); + errno = EIO; + goto free_err_out; + } + /* and it is in the currently loaded bitmap window... */ + if (lastbyte_pos <= bufsize) { + lastbyte_buf = buf + lastbyte_pos - 1; - /* Loop until @count reaches zero. */ - lastbyte = 0; - lastbyte_buf = NULL; - bit = count & 7; - do - { - /* If there is a last partial byte... */ - if ( count > 0 && bit ) - { - lastbyte_pos = ( ( count + 7 ) >> 3 ) + firstbyte; - if ( !lastbyte_pos ) - { - // FIXME: Eeek! BUG! - ntfs_log_error( "Lastbyte is zero. Leaving " - "inconsistent metadata.\n" ); - errno = EIO; - goto free_err_out; - } - /* and it is in the currently loaded bitmap window... */ - if ( lastbyte_pos <= bufsize ) - { - lastbyte_buf = buf + lastbyte_pos - 1; + /* read the byte in... */ + br = ntfs_attr_pread(na, (start_bit + count) >> + 3, 1, lastbyte_buf); + if (br != 1) { + // FIXME: Eeek! We need rollback! (AIA) + if (br >= 0) + errno = EIO; + ntfs_log_perror("Reading of last byte " + "failed (%lld). Leaving inconsistent " + "metadata", (long long)br); + goto free_err_out; + } + /* and set/clear the appropriate bits in it. */ + while (bit && count--) { + if (value) + *lastbyte_buf |= 1 << --bit; + else + *lastbyte_buf &= ~(1 << --bit); + } + /* We don't want to come back here... */ + bit = 0; + /* We have a last byte that we have handled. */ + lastbyte = 1; + } + } - /* read the byte in... */ - br = ntfs_attr_pread( na, ( start_bit + count ) >> - 3, 1, lastbyte_buf ); - if ( br != 1 ) - { - // FIXME: Eeek! We need rollback! (AIA) - if ( br >= 0 ) - errno = EIO; - ntfs_log_perror( "Reading of last byte " - "failed (%lld). Leaving inconsistent " - "metadata", ( long long )br ); - goto free_err_out; - } - /* and set/clear the appropriate bits in it. */ - while ( bit && count-- ) - { - if ( value ) - *lastbyte_buf |= 1 << --bit; - else - *lastbyte_buf &= ~( 1 << --bit ); - } - /* We don't want to come back here... */ - bit = 0; - /* We have a last byte that we have handled. */ - lastbyte = 1; - } - } + /* Write the prepared buffer to disk. */ + tmp = (start_bit >> 3) - firstbyte; + br = ntfs_attr_pwrite(na, tmp, bufsize, buf); + if (br != bufsize) { + // FIXME: Eeek! We need rollback! (AIA) + if (br >= 0) + errno = EIO; + ntfs_log_perror("Failed to write buffer to bitmap " + "(%lld != %lld). Leaving inconsistent metadata", + (long long)br, (long long)bufsize); + goto free_err_out; + } - /* Write the prepared buffer to disk. */ - tmp = ( start_bit >> 3 ) - firstbyte; - br = ntfs_attr_pwrite( na, tmp, bufsize, buf ); - if ( br != bufsize ) - { - // FIXME: Eeek! We need rollback! (AIA) - if ( br >= 0 ) - errno = EIO; - ntfs_log_perror( "Failed to write buffer to bitmap " - "(%lld != %lld). Leaving inconsistent metadata", - ( long long )br, ( long long )bufsize ); - goto free_err_out; - } - - /* Update counters. */ - tmp = ( bufsize - firstbyte - lastbyte ) << 3; - if ( firstbyte ) - { - firstbyte = 0; - /* - * Re-set the partial first byte so a subsequent write - * of the buffer does not have stale, incorrect bits. - */ - *buf = value ? 0xff : 0; - } - start_bit += tmp; - count -= tmp; - if ( bufsize > ( tmp = ( count + 7 ) >> 3 ) ) - bufsize = tmp; - - if ( lastbyte && count != 0 ) - { - // FIXME: Eeek! BUG! - ntfs_log_error( "Last buffer but count is not zero " - "(%lld). Leaving inconsistent metadata.\n", - ( long long )count ); - errno = EIO; - goto free_err_out; - } - } - while ( count > 0 ); - - ret = 0; + /* Update counters. */ + tmp = (bufsize - firstbyte - lastbyte) << 3; + if (firstbyte) { + firstbyte = 0; + /* + * Re-set the partial first byte so a subsequent write + * of the buffer does not have stale, incorrect bits. + */ + *buf = value ? 0xff : 0; + } + start_bit += tmp; + count -= tmp; + if (bufsize > (tmp = (count + 7) >> 3)) + bufsize = tmp; + if (lastbyte && count != 0) { + // FIXME: Eeek! BUG! + ntfs_log_error("Last buffer but count is not zero " + "(%lld). Leaving inconsistent metadata.\n", + (long long)count); + errno = EIO; + goto free_err_out; + } + } while (count > 0); + + ret = 0; + free_err_out: - free( buf ); - return ret; + free(buf); + return ret; } /** * ntfs_bitmap_set_run - set a run of bits in a bitmap - * @na: attribute containing the bitmap - * @start_bit: first bit to set - * @count: number of bits to set + * @na: attribute containing the bitmap + * @start_bit: first bit to set + * @count: number of bits to set * * Set @count bits starting at bit @start_bit in the bitmap described by the * attribute @na. * * On success return 0 and on error return -1 with errno set to the error code. */ -int ntfs_bitmap_set_run( ntfs_attr *na, s64 start_bit, s64 count ) +int ntfs_bitmap_set_run(ntfs_attr *na, s64 start_bit, s64 count) { - int ret; - - ntfs_log_enter( "Set from bit %lld, count %lld\n", - ( long long )start_bit, ( long long )count ); - ret = ntfs_bitmap_set_bits_in_run( na, start_bit, count, 1 ); - ntfs_log_leave( "\n" ); - return ret; + int ret; + + ntfs_log_enter("Set from bit %lld, count %lld\n", + (long long)start_bit, (long long)count); + ret = ntfs_bitmap_set_bits_in_run(na, start_bit, count, 1); + ntfs_log_leave("\n"); + return ret; } /** * ntfs_bitmap_clear_run - clear a run of bits in a bitmap - * @na: attribute containing the bitmap - * @start_bit: first bit to clear - * @count: number of bits to clear + * @na: attribute containing the bitmap + * @start_bit: first bit to clear + * @count: number of bits to clear * * Clear @count bits starting at bit @start_bit in the bitmap described by the * attribute @na. * * On success return 0 and on error return -1 with errno set to the error code. */ -int ntfs_bitmap_clear_run( ntfs_attr *na, s64 start_bit, s64 count ) +int ntfs_bitmap_clear_run(ntfs_attr *na, s64 start_bit, s64 count) { - int ret; - - ntfs_log_enter( "Clear from bit %lld, count %lld\n", - ( long long )start_bit, ( long long )count ); - ret = ntfs_bitmap_set_bits_in_run( na, start_bit, count, 0 ); - ntfs_log_leave( "\n" ); - return ret; + int ret; + + ntfs_log_enter("Clear from bit %lld, count %lld\n", + (long long)start_bit, (long long)count); + ret = ntfs_bitmap_set_bits_in_run(na, start_bit, count, 0); + ntfs_log_leave("\n"); + return ret; } diff --git a/source/libntfs/bitmap.h b/source/libntfs/bitmap.h index 70cddd97..10b5f6c5 100644 --- a/source/libntfs/bitmap.h +++ b/source/libntfs/bitmap.h @@ -36,38 +36,38 @@ * size of the bitmap. */ -extern void ntfs_bit_set( u8 *bitmap, const u64 bit, const u8 new_value ); -extern char ntfs_bit_get( const u8 *bitmap, const u64 bit ); -extern char ntfs_bit_get_and_set( u8 *bitmap, const u64 bit, const u8 new_value ); -extern int ntfs_bitmap_set_run( ntfs_attr *na, s64 start_bit, s64 count ); -extern int ntfs_bitmap_clear_run( ntfs_attr *na, s64 start_bit, s64 count ); +extern void ntfs_bit_set(u8 *bitmap, const u64 bit, const u8 new_value); +extern char ntfs_bit_get(const u8 *bitmap, const u64 bit); +extern char ntfs_bit_get_and_set(u8 *bitmap, const u64 bit, const u8 new_value); +extern int ntfs_bitmap_set_run(ntfs_attr *na, s64 start_bit, s64 count); +extern int ntfs_bitmap_clear_run(ntfs_attr *na, s64 start_bit, s64 count); /** * ntfs_bitmap_set_bit - set a bit in a bitmap - * @na: attribute containing the bitmap - * @bit: bit to set + * @na: attribute containing the bitmap + * @bit: bit to set * * Set the @bit in the bitmap described by the attribute @na. * * On success return 0 and on error return -1 with errno set to the error code. */ -static __inline__ int ntfs_bitmap_set_bit( ntfs_attr *na, s64 bit ) +static __inline__ int ntfs_bitmap_set_bit(ntfs_attr *na, s64 bit) { - return ntfs_bitmap_set_run( na, bit, 1 ); + return ntfs_bitmap_set_run(na, bit, 1); } /** * ntfs_bitmap_clear_bit - clear a bit in a bitmap - * @na: attribute containing the bitmap - * @bit: bit to clear + * @na: attribute containing the bitmap + * @bit: bit to clear * * Clear @bit in the bitmap described by the attribute @na. * * On success return 0 and on error return -1 with errno set to the error code. */ -static __inline__ int ntfs_bitmap_clear_bit( ntfs_attr *na, s64 bit ) +static __inline__ int ntfs_bitmap_clear_bit(ntfs_attr *na, s64 bit) { - return ntfs_bitmap_clear_run( na, bit, 1 ); + return ntfs_bitmap_clear_run(na, bit, 1); } /* @@ -76,9 +76,9 @@ static __inline__ int ntfs_bitmap_clear_bit( ntfs_attr *na, s64 bit ) * @word: value to rotate * @shift: bits to roll */ -static __inline__ u32 ntfs_rol32( u32 word, unsigned int shift ) +static __inline__ u32 ntfs_rol32(u32 word, unsigned int shift) { - return ( word << shift ) | ( word >> ( 32 - shift ) ); + return (word << shift) | (word >> (32 - shift)); } /* @@ -87,9 +87,9 @@ static __inline__ u32 ntfs_rol32( u32 word, unsigned int shift ) * @word: value to rotate * @shift: bits to roll */ -static __inline__ u32 ntfs_ror32( u32 word, unsigned int shift ) +static __inline__ u32 ntfs_ror32(u32 word, unsigned int shift) { - return ( word >> shift ) | ( word << ( 32 - shift ) ); + return (word >> shift) | (word << (32 - shift)); } #endif /* defined _NTFS_BITMAP_H */ diff --git a/source/libntfs/bootsect.c b/source/libntfs/bootsect.c index a97e36dd..e9bea370 100644 --- a/source/libntfs/bootsect.c +++ b/source/libntfs/bootsect.c @@ -45,8 +45,8 @@ /** * ntfs_boot_sector_is_ntfs - check if buffer contains a valid ntfs boot sector - * @b: buffer containing putative boot sector to analyze - * @silent: if zero, output progress messages to stderr + * @b: buffer containing putative boot sector to analyze + * @silent: if zero, output progress messages to stderr * * Check if the buffer @b contains a valid ntfs boot sector. The buffer @b * must be at least 512 bytes in size. @@ -57,244 +57,229 @@ * * Return TRUE if @b contains a valid ntfs boot sector and FALSE if not. */ -BOOL ntfs_boot_sector_is_ntfs( NTFS_BOOT_SECTOR *b ) +BOOL ntfs_boot_sector_is_ntfs(NTFS_BOOT_SECTOR *b) { - u32 i; - BOOL ret = FALSE; + u32 i; + BOOL ret = FALSE; - ntfs_log_debug( "Beginning bootsector check.\n" ); + ntfs_log_debug("Beginning bootsector check.\n"); - ntfs_log_debug( "Checking OEMid, NTFS signature.\n" ); - if ( b->oem_id != cpu_to_le64( 0x202020205346544eULL ) ) /* "NTFS " */ - { - ntfs_log_error( "NTFS signature is missing.\n" ); - goto not_ntfs; - } + ntfs_log_debug("Checking OEMid, NTFS signature.\n"); + if (b->oem_id != cpu_to_le64(0x202020205346544eULL)) { /* "NTFS " */ + ntfs_log_error("NTFS signature is missing.\n"); + goto not_ntfs; + } - ntfs_log_debug( "Checking bytes per sector.\n" ); - if ( le16_to_cpu( b->bpb.bytes_per_sector ) < 256 || - le16_to_cpu( b->bpb.bytes_per_sector ) > 4096 ) - { - ntfs_log_error( "Unexpected bytes per sector value (%d).\n", - le16_to_cpu( b->bpb.bytes_per_sector ) ); - goto not_ntfs; - } + ntfs_log_debug("Checking bytes per sector.\n"); + if (le16_to_cpu(b->bpb.bytes_per_sector) < 256 || + le16_to_cpu(b->bpb.bytes_per_sector) > 4096) { + ntfs_log_error("Unexpected bytes per sector value (%d).\n", + le16_to_cpu(b->bpb.bytes_per_sector)); + goto not_ntfs; + } - ntfs_log_debug( "Checking sectors per cluster.\n" ); - switch ( b->bpb.sectors_per_cluster ) - { - case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128: - break; - default: - ntfs_log_error( "Unexpected sectors per cluster value (%d).\n", - b->bpb.sectors_per_cluster ); - goto not_ntfs; - } + ntfs_log_debug("Checking sectors per cluster.\n"); + switch (b->bpb.sectors_per_cluster) { + case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128: + break; + default: + ntfs_log_error("Unexpected sectors per cluster value (%d).\n", + b->bpb.sectors_per_cluster); + goto not_ntfs; + } - ntfs_log_debug( "Checking cluster size.\n" ); - i = ( u32 )le16_to_cpu( b->bpb.bytes_per_sector ) * - b->bpb.sectors_per_cluster; - if ( i > 65536 ) - { - ntfs_log_error( "Unexpected cluster size (%d).\n", i ); - goto not_ntfs; - } + ntfs_log_debug("Checking cluster size.\n"); + i = (u32)le16_to_cpu(b->bpb.bytes_per_sector) * + b->bpb.sectors_per_cluster; + if (i > 65536) { + ntfs_log_error("Unexpected cluster size (%d).\n", i); + goto not_ntfs; + } - ntfs_log_debug( "Checking reserved fields are zero.\n" ); - if ( le16_to_cpu( b->bpb.reserved_sectors ) || - le16_to_cpu( b->bpb.root_entries ) || - le16_to_cpu( b->bpb.sectors ) || - le16_to_cpu( b->bpb.sectors_per_fat ) || - le32_to_cpu( b->bpb.large_sectors ) || - b->bpb.fats ) - { - ntfs_log_error( "Reserved fields aren't zero " - "(%d, %d, %d, %d, %d, %d).\n", - le16_to_cpu( b->bpb.reserved_sectors ), - le16_to_cpu( b->bpb.root_entries ), - le16_to_cpu( b->bpb.sectors ), - le16_to_cpu( b->bpb.sectors_per_fat ), - le32_to_cpu( b->bpb.large_sectors ), - b->bpb.fats ); - goto not_ntfs; - } + ntfs_log_debug("Checking reserved fields are zero.\n"); + if (le16_to_cpu(b->bpb.reserved_sectors) || + le16_to_cpu(b->bpb.root_entries) || + le16_to_cpu(b->bpb.sectors) || + le16_to_cpu(b->bpb.sectors_per_fat) || + le32_to_cpu(b->bpb.large_sectors) || + b->bpb.fats) { + ntfs_log_error("Reserved fields aren't zero " + "(%d, %d, %d, %d, %d, %d).\n", + le16_to_cpu(b->bpb.reserved_sectors), + le16_to_cpu(b->bpb.root_entries), + le16_to_cpu(b->bpb.sectors), + le16_to_cpu(b->bpb.sectors_per_fat), + le32_to_cpu(b->bpb.large_sectors), + b->bpb.fats); + goto not_ntfs; + } - ntfs_log_debug( "Checking clusters per mft record.\n" ); - if ( ( u8 )b->clusters_per_mft_record < 0xe1 || - ( u8 )b->clusters_per_mft_record > 0xf7 ) - { - switch ( b->clusters_per_mft_record ) - { - case 1: case 2: case 4: case 8: case 0x10: case 0x20: case 0x40: - break; - default: - ntfs_log_error( "Unexpected clusters per mft record " - "(%d).\n", b->clusters_per_mft_record ); - goto not_ntfs; - } - } + ntfs_log_debug("Checking clusters per mft record.\n"); + if ((u8)b->clusters_per_mft_record < 0xe1 || + (u8)b->clusters_per_mft_record > 0xf7) { + switch (b->clusters_per_mft_record) { + case 1: case 2: case 4: case 8: case 0x10: case 0x20: case 0x40: + break; + default: + ntfs_log_error("Unexpected clusters per mft record " + "(%d).\n", b->clusters_per_mft_record); + goto not_ntfs; + } + } - ntfs_log_debug( "Checking clusters per index block.\n" ); - if ( ( u8 )b->clusters_per_index_record < 0xe1 || - ( u8 )b->clusters_per_index_record > 0xf7 ) - { - switch ( b->clusters_per_index_record ) - { - case 1: case 2: case 4: case 8: case 0x10: case 0x20: case 0x40: - break; - default: - ntfs_log_error( "Unexpected clusters per index record " - "(%d).\n", b->clusters_per_index_record ); - goto not_ntfs; - } - } + ntfs_log_debug("Checking clusters per index block.\n"); + if ((u8)b->clusters_per_index_record < 0xe1 || + (u8)b->clusters_per_index_record > 0xf7) { + switch (b->clusters_per_index_record) { + case 1: case 2: case 4: case 8: case 0x10: case 0x20: case 0x40: + break; + default: + ntfs_log_error("Unexpected clusters per index record " + "(%d).\n", b->clusters_per_index_record); + goto not_ntfs; + } + } - if ( b->end_of_sector_marker != cpu_to_le16( 0xaa55 ) ) - ntfs_log_debug( "Warning: Bootsector has invalid end of sector " - "marker.\n" ); + if (b->end_of_sector_marker != cpu_to_le16(0xaa55)) + ntfs_log_debug("Warning: Bootsector has invalid end of sector " + "marker.\n"); - ntfs_log_debug( "Bootsector check completed successfully.\n" ); + ntfs_log_debug("Bootsector check completed successfully.\n"); - ret = TRUE; + ret = TRUE; not_ntfs: - return ret; + return ret; } static const char *last_sector_error = - "HINTS: Either the volume is a RAID/LDM but it wasn't setup yet,\n" - " or it was not setup correctly (e.g. by not using mdadm --build ...),\n" - " or a wrong device is tried to be mounted,\n" - " or the partition table is corrupt (partition is smaller than NTFS),\n" - " or the NTFS boot sector is corrupt (NTFS size is not valid).\n"; +"HINTS: Either the volume is a RAID/LDM but it wasn't setup yet,\n" +" or it was not setup correctly (e.g. by not using mdadm --build ...),\n" +" or a wrong device is tried to be mounted,\n" +" or the partition table is corrupt (partition is smaller than NTFS),\n" +" or the NTFS boot sector is corrupt (NTFS size is not valid).\n"; /** * ntfs_boot_sector_parse - setup an ntfs volume from an ntfs boot sector - * @vol: ntfs_volume to setup - * @bs: buffer containing ntfs boot sector to parse + * @vol: ntfs_volume to setup + * @bs: buffer containing ntfs boot sector to parse * * Parse the ntfs bootsector @bs and setup the ntfs volume @vol with the * obtained values. * * Return 0 on success or -1 on error with errno set to the error code EINVAL. */ -int ntfs_boot_sector_parse( ntfs_volume *vol, const NTFS_BOOT_SECTOR *bs ) +int ntfs_boot_sector_parse(ntfs_volume *vol, const NTFS_BOOT_SECTOR *bs) { - s64 sectors; - u8 sectors_per_cluster; - s8 c; + s64 sectors; + u8 sectors_per_cluster; + s8 c; - /* We return -1 with errno = EINVAL on error. */ - errno = EINVAL; + /* We return -1 with errno = EINVAL on error. */ + errno = EINVAL; - vol->sector_size = le16_to_cpu( bs->bpb.bytes_per_sector ); - vol->sector_size_bits = ffs( vol->sector_size ) - 1; - ntfs_log_debug( "SectorSize = 0x%x\n", vol->sector_size ); - ntfs_log_debug( "SectorSizeBits = %u\n", vol->sector_size_bits ); - /* - * The bounds checks on mft_lcn and mft_mirr_lcn (i.e. them being - * below or equal the number_of_clusters) really belong in the - * ntfs_boot_sector_is_ntfs but in this way we can just do this once. - */ - sectors_per_cluster = bs->bpb.sectors_per_cluster; - ntfs_log_debug( "SectorsPerCluster = 0x%x\n", sectors_per_cluster ); - if ( sectors_per_cluster & ( sectors_per_cluster - 1 ) ) - { - ntfs_log_error( "sectors_per_cluster (%d) is not a power of 2." - "\n", sectors_per_cluster ); - return -1; - } + vol->sector_size = le16_to_cpu(bs->bpb.bytes_per_sector); + vol->sector_size_bits = ffs(vol->sector_size) - 1; + ntfs_log_debug("SectorSize = 0x%x\n", vol->sector_size); + ntfs_log_debug("SectorSizeBits = %u\n", vol->sector_size_bits); + /* + * The bounds checks on mft_lcn and mft_mirr_lcn (i.e. them being + * below or equal the number_of_clusters) really belong in the + * ntfs_boot_sector_is_ntfs but in this way we can just do this once. + */ + sectors_per_cluster = bs->bpb.sectors_per_cluster; + ntfs_log_debug("SectorsPerCluster = 0x%x\n", sectors_per_cluster); + if (sectors_per_cluster & (sectors_per_cluster - 1)) { + ntfs_log_error("sectors_per_cluster (%d) is not a power of 2." + "\n", sectors_per_cluster); + return -1; + } + + sectors = sle64_to_cpu(bs->number_of_sectors); + ntfs_log_debug("NumberOfSectors = %lld\n", (long long)sectors); + if (!sectors) { + ntfs_log_error("Volume size is set to zero.\n"); + return -1; + } + if (vol->dev->d_ops->seek(vol->dev, + (sectors - 1) << vol->sector_size_bits, + SEEK_SET) == -1) { + ntfs_log_perror("Failed to read last sector (%lld)", + (long long)sectors); + ntfs_log_error("%s", last_sector_error); + return -1; + } + + vol->nr_clusters = sectors >> (ffs(sectors_per_cluster) - 1); - sectors = sle64_to_cpu( bs->number_of_sectors ); - ntfs_log_debug( "NumberOfSectors = %lld\n", ( long long )sectors ); - if ( !sectors ) - { - ntfs_log_error( "Volume size is set to zero.\n" ); - return -1; - } - if ( vol->dev->d_ops->seek( vol->dev, - ( sectors - 1 ) << vol->sector_size_bits, - SEEK_SET ) == -1 ) - { - ntfs_log_perror( "Failed to read last sector (%lld)", - ( long long )sectors ); - ntfs_log_error( "%s", last_sector_error ); - return -1; - } - - vol->nr_clusters = sectors >> ( ffs( sectors_per_cluster ) - 1 ); - - vol->mft_lcn = sle64_to_cpu( bs->mft_lcn ); - vol->mftmirr_lcn = sle64_to_cpu( bs->mftmirr_lcn ); - ntfs_log_debug( "MFT LCN = %lld\n", ( long long )vol->mft_lcn ); - ntfs_log_debug( "MFTMirr LCN = %lld\n", ( long long )vol->mftmirr_lcn ); - if ( vol->mft_lcn > vol->nr_clusters || - vol->mftmirr_lcn > vol->nr_clusters ) - { - ntfs_log_error( "$MFT LCN (%lld) or $MFTMirr LCN (%lld) is " - "greater than the number of clusters (%lld).\n", - ( long long )vol->mft_lcn, ( long long )vol->mftmirr_lcn, - ( long long )vol->nr_clusters ); - return -1; - } - - vol->cluster_size = sectors_per_cluster * vol->sector_size; - if ( vol->cluster_size & ( vol->cluster_size - 1 ) ) - { - ntfs_log_error( "cluster_size (%d) is not a power of 2.\n", - vol->cluster_size ); - return -1; - } - vol->cluster_size_bits = ffs( vol->cluster_size ) - 1; - /* - * Need to get the clusters per mft record and handle it if it is - * negative. Then calculate the mft_record_size. A value of 0x80 is - * illegal, thus signed char is actually ok! - */ - c = bs->clusters_per_mft_record; - ntfs_log_debug( "ClusterSize = 0x%x\n", ( unsigned )vol->cluster_size ); - ntfs_log_debug( "ClusterSizeBits = %u\n", vol->cluster_size_bits ); - ntfs_log_debug( "ClustersPerMftRecord = 0x%x\n", c ); - /* - * When clusters_per_mft_record is negative, it means that it is to - * be taken to be the negative base 2 logarithm of the mft_record_size - * min bytes. Then: - * mft_record_size = 2^(-clusters_per_mft_record) bytes. - */ - if ( c < 0 ) - vol->mft_record_size = 1 << -c; - else - vol->mft_record_size = c << vol->cluster_size_bits; - if ( vol->mft_record_size & ( vol->mft_record_size - 1 ) ) - { - ntfs_log_error( "mft_record_size (%d) is not a power of 2.\n", - vol->mft_record_size ); - return -1; - } - vol->mft_record_size_bits = ffs( vol->mft_record_size ) - 1; - ntfs_log_debug( "MftRecordSize = 0x%x\n", ( unsigned )vol->mft_record_size ); - ntfs_log_debug( "MftRecordSizeBits = %u\n", vol->mft_record_size_bits ); - /* Same as above for INDX record. */ - c = bs->clusters_per_index_record; - ntfs_log_debug( "ClustersPerINDXRecord = 0x%x\n", c ); - if ( c < 0 ) - vol->indx_record_size = 1 << -c; - else - vol->indx_record_size = c << vol->cluster_size_bits; - vol->indx_record_size_bits = ffs( vol->indx_record_size ) - 1; - ntfs_log_debug( "INDXRecordSize = 0x%x\n", ( unsigned )vol->indx_record_size ); - ntfs_log_debug( "INDXRecordSizeBits = %u\n", vol->indx_record_size_bits ); - /* - * Work out the size of the MFT mirror in number of mft records. If the - * cluster size is less than or equal to the size taken by four mft - * records, the mft mirror stores the first four mft records. If the - * cluster size is bigger than the size taken by four mft records, the - * mft mirror contains as many mft records as will fit into one - * cluster. - */ - if ( vol->cluster_size <= 4 * vol->mft_record_size ) - vol->mftmirr_size = 4; - else - vol->mftmirr_size = vol->cluster_size / vol->mft_record_size; - return 0; + vol->mft_lcn = sle64_to_cpu(bs->mft_lcn); + vol->mftmirr_lcn = sle64_to_cpu(bs->mftmirr_lcn); + ntfs_log_debug("MFT LCN = %lld\n", (long long)vol->mft_lcn); + ntfs_log_debug("MFTMirr LCN = %lld\n", (long long)vol->mftmirr_lcn); + if (vol->mft_lcn > vol->nr_clusters || + vol->mftmirr_lcn > vol->nr_clusters) { + ntfs_log_error("$MFT LCN (%lld) or $MFTMirr LCN (%lld) is " + "greater than the number of clusters (%lld).\n", + (long long)vol->mft_lcn, (long long)vol->mftmirr_lcn, + (long long)vol->nr_clusters); + return -1; + } + + vol->cluster_size = sectors_per_cluster * vol->sector_size; + if (vol->cluster_size & (vol->cluster_size - 1)) { + ntfs_log_error("cluster_size (%d) is not a power of 2.\n", + vol->cluster_size); + return -1; + } + vol->cluster_size_bits = ffs(vol->cluster_size) - 1; + /* + * Need to get the clusters per mft record and handle it if it is + * negative. Then calculate the mft_record_size. A value of 0x80 is + * illegal, thus signed char is actually ok! + */ + c = bs->clusters_per_mft_record; + ntfs_log_debug("ClusterSize = 0x%x\n", (unsigned)vol->cluster_size); + ntfs_log_debug("ClusterSizeBits = %u\n", vol->cluster_size_bits); + ntfs_log_debug("ClustersPerMftRecord = 0x%x\n", c); + /* + * When clusters_per_mft_record is negative, it means that it is to + * be taken to be the negative base 2 logarithm of the mft_record_size + * min bytes. Then: + * mft_record_size = 2^(-clusters_per_mft_record) bytes. + */ + if (c < 0) + vol->mft_record_size = 1 << -c; + else + vol->mft_record_size = c << vol->cluster_size_bits; + if (vol->mft_record_size & (vol->mft_record_size - 1)) { + ntfs_log_error("mft_record_size (%d) is not a power of 2.\n", + vol->mft_record_size); + return -1; + } + vol->mft_record_size_bits = ffs(vol->mft_record_size) - 1; + ntfs_log_debug("MftRecordSize = 0x%x\n", (unsigned)vol->mft_record_size); + ntfs_log_debug("MftRecordSizeBits = %u\n", vol->mft_record_size_bits); + /* Same as above for INDX record. */ + c = bs->clusters_per_index_record; + ntfs_log_debug("ClustersPerINDXRecord = 0x%x\n", c); + if (c < 0) + vol->indx_record_size = 1 << -c; + else + vol->indx_record_size = c << vol->cluster_size_bits; + vol->indx_record_size_bits = ffs(vol->indx_record_size) - 1; + ntfs_log_debug("INDXRecordSize = 0x%x\n", (unsigned)vol->indx_record_size); + ntfs_log_debug("INDXRecordSizeBits = %u\n", vol->indx_record_size_bits); + /* + * Work out the size of the MFT mirror in number of mft records. If the + * cluster size is less than or equal to the size taken by four mft + * records, the mft mirror stores the first four mft records. If the + * cluster size is bigger than the size taken by four mft records, the + * mft mirror contains as many mft records as will fit into one + * cluster. + */ + if (vol->cluster_size <= 4 * vol->mft_record_size) + vol->mftmirr_size = 4; + else + vol->mftmirr_size = vol->cluster_size / vol->mft_record_size; + return 0; } diff --git a/source/libntfs/bootsect.h b/source/libntfs/bootsect.h index 44408732..a299e821 100644 --- a/source/libntfs/bootsect.h +++ b/source/libntfs/bootsect.h @@ -1,6 +1,6 @@ /* - * bootsect.h - Exports for bootsector record handling. - * Originated from the Linux-NTFS project. + * bootsect.h - Exports for bootsector record handling. + * Originated from the Linux-NTFS project. * * Copyright (c) 2000-2002 Anton Altaparmakov * Copyright (c) 2006 Szabolcs Szakacsits @@ -30,13 +30,13 @@ /** * ntfs_boot_sector_is_ntfs - check a boot sector for describing an ntfs volume - * @b: buffer containing the boot sector + * @b: buffer containing the boot sector * * This function checks the boot sector in @b for describing a valid ntfs * volume. Return TRUE if @b is a valid NTFS boot sector or FALSE otherwise. */ -extern BOOL ntfs_boot_sector_is_ntfs( NTFS_BOOT_SECTOR *b ); -extern int ntfs_boot_sector_parse( ntfs_volume *vol, const NTFS_BOOT_SECTOR *bs ); +extern BOOL ntfs_boot_sector_is_ntfs(NTFS_BOOT_SECTOR *b); +extern int ntfs_boot_sector_parse(ntfs_volume *vol, const NTFS_BOOT_SECTOR *bs); #endif /* defined _NTFS_BOOTSECT_H */ diff --git a/source/libntfs/cache.c b/source/libntfs/cache.c index 31f59a96..dd147672 100644 --- a/source/libntfs/cache.c +++ b/source/libntfs/cache.c @@ -37,637 +37,573 @@ #include "logging.h" /* - * General functions to deal with LRU caches + * General functions to deal with LRU caches * - * The cached data have to be organized in a structure in which - * the first fields must follow a mandatory pattern and further - * fields may contain any fixed size data. They are stored in an - * LRU list. + * The cached data have to be organized in a structure in which + * the first fields must follow a mandatory pattern and further + * fields may contain any fixed size data. They are stored in an + * LRU list. * - * A compare function must be provided for finding a wanted entry - * in the cache. Another function may be provided for invalidating - * an entry to facilitate multiple invalidation. + * A compare function must be provided for finding a wanted entry + * in the cache. Another function may be provided for invalidating + * an entry to facilitate multiple invalidation. * - * These functions never return error codes. When there is a - * shortage of memory, data is simply not cached. - * When there is a hashing bug, hashing is dropped, and sequential - * searches are used. + * These functions never return error codes. When there is a + * shortage of memory, data is simply not cached. + * When there is a hashing bug, hashing is dropped, and sequential + * searches are used. */ /* - * Enter a new hash index, after a new record has been inserted + * Enter a new hash index, after a new record has been inserted * - * Do not call when a record has been modified (with no key change) + * Do not call when a record has been modified (with no key change) */ -static void inserthashindex( struct CACHE_HEADER *cache, - struct CACHED_GENERIC *current ) +static void inserthashindex(struct CACHE_HEADER *cache, + struct CACHED_GENERIC *current) { - int h; - struct HASH_ENTRY *link; - struct HASH_ENTRY *first; + int h; + struct HASH_ENTRY *link; + struct HASH_ENTRY *first; - if ( cache->dohash ) - { - h = cache->dohash( current ); - if ( ( h >= 0 ) && ( h < cache->max_hash ) ) - { - /* get a free link and insert at top of hash list */ - link = cache->free_hash; - if ( link ) - { - cache->free_hash = link->next; - first = cache->first_hash[h]; - if ( first ) - link->next = first; - else - link->next = NULL; - link->entry = current; - cache->first_hash[h] = link; - } - else - { - ntfs_log_error( "No more hash entries," - " cache %s hashing dropped\n", - cache->name ); - cache->dohash = ( cache_hash )NULL; - } - } - else - { - ntfs_log_error( "Illegal hash value," - " cache %s hashing dropped\n", - cache->name ); - cache->dohash = ( cache_hash )NULL; - } - } + if (cache->dohash) { + h = cache->dohash(current); + if ((h >= 0) && (h < cache->max_hash)) { + /* get a free link and insert at top of hash list */ + link = cache->free_hash; + if (link) { + cache->free_hash = link->next; + first = cache->first_hash[h]; + if (first) + link->next = first; + else + link->next = NULL; + link->entry = current; + cache->first_hash[h] = link; + } else { + ntfs_log_error("No more hash entries," + " cache %s hashing dropped\n", + cache->name); + cache->dohash = (cache_hash)NULL; + } + } else { + ntfs_log_error("Illegal hash value," + " cache %s hashing dropped\n", + cache->name); + cache->dohash = (cache_hash)NULL; + } + } } /* - * Drop a hash index when a record is about to be deleted + * Drop a hash index when a record is about to be deleted */ -static void drophashindex( struct CACHE_HEADER *cache, - const struct CACHED_GENERIC *current, int hash ) +static void drophashindex(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *current, int hash) { - struct HASH_ENTRY *link; - struct HASH_ENTRY *previous; + struct HASH_ENTRY *link; + struct HASH_ENTRY *previous; - if ( cache->dohash ) - { - if ( ( hash >= 0 ) && ( hash < cache->max_hash ) ) - { - /* find the link and unlink */ - link = cache->first_hash[hash]; - previous = ( struct HASH_ENTRY* )NULL; - while ( link && ( link->entry != current ) ) - { - previous = link; - link = link->next; - } - if ( link ) - { - if ( previous ) - previous->next = link->next; - else - cache->first_hash[hash] = link->next; - link->next = cache->free_hash; - cache->free_hash = link; - } - else - { - ntfs_log_error( "Bad hash list," - " cache %s hashing dropped\n", - cache->name ); - cache->dohash = ( cache_hash )NULL; - } - } - else - { - ntfs_log_error( "Illegal hash value," - " cache %s hashing dropped\n", - cache->name ); - cache->dohash = ( cache_hash )NULL; - } - } + if (cache->dohash) { + if ((hash >= 0) && (hash < cache->max_hash)) { + /* find the link and unlink */ + link = cache->first_hash[hash]; + previous = (struct HASH_ENTRY*)NULL; + while (link && (link->entry != current)) { + previous = link; + link = link->next; + } + if (link) { + if (previous) + previous->next = link->next; + else + cache->first_hash[hash] = link->next; + link->next = cache->free_hash; + cache->free_hash = link; + } else { + ntfs_log_error("Bad hash list," + " cache %s hashing dropped\n", + cache->name); + cache->dohash = (cache_hash)NULL; + } + } else { + ntfs_log_error("Illegal hash value," + " cache %s hashing dropped\n", + cache->name); + cache->dohash = (cache_hash)NULL; + } + } } /* - * Fetch an entry from cache + * Fetch an entry from cache * - * returns the cache entry, or NULL if not available - * The returned entry may be modified, but not freed + * returns the cache entry, or NULL if not available + * The returned entry may be modified, but not freed */ -struct CACHED_GENERIC *ntfs_fetch_cache( struct CACHE_HEADER *cache, - const struct CACHED_GENERIC *wanted, cache_compare compare ) +struct CACHED_GENERIC *ntfs_fetch_cache(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *wanted, cache_compare compare) { - struct CACHED_GENERIC *current; - struct CACHED_GENERIC *previous; - struct HASH_ENTRY *link; - int h; + struct CACHED_GENERIC *current; + struct CACHED_GENERIC *previous; + struct HASH_ENTRY *link; + int h; - current = ( struct CACHED_GENERIC* )NULL; - if ( cache ) - { - if ( cache->dohash ) - { - /* - * When possible, use the hash table to - * locate the entry if present - */ - h = cache->dohash( wanted ); - link = cache->first_hash[h]; - while ( link && compare( link->entry, wanted ) ) - link = link->next; - if ( link ) - current = link->entry; - } - if ( !cache->dohash ) - { - /* - * Search sequentially in LRU list if no hash table - * or if hashing has just failed - */ - current = cache->most_recent_entry; - while ( current - && compare( current, wanted ) ) - { - current = current->next; - } - } - if ( current ) - { - previous = current->previous; - cache->hits++; - if ( previous ) - { - /* - * found and not at head of list, unlink from current - * position and relink as head of list - */ - previous->next = current->next; - if ( current->next ) - current->next->previous - = current->previous; - else - cache->oldest_entry - = current->previous; - current->next = cache->most_recent_entry; - current->previous - = ( struct CACHED_GENERIC* )NULL; - cache->most_recent_entry->previous = current; - cache->most_recent_entry = current; - } - } - cache->reads++; - } - return ( current ); + current = (struct CACHED_GENERIC*)NULL; + if (cache) { + if (cache->dohash) { + /* + * When possible, use the hash table to + * locate the entry if present + */ + h = cache->dohash(wanted); + link = cache->first_hash[h]; + while (link && compare(link->entry, wanted)) + link = link->next; + if (link) + current = link->entry; + } + if (!cache->dohash) { + /* + * Search sequentially in LRU list if no hash table + * or if hashing has just failed + */ + current = cache->most_recent_entry; + while (current + && compare(current, wanted)) { + current = current->next; + } + } + if (current) { + previous = current->previous; + cache->hits++; + if (previous) { + /* + * found and not at head of list, unlink from current + * position and relink as head of list + */ + previous->next = current->next; + if (current->next) + current->next->previous + = current->previous; + else + cache->oldest_entry + = current->previous; + current->next = cache->most_recent_entry; + current->previous + = (struct CACHED_GENERIC*)NULL; + cache->most_recent_entry->previous = current; + cache->most_recent_entry = current; + } + } + cache->reads++; + } + return (current); } /* - * Enter an inode number into cache - * returns the cache entry or NULL if not possible + * Enter an inode number into cache + * returns the cache entry or NULL if not possible */ -struct CACHED_GENERIC *ntfs_enter_cache( struct CACHE_HEADER *cache, - const struct CACHED_GENERIC *item, - cache_compare compare ) +struct CACHED_GENERIC *ntfs_enter_cache(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *item, + cache_compare compare) { - struct CACHED_GENERIC *current; - struct CACHED_GENERIC *before; - struct HASH_ENTRY *link; - int h; + struct CACHED_GENERIC *current; + struct CACHED_GENERIC *before; + struct HASH_ENTRY *link; + int h; - current = ( struct CACHED_GENERIC* )NULL; - if ( cache ) - { - if ( cache->dohash ) - { - /* - * When possible, use the hash table to - * find out whether the entry if present - */ - h = cache->dohash( item ); - link = cache->first_hash[h]; - while ( link && compare( link->entry, item ) ) - link = link->next; - if ( link ) - { - current = link->entry; - } - } - if ( !cache->dohash ) - { - /* - * Search sequentially in LRU list to locate the end, - * and find out whether the entry is already in list - * As we normally go to the end, no statistics is - * kept. - */ - current = cache->most_recent_entry; - while ( current - && compare( current, item ) ) - { - current = current->next; - } - } + current = (struct CACHED_GENERIC*)NULL; + if (cache) { + if (cache->dohash) { + /* + * When possible, use the hash table to + * find out whether the entry if present + */ + h = cache->dohash(item); + link = cache->first_hash[h]; + while (link && compare(link->entry, item)) + link = link->next; + if (link) { + current = link->entry; + } + } + if (!cache->dohash) { + /* + * Search sequentially in LRU list to locate the end, + * and find out whether the entry is already in list + * As we normally go to the end, no statistics is + * kept. + */ + current = cache->most_recent_entry; + while (current + && compare(current, item)) { + current = current->next; + } + } - if ( !current ) - { - /* - * Not in list, get a free entry or reuse the - * last entry, and relink as head of list - * Note : we assume at least three entries, so - * before, previous and first are different when - * an entry is reused. - */ + if (!current) { + /* + * Not in list, get a free entry or reuse the + * last entry, and relink as head of list + * Note : we assume at least three entries, so + * before, previous and first are different when + * an entry is reused. + */ - if ( cache->free_entry ) - { - current = cache->free_entry; - cache->free_entry = cache->free_entry->next; - if ( item->varsize ) - { - current->variable = ntfs_malloc( - item->varsize ); - } - else - current->variable = ( void* )NULL; - current->varsize = item->varsize; - if ( !cache->oldest_entry ) - cache->oldest_entry = current; - } - else - { - /* reusing the oldest entry */ - current = cache->oldest_entry; - before = current->previous; - before->next = ( struct CACHED_GENERIC* )NULL; - if ( cache->dohash ) - drophashindex( cache, current, - cache->dohash( current ) ); - if ( cache->dofree ) - cache->dofree( current ); - cache->oldest_entry = current->previous; - if ( item->varsize ) - { - if ( current->varsize ) - current->variable = realloc( - current->variable, - item->varsize ); - else - current->variable = ntfs_malloc( - item->varsize ); - } - else - { - if ( current->varsize ) - free( current->variable ); - current->variable = ( void* )NULL; - } - current->varsize = item->varsize; - } - current->next = cache->most_recent_entry; - current->previous = ( struct CACHED_GENERIC* )NULL; - if ( cache->most_recent_entry ) - cache->most_recent_entry->previous = current; - cache->most_recent_entry = current; - memcpy( current->fixed, item->fixed, cache->fixed_size ); - if ( item->varsize ) - { - if ( current->variable ) - { - memcpy( current->variable, - item->variable, item->varsize ); - } - else - { - /* - * no more memory for variable part - * recycle entry in free list - * not an error, just uncacheable - */ - cache->most_recent_entry = current->next; - current->next = cache->free_entry; - cache->free_entry = current; - current = ( struct CACHED_GENERIC* )NULL; - } - } - else - { - current->variable = ( void* )NULL; - current->varsize = 0; - } - if ( cache->dohash && current ) - inserthashindex( cache, current ); - } - cache->writes++; - } - return ( current ); + if (cache->free_entry) { + current = cache->free_entry; + cache->free_entry = cache->free_entry->next; + if (item->varsize) { + current->variable = ntfs_malloc( + item->varsize); + } else + current->variable = (void*)NULL; + current->varsize = item->varsize; + if (!cache->oldest_entry) + cache->oldest_entry = current; + } else { + /* reusing the oldest entry */ + current = cache->oldest_entry; + before = current->previous; + before->next = (struct CACHED_GENERIC*)NULL; + if (cache->dohash) + drophashindex(cache,current, + cache->dohash(current)); + if (cache->dofree) + cache->dofree(current); + cache->oldest_entry = current->previous; + if (item->varsize) { + if (current->varsize) + current->variable = realloc( + current->variable, + item->varsize); + else + current->variable = ntfs_malloc( + item->varsize); + } else { + if (current->varsize) + free(current->variable); + current->variable = (void*)NULL; + } + current->varsize = item->varsize; + } + current->next = cache->most_recent_entry; + current->previous = (struct CACHED_GENERIC*)NULL; + if (cache->most_recent_entry) + cache->most_recent_entry->previous = current; + cache->most_recent_entry = current; + memcpy(current->fixed, item->fixed, cache->fixed_size); + if (item->varsize) { + if (current->variable) { + memcpy(current->variable, + item->variable, item->varsize); + } else { + /* + * no more memory for variable part + * recycle entry in free list + * not an error, just uncacheable + */ + cache->most_recent_entry = current->next; + current->next = cache->free_entry; + cache->free_entry = current; + current = (struct CACHED_GENERIC*)NULL; + } + } else { + current->variable = (void*)NULL; + current->varsize = 0; + } + if (cache->dohash && current) + inserthashindex(cache,current); + } + cache->writes++; + } + return (current); } /* - * Invalidate a cache entry - * The entry is moved to the free entry list - * A specific function may be called for entry deletion + * Invalidate a cache entry + * The entry is moved to the free entry list + * A specific function may be called for entry deletion */ -static void do_invalidate( struct CACHE_HEADER *cache, - struct CACHED_GENERIC *current, int flags ) +static void do_invalidate(struct CACHE_HEADER *cache, + struct CACHED_GENERIC *current, int flags) { - struct CACHED_GENERIC *previous; + struct CACHED_GENERIC *previous; - previous = current->previous; - if ( ( flags & CACHE_FREE ) && cache->dofree ) - cache->dofree( current ); - /* - * Relink into free list - */ - if ( current->next ) - current->next->previous = current->previous; - else - cache->oldest_entry = current->previous; - if ( previous ) - previous->next = current->next; - else - cache->most_recent_entry = current->next; - current->next = cache->free_entry; - cache->free_entry = current; - if ( current->variable ) - free( current->variable ); - current->varsize = 0; -} + previous = current->previous; + if ((flags & CACHE_FREE) && cache->dofree) + cache->dofree(current); + /* + * Relink into free list + */ + if (current->next) + current->next->previous = current->previous; + else + cache->oldest_entry = current->previous; + if (previous) + previous->next = current->next; + else + cache->most_recent_entry = current->next; + current->next = cache->free_entry; + cache->free_entry = current; + if (current->variable) + free(current->variable); + current->varsize = 0; + } /* - * Invalidate entries in cache + * Invalidate entries in cache * - * Several entries may have to be invalidated (at least for inodes - * associated to directories which have been renamed), a different - * compare function may be provided to select entries to invalidate + * Several entries may have to be invalidated (at least for inodes + * associated to directories which have been renamed), a different + * compare function may be provided to select entries to invalidate * - * Returns the number of deleted entries, this can be used by - * the caller to signal a cache corruption if the entry was - * supposed to be found. + * Returns the number of deleted entries, this can be used by + * the caller to signal a cache corruption if the entry was + * supposed to be found. */ -int ntfs_invalidate_cache( struct CACHE_HEADER *cache, - const struct CACHED_GENERIC *item, cache_compare compare, - int flags ) +int ntfs_invalidate_cache(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *item, cache_compare compare, + int flags) { - struct CACHED_GENERIC *current; - struct CACHED_GENERIC *previous; - struct CACHED_GENERIC *next; - struct HASH_ENTRY *link; - int count; - int h; + struct CACHED_GENERIC *current; + struct CACHED_GENERIC *previous; + struct CACHED_GENERIC *next; + struct HASH_ENTRY *link; + int count; + int h; - current = ( struct CACHED_GENERIC* )NULL; - count = 0; - if ( cache ) - { - if ( !( flags & CACHE_NOHASH ) && cache->dohash ) - { - /* - * When possible, use the hash table to - * find out whether the entry if present - */ - h = cache->dohash( item ); - link = cache->first_hash[h]; - while ( link ) - { - if ( compare( link->entry, item ) ) - link = link->next; - else - { - current = link->entry; - link = link->next; - if ( current ) - { - drophashindex( cache, current, h ); - do_invalidate( cache, - current, flags ); - count++; - } - } - } - } - if ( ( flags & CACHE_NOHASH ) || !cache->dohash ) - { - /* - * Search sequentially in LRU list - */ - current = cache->most_recent_entry; - previous = ( struct CACHED_GENERIC* )NULL; - while ( current ) - { - if ( !compare( current, item ) ) - { - next = current->next; - if ( cache->dohash ) - drophashindex( cache, current, - cache->dohash( current ) ); - do_invalidate( cache, current, flags ); - current = next; - count++; - } - else - { - previous = current; - current = current->next; - } - } - } - } - return ( count ); + current = (struct CACHED_GENERIC*)NULL; + count = 0; + if (cache) { + if (!(flags & CACHE_NOHASH) && cache->dohash) { + /* + * When possible, use the hash table to + * find out whether the entry if present + */ + h = cache->dohash(item); + link = cache->first_hash[h]; + while (link) { + if (compare(link->entry, item)) + link = link->next; + else { + current = link->entry; + link = link->next; + if (current) { + drophashindex(cache,current,h); + do_invalidate(cache, + current,flags); + count++; + } + } + } + } + if ((flags & CACHE_NOHASH) || !cache->dohash) { + /* + * Search sequentially in LRU list + */ + current = cache->most_recent_entry; + previous = (struct CACHED_GENERIC*)NULL; + while (current) { + if (!compare(current, item)) { + next = current->next; + if (cache->dohash) + drophashindex(cache,current, + cache->dohash(current)); + do_invalidate(cache,current,flags); + current = next; + count++; + } else { + previous = current; + current = current->next; + } + } + } + } + return (count); } -int ntfs_remove_cache( struct CACHE_HEADER *cache, - struct CACHED_GENERIC *item, int flags ) +int ntfs_remove_cache(struct CACHE_HEADER *cache, + struct CACHED_GENERIC *item, int flags) { - int count; + int count; - count = 0; - if ( cache ) - { - if ( cache->dohash ) - drophashindex( cache, item, cache->dohash( item ) ); - do_invalidate( cache, item, flags ); - count++; - } - return ( count ); + count = 0; + if (cache) { + if (cache->dohash) + drophashindex(cache,item,cache->dohash(item)); + do_invalidate(cache,item,flags); + count++; + } + return (count); } /* - * Free memory allocated to a cache + * Free memory allocated to a cache */ -static void ntfs_free_cache( struct CACHE_HEADER *cache ) +static void ntfs_free_cache(struct CACHE_HEADER *cache) { - struct CACHED_GENERIC *entry; + struct CACHED_GENERIC *entry; - if ( cache ) - { - for ( entry = cache->most_recent_entry; entry; entry = entry->next ) - { - if ( cache->dofree ) - cache->dofree( entry ); - if ( entry->variable ) - free( entry->variable ); - } - free( cache ); - } + if (cache) { + for (entry=cache->most_recent_entry; entry; entry=entry->next) { + if (cache->dofree) + cache->dofree(entry); + if (entry->variable) + free(entry->variable); + } + free(cache); + } } /* - * Create a cache + * Create a cache * - * Returns the cache header, or NULL if the cache could not be created + * Returns the cache header, or NULL if the cache could not be created */ -static struct CACHE_HEADER *ntfs_create_cache( const char *name, - cache_free dofree, cache_hash dohash, - int full_item_size, - int item_count, int max_hash ) +static struct CACHE_HEADER *ntfs_create_cache(const char *name, + cache_free dofree, cache_hash dohash, + int full_item_size, + int item_count, int max_hash) { - struct CACHE_HEADER *cache; - struct CACHED_GENERIC *pc; - struct CACHED_GENERIC *qc; - struct HASH_ENTRY *ph; - struct HASH_ENTRY *qh; - struct HASH_ENTRY **px; - size_t size; - int i; + struct CACHE_HEADER *cache; + struct CACHED_GENERIC *pc; + struct CACHED_GENERIC *qc; + struct HASH_ENTRY *ph; + struct HASH_ENTRY *qh; + struct HASH_ENTRY **px; + size_t size; + int i; - size = sizeof( struct CACHE_HEADER ) + item_count*full_item_size; - if ( max_hash ) - size += item_count*sizeof( struct HASH_ENTRY ) - + max_hash*sizeof( struct HASH_ENTRY* ); - cache = ( struct CACHE_HEADER* )ntfs_malloc( size ); - if ( cache ) - { - /* header */ - cache->name = name; - cache->dofree = dofree; - if ( dohash && max_hash ) - { - cache->dohash = dohash; - cache->max_hash = max_hash; - } - else - { - cache->dohash = ( cache_hash )NULL; - cache->max_hash = 0; - } - cache->fixed_size = full_item_size - sizeof( struct CACHED_GENERIC ); - cache->reads = 0; - cache->writes = 0; - cache->hits = 0; - /* chain the data entries, and mark an invalid entry */ - cache->most_recent_entry = ( struct CACHED_GENERIC* )NULL; - cache->oldest_entry = ( struct CACHED_GENERIC* )NULL; - cache->free_entry = &cache->entry[0]; - pc = &cache->entry[0]; - for ( i = 0; i < ( item_count - 1 ); i++ ) - { - qc = ( struct CACHED_GENERIC* )( ( char* )pc - + full_item_size ); - pc->next = qc; - pc->variable = ( void* )NULL; - pc->varsize = 0; - pc = qc; - } - /* special for the last entry */ - pc->next = ( struct CACHED_GENERIC* )NULL; - pc->variable = ( void* )NULL; - pc->varsize = 0; + size = sizeof(struct CACHE_HEADER) + item_count*full_item_size; + if (max_hash) + size += item_count*sizeof(struct HASH_ENTRY) + + max_hash*sizeof(struct HASH_ENTRY*); + cache = (struct CACHE_HEADER*)ntfs_malloc(size); + if (cache) { + /* header */ + cache->name = name; + cache->dofree = dofree; + if (dohash && max_hash) { + cache->dohash = dohash; + cache->max_hash = max_hash; + } else { + cache->dohash = (cache_hash)NULL; + cache->max_hash = 0; + } + cache->fixed_size = full_item_size - sizeof(struct CACHED_GENERIC); + cache->reads = 0; + cache->writes = 0; + cache->hits = 0; + /* chain the data entries, and mark an invalid entry */ + cache->most_recent_entry = (struct CACHED_GENERIC*)NULL; + cache->oldest_entry = (struct CACHED_GENERIC*)NULL; + cache->free_entry = &cache->entry[0]; + pc = &cache->entry[0]; + for (i=0; i<(item_count - 1); i++) { + qc = (struct CACHED_GENERIC*)((char*)pc + + full_item_size); + pc->next = qc; + pc->variable = (void*)NULL; + pc->varsize = 0; + pc = qc; + } + /* special for the last entry */ + pc->next = (struct CACHED_GENERIC*)NULL; + pc->variable = (void*)NULL; + pc->varsize = 0; - if ( max_hash ) - { - /* chain the hash entries */ - ph = ( struct HASH_ENTRY* )( ( ( char* )pc ) + full_item_size ); - cache->free_hash = ph; - for ( i = 0; i < ( item_count - 1 ); i++ ) - { - qh = &ph[1]; - ph->next = qh; - ph = qh; - } - /* special for the last entry */ - if ( item_count ) - { - ph->next = ( struct HASH_ENTRY* )NULL; - } - /* create and initialize the hash indexes */ - px = ( struct HASH_ENTRY** ) & ph[1]; - cache->first_hash = px; - for ( i = 0; i < max_hash; i++ ) - px[i] = ( struct HASH_ENTRY* )NULL; - } - else - { - cache->free_hash = ( struct HASH_ENTRY* )NULL; - cache->first_hash = ( struct HASH_ENTRY** )NULL; - } - } - return ( cache ); + if (max_hash) { + /* chain the hash entries */ + ph = (struct HASH_ENTRY*)(((char*)pc) + full_item_size); + cache->free_hash = ph; + for (i=0; i<(item_count - 1); i++) { + qh = &ph[1]; + ph->next = qh; + ph = qh; + } + /* special for the last entry */ + if (item_count) { + ph->next = (struct HASH_ENTRY*)NULL; + } + /* create and initialize the hash indexes */ + px = (struct HASH_ENTRY**)&ph[1]; + cache->first_hash = px; + for (i=0; ifree_hash = (struct HASH_ENTRY*)NULL; + cache->first_hash = (struct HASH_ENTRY**)NULL; + } + } + return (cache); } /* - * Create all LRU caches + * Create all LRU caches * - * No error return, if creation is not possible, cacheing will - * just be not available + * No error return, if creation is not possible, cacheing will + * just be not available */ -void ntfs_create_lru_caches( ntfs_volume *vol ) +void ntfs_create_lru_caches(ntfs_volume *vol) { #if CACHE_INODE_SIZE - /* inode cache */ - vol->xinode_cache = ntfs_create_cache( "inode", ( cache_free )NULL, - ntfs_dir_inode_hash, sizeof( struct CACHED_INODE ), - CACHE_INODE_SIZE, 2 * CACHE_INODE_SIZE ); + /* inode cache */ + vol->xinode_cache = ntfs_create_cache("inode",(cache_free)NULL, + ntfs_dir_inode_hash, sizeof(struct CACHED_INODE), + CACHE_INODE_SIZE, 2*CACHE_INODE_SIZE); #endif #if CACHE_NIDATA_SIZE - /* idata cache */ - vol->nidata_cache = ntfs_create_cache( "nidata", - ntfs_inode_nidata_free, ntfs_inode_nidata_hash, - sizeof( struct CACHED_NIDATA ), - CACHE_NIDATA_SIZE, 2 * CACHE_NIDATA_SIZE ); + /* idata cache */ + vol->nidata_cache = ntfs_create_cache("nidata", + ntfs_inode_nidata_free, ntfs_inode_nidata_hash, + sizeof(struct CACHED_NIDATA), + CACHE_NIDATA_SIZE, 2*CACHE_NIDATA_SIZE); #endif #if CACHE_LOOKUP_SIZE - /* lookup cache */ - vol->lookup_cache = ntfs_create_cache( "lookup", - ( cache_free )NULL, ntfs_dir_lookup_hash, - sizeof( struct CACHED_LOOKUP ), - CACHE_LOOKUP_SIZE, 2 * CACHE_LOOKUP_SIZE ); + /* lookup cache */ + vol->lookup_cache = ntfs_create_cache("lookup", + (cache_free)NULL, ntfs_dir_lookup_hash, + sizeof(struct CACHED_LOOKUP), + CACHE_LOOKUP_SIZE, 2*CACHE_LOOKUP_SIZE); #endif - vol->securid_cache = ntfs_create_cache( "securid", ( cache_free )NULL, - ( cache_hash )NULL, sizeof( struct CACHED_SECURID ), CACHE_SECURID_SIZE, 0 ); + vol->securid_cache = ntfs_create_cache("securid",(cache_free)NULL, + (cache_hash)NULL,sizeof(struct CACHED_SECURID), CACHE_SECURID_SIZE, 0); #if CACHE_LEGACY_SIZE - vol->legacy_cache = ntfs_create_cache( "legacy", ( cache_free )NULL, - ( cache_hash )NULL, sizeof( struct CACHED_PERMISSIONS_LEGACY ), CACHE_LEGACY_SIZE, 0 ); + vol->legacy_cache = ntfs_create_cache("legacy",(cache_free)NULL, + (cache_hash)NULL, sizeof(struct CACHED_PERMISSIONS_LEGACY), CACHE_LEGACY_SIZE, 0); #endif } /* - * Free all LRU caches + * Free all LRU caches */ -void ntfs_free_lru_caches( ntfs_volume *vol ) +void ntfs_free_lru_caches(ntfs_volume *vol) { #if CACHE_INODE_SIZE - ntfs_free_cache( vol->xinode_cache ); + ntfs_free_cache(vol->xinode_cache); #endif #if CACHE_NIDATA_SIZE - ntfs_free_cache( vol->nidata_cache ); + ntfs_free_cache(vol->nidata_cache); #endif #if CACHE_LOOKUP_SIZE - ntfs_free_cache( vol->lookup_cache ); + ntfs_free_cache(vol->lookup_cache); #endif - ntfs_free_cache( vol->securid_cache ); + ntfs_free_cache(vol->securid_cache); #if CACHE_LEGACY_SIZE - ntfs_free_cache( vol->legacy_cache ); + ntfs_free_cache(vol->legacy_cache); #endif } diff --git a/source/libntfs/cache.h b/source/libntfs/cache.h index f248f48b..67e4f9da 100644 --- a/source/libntfs/cache.h +++ b/source/libntfs/cache.h @@ -24,104 +24,96 @@ #include "volume.h" -struct CACHED_GENERIC -{ - struct CACHED_GENERIC *next; - struct CACHED_GENERIC *previous; - void *variable; - size_t varsize; - union - { - /* force alignment for pointers and u64 */ - u64 u64align; - void *ptralign; - } fixed[0]; +struct CACHED_GENERIC { + struct CACHED_GENERIC *next; + struct CACHED_GENERIC *previous; + void *variable; + size_t varsize; + union { + /* force alignment for pointers and u64 */ + u64 u64align; + void *ptralign; + } fixed[0]; } ; -struct CACHED_INODE -{ - struct CACHED_INODE *next; - struct CACHED_INODE *previous; - const char *pathname; - size_t varsize; - /* above fields must match "struct CACHED_GENERIC" */ - u64 inum; +struct CACHED_INODE { + struct CACHED_INODE *next; + struct CACHED_INODE *previous; + const char *pathname; + size_t varsize; + /* above fields must match "struct CACHED_GENERIC" */ + u64 inum; } ; -struct CACHED_NIDATA -{ - struct CACHED_NIDATA *next; - struct CACHED_NIDATA *previous; - const char *pathname; /* not used */ - size_t varsize; /* not used */ - /* above fields must match "struct CACHED_GENERIC" */ - u64 inum; - ntfs_inode *ni; +struct CACHED_NIDATA { + struct CACHED_NIDATA *next; + struct CACHED_NIDATA *previous; + const char *pathname; /* not used */ + size_t varsize; /* not used */ + /* above fields must match "struct CACHED_GENERIC" */ + u64 inum; + ntfs_inode *ni; } ; -struct CACHED_LOOKUP -{ - struct CACHED_LOOKUP *next; - struct CACHED_LOOKUP *previous; - const char *name; - size_t namesize; - /* above fields must match "struct CACHED_GENERIC" */ - u64 parent; - u64 inum; +struct CACHED_LOOKUP { + struct CACHED_LOOKUP *next; + struct CACHED_LOOKUP *previous; + const char *name; + size_t namesize; + /* above fields must match "struct CACHED_GENERIC" */ + u64 parent; + u64 inum; } ; -enum -{ - CACHE_FREE = 1, - CACHE_NOHASH = 2 +enum { + CACHE_FREE = 1, + CACHE_NOHASH = 2 } ; -typedef int ( *cache_compare )( const struct CACHED_GENERIC *cached, - const struct CACHED_GENERIC *item ); -typedef void ( *cache_free )( const struct CACHED_GENERIC *cached ); -typedef int ( *cache_hash )( const struct CACHED_GENERIC *cached ); +typedef int (*cache_compare)(const struct CACHED_GENERIC *cached, + const struct CACHED_GENERIC *item); +typedef void (*cache_free)(const struct CACHED_GENERIC *cached); +typedef int (*cache_hash)(const struct CACHED_GENERIC *cached); -struct HASH_ENTRY -{ - struct HASH_ENTRY *next; - struct CACHED_GENERIC *entry; +struct HASH_ENTRY { + struct HASH_ENTRY *next; + struct CACHED_GENERIC *entry; } ; -struct CACHE_HEADER -{ - const char *name; - struct CACHED_GENERIC *most_recent_entry; - struct CACHED_GENERIC *oldest_entry; - struct CACHED_GENERIC *free_entry; - struct HASH_ENTRY *free_hash; - struct HASH_ENTRY **first_hash; - cache_free dofree; - cache_hash dohash; - unsigned long reads; - unsigned long writes; - unsigned long hits; - int fixed_size; - int max_hash; - struct CACHED_GENERIC entry[0]; +struct CACHE_HEADER { + const char *name; + struct CACHED_GENERIC *most_recent_entry; + struct CACHED_GENERIC *oldest_entry; + struct CACHED_GENERIC *free_entry; + struct HASH_ENTRY *free_hash; + struct HASH_ENTRY **first_hash; + cache_free dofree; + cache_hash dohash; + unsigned long reads; + unsigned long writes; + unsigned long hits; + int fixed_size; + int max_hash; + struct CACHED_GENERIC entry[0]; } ; -/* cast to generic, avoiding gcc warnings */ + /* cast to generic, avoiding gcc warnings */ #define GENERIC(pstr) ((const struct CACHED_GENERIC*)(const void*)(pstr)) -struct CACHED_GENERIC *ntfs_fetch_cache( struct CACHE_HEADER *cache, - const struct CACHED_GENERIC *wanted, - cache_compare compare ); -struct CACHED_GENERIC *ntfs_enter_cache( struct CACHE_HEADER *cache, - const struct CACHED_GENERIC *item, - cache_compare compare ); -int ntfs_invalidate_cache( struct CACHE_HEADER *cache, - const struct CACHED_GENERIC *item, - cache_compare compare, int flags ); -int ntfs_remove_cache( struct CACHE_HEADER *cache, - struct CACHED_GENERIC *item, int flags ); +struct CACHED_GENERIC *ntfs_fetch_cache(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *wanted, + cache_compare compare); +struct CACHED_GENERIC *ntfs_enter_cache(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *item, + cache_compare compare); +int ntfs_invalidate_cache(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *item, + cache_compare compare, int flags); +int ntfs_remove_cache(struct CACHE_HEADER *cache, + struct CACHED_GENERIC *item, int flags); -void ntfs_create_lru_caches( ntfs_volume *vol ); -void ntfs_free_lru_caches( ntfs_volume *vol ); +void ntfs_create_lru_caches(ntfs_volume *vol); +void ntfs_free_lru_caches(ntfs_volume *vol); #endif /* _NTFS_CACHE_H_ */ diff --git a/source/libntfs/cache2.c b/source/libntfs/cache2.c index 333a5da7..872f1a57 100644 --- a/source/libntfs/cache2.c +++ b/source/libntfs/cache2.c @@ -45,365 +45,330 @@ #define CACHE_FREE UINT_MAX -NTFS_CACHE* _NTFS_cache_constructor ( unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition, sec_t sectorSize ) -{ - NTFS_CACHE* cache; - unsigned int i; - NTFS_CACHE_ENTRY* cacheEntries; +NTFS_CACHE* _NTFS_cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition, sec_t sectorSize) { + NTFS_CACHE* cache; + unsigned int i; + NTFS_CACHE_ENTRY* cacheEntries; - if ( numberOfPages == 0 || sectorsPerPage == 0 ) return NULL; + if(numberOfPages==0 || sectorsPerPage==0) return NULL; - if ( numberOfPages < 4 ) - { - numberOfPages = 4; - } + if (numberOfPages < 4) { + numberOfPages = 4; + } - if ( sectorsPerPage < 32 ) - { - sectorsPerPage = 32; - } + if (sectorsPerPage < 32) { + sectorsPerPage = 32; + } - cache = ( NTFS_CACHE* ) ntfs_alloc ( sizeof( NTFS_CACHE ) ); - if ( cache == NULL ) - { - return NULL; - } + cache = (NTFS_CACHE*) ntfs_alloc (sizeof(NTFS_CACHE)); + if (cache == NULL) { + return NULL; + } - cache->disc = discInterface; - cache->endOfPartition = endOfPartition; - cache->numberOfPages = numberOfPages; - cache->sectorsPerPage = sectorsPerPage; - cache->sectorSize = sectorSize; + cache->disc = discInterface; + cache->endOfPartition = endOfPartition; + cache->numberOfPages = numberOfPages; + cache->sectorsPerPage = sectorsPerPage; + cache->sectorSize = sectorSize; - cacheEntries = ( NTFS_CACHE_ENTRY* ) ntfs_alloc ( sizeof( NTFS_CACHE_ENTRY ) * numberOfPages ); - if ( cacheEntries == NULL ) - { - ntfs_free ( cache ); - return NULL; - } + cacheEntries = (NTFS_CACHE_ENTRY*) ntfs_alloc ( sizeof(NTFS_CACHE_ENTRY) * numberOfPages); + if (cacheEntries == NULL) { + ntfs_free (cache); + return NULL; + } - for ( i = 0; i < numberOfPages; i++ ) - { - cacheEntries[i].sector = CACHE_FREE; - cacheEntries[i].count = 0; - cacheEntries[i].last_access = 0; - cacheEntries[i].dirty = false; - cacheEntries[i].cache = ( uint8_t* ) ntfs_align ( sectorsPerPage * cache->sectorSize ); - } + for (i = 0; i < numberOfPages; i++) { + cacheEntries[i].sector = CACHE_FREE; + cacheEntries[i].count = 0; + cacheEntries[i].last_access = 0; + cacheEntries[i].dirty = false; + cacheEntries[i].cache = (uint8_t*) ntfs_align ( sectorsPerPage * cache->sectorSize ); + } - cache->cacheEntries = cacheEntries; + cache->cacheEntries = cacheEntries; - return cache; + return cache; } -void _NTFS_cache_destructor ( NTFS_CACHE* cache ) -{ - unsigned int i; +void _NTFS_cache_destructor (NTFS_CACHE* cache) { + unsigned int i; - if ( cache == NULL ) return; + if(cache==NULL) return; - // Clear out cache before destroying it - _NTFS_cache_flush( cache ); + // Clear out cache before destroying it + _NTFS_cache_flush(cache); - // Free memory in reverse allocation order - for ( i = 0; i < cache->numberOfPages; i++ ) - { - ntfs_free ( cache->cacheEntries[i].cache ); - } - ntfs_free ( cache->cacheEntries ); - ntfs_free ( cache ); + // Free memory in reverse allocation order + for (i = 0; i < cache->numberOfPages; i++) { + ntfs_free (cache->cacheEntries[i].cache); + } + ntfs_free (cache->cacheEntries); + ntfs_free (cache); } static u32 accessCounter = 0; -static u32 accessTime() -{ - accessCounter++; - return accessCounter; +static u32 accessTime(){ + accessCounter++; + return accessCounter; } -static NTFS_CACHE_ENTRY* _NTFS_cache_getPage( NTFS_CACHE *cache, sec_t sector ) +static NTFS_CACHE_ENTRY* _NTFS_cache_getPage(NTFS_CACHE *cache,sec_t sector) { - unsigned int i; - NTFS_CACHE_ENTRY* cacheEntries = cache->cacheEntries; - unsigned int numberOfPages = cache->numberOfPages; - unsigned int sectorsPerPage = cache->sectorsPerPage; + unsigned int i; + NTFS_CACHE_ENTRY* cacheEntries = cache->cacheEntries; + unsigned int numberOfPages = cache->numberOfPages; + unsigned int sectorsPerPage = cache->sectorsPerPage; - bool foundFree = false; - unsigned int oldUsed = 0; - unsigned int oldAccess = UINT_MAX; + bool foundFree = false; + unsigned int oldUsed = 0; + unsigned int oldAccess = UINT_MAX; - for ( i = 0; i < numberOfPages; i++ ) - { - if ( sector >= cacheEntries[i].sector && sector < ( cacheEntries[i].sector + cacheEntries[i].count ) ) - { - cacheEntries[i].last_access = accessTime(); - return &( cacheEntries[i] ); - } + for(i=0;i=cacheEntries[i].sector && sector<(cacheEntries[i].sector + cacheEntries[i].count)) { + cacheEntries[i].last_access = accessTime(); + return &(cacheEntries[i]); + } - if ( foundFree == false && ( cacheEntries[i].sector == CACHE_FREE || cacheEntries[i].last_access < oldAccess ) ) - { - if ( cacheEntries[i].sector == CACHE_FREE ) foundFree = true; - oldUsed = i; - oldAccess = cacheEntries[i].last_access; - } - } + if(foundFree==false && (cacheEntries[i].sector==CACHE_FREE || cacheEntries[i].last_accessdisc->writeSectors( cacheEntries[oldUsed].sector, cacheEntries[oldUsed].count, cacheEntries[oldUsed].cache ) ) return NULL; - cacheEntries[oldUsed].dirty = false; - } - sector = ( sector / sectorsPerPage ) * sectorsPerPage; // align base sector to page size - sec_t next_page = sector + sectorsPerPage; - if ( next_page > cache->endOfPartition ) next_page = cache->endOfPartition; + if(foundFree==false && cacheEntries[oldUsed].dirty==true) { + if(!cache->disc->writeSectors(cacheEntries[oldUsed].sector,cacheEntries[oldUsed].count,cacheEntries[oldUsed].cache)) return NULL; + cacheEntries[oldUsed].dirty = false; + } + sector = (sector/sectorsPerPage)*sectorsPerPage; // align base sector to page size + sec_t next_page = sector + sectorsPerPage; + if(next_page > cache->endOfPartition) next_page = cache->endOfPartition; - if ( !cache->disc->readSectors( sector, next_page - sector, cacheEntries[oldUsed].cache ) ) return NULL; + if(!cache->disc->readSectors(sector,next_page-sector,cacheEntries[oldUsed].cache)) return NULL; - cacheEntries[oldUsed].sector = sector; - cacheEntries[oldUsed].count = next_page - sector; - cacheEntries[oldUsed].last_access = accessTime(); + cacheEntries[oldUsed].sector = sector; + cacheEntries[oldUsed].count = next_page-sector; + cacheEntries[oldUsed].last_access = accessTime(); - return &( cacheEntries[oldUsed] ); + return &(cacheEntries[oldUsed]); } -static NTFS_CACHE_ENTRY* _NTFS_cache_findPage( NTFS_CACHE *cache, sec_t sector, sec_t count ) -{ +static NTFS_CACHE_ENTRY* _NTFS_cache_findPage(NTFS_CACHE *cache, sec_t sector, sec_t count) { - unsigned int i; - NTFS_CACHE_ENTRY* cacheEntries = cache->cacheEntries; - unsigned int numberOfPages = cache->numberOfPages; - NTFS_CACHE_ENTRY *entry = NULL; - sec_t lowest = UINT_MAX; + unsigned int i; + NTFS_CACHE_ENTRY* cacheEntries = cache->cacheEntries; + unsigned int numberOfPages = cache->numberOfPages; + NTFS_CACHE_ENTRY *entry = NULL; + sec_t lowest = UINT_MAX; - for ( i = 0; i < numberOfPages; i++ ) - { - if ( cacheEntries[i].sector != CACHE_FREE ) - { - bool intersect; - if ( sector > cacheEntries[i].sector ) - { - intersect = sector - cacheEntries[i].sector < cacheEntries[i].count; - } - else - { - intersect = cacheEntries[i].sector - sector < count; - } + for(i=0;i cacheEntries[i].sector) { + intersect = sector - cacheEntries[i].sector < cacheEntries[i].count; + } else { + intersect = cacheEntries[i].sector - sector < count; + } - if ( intersect && ( cacheEntries[i].sector < lowest ) ) - { - lowest = cacheEntries[i].sector; - entry = &cacheEntries[i]; - } - } - } + if ( intersect && (cacheEntries[i].sector < lowest)) { + lowest = cacheEntries[i].sector; + entry = &cacheEntries[i]; + } + } + } - return entry; + return entry; } -bool _NTFS_cache_readSectors( NTFS_CACHE *cache, sec_t sector, sec_t numSectors, void *buffer ) +bool _NTFS_cache_readSectors(NTFS_CACHE *cache,sec_t sector,sec_t numSectors,void *buffer) { - sec_t sec; - sec_t secs_to_read; - NTFS_CACHE_ENTRY *entry; - uint8_t *dest = buffer; + sec_t sec; + sec_t secs_to_read; + NTFS_CACHE_ENTRY *entry; + uint8_t *dest = buffer; - while ( numSectors > 0 ) - { - entry = _NTFS_cache_getPage( cache, sector ); - if ( entry == NULL ) return false; + while(numSectors>0) { + entry = _NTFS_cache_getPage(cache,sector); + if(entry==NULL) return false; - sec = sector - entry->sector; - secs_to_read = entry->count - sec; - if ( secs_to_read > numSectors ) secs_to_read = numSectors; + sec = sector - entry->sector; + secs_to_read = entry->count - sec; + if(secs_to_read>numSectors) secs_to_read = numSectors; - memcpy( dest, entry->cache + ( sec*cache->sectorSize ), ( secs_to_read*cache->sectorSize ) ); + memcpy(dest,entry->cache + (sec*cache->sectorSize),(secs_to_read*cache->sectorSize)); - dest += ( secs_to_read * cache->sectorSize ); - sector += secs_to_read; - numSectors -= secs_to_read; - } + dest += (secs_to_read*cache->sectorSize); + sector += secs_to_read; + numSectors -= secs_to_read; + } - return true; + return true; } /* Reads some data from a cache page, determined by the sector number */ -bool _NTFS_cache_readPartialSector ( NTFS_CACHE* cache, void* buffer, sec_t sector, unsigned int offset, size_t size ) +bool _NTFS_cache_readPartialSector (NTFS_CACHE* cache, void* buffer, sec_t sector, unsigned int offset, size_t size) { - sec_t sec; - NTFS_CACHE_ENTRY *entry; + sec_t sec; + NTFS_CACHE_ENTRY *entry; - if ( offset + size > cache->sectorSize ) return false; + if (offset + size > cache->sectorSize) return false; - entry = _NTFS_cache_getPage( cache, sector ); - if ( entry == NULL ) return false; + entry = _NTFS_cache_getPage(cache,sector); + if(entry==NULL) return false; - sec = sector - entry->sector; - memcpy( buffer, entry->cache + ( ( sec*cache->sectorSize ) + offset ), size ); + sec = sector - entry->sector; + memcpy(buffer,entry->cache + ((sec*cache->sectorSize) + offset),size); - return true; + return true; } -bool _NTFS_cache_readLittleEndianValue ( NTFS_CACHE* cache, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes ) -{ - uint8_t buf[4]; - if ( !_NTFS_cache_readPartialSector( cache, buf, sector, offset, num_bytes ) ) return false; +bool _NTFS_cache_readLittleEndianValue (NTFS_CACHE* cache, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes) { + uint8_t buf[4]; + if (!_NTFS_cache_readPartialSector(cache, buf, sector, offset, num_bytes)) return false; - switch ( num_bytes ) - { - case 1: *value = buf[0]; break; - case 2: *value = u8array_to_u16( buf, 0 ); break; - case 4: *value = u8array_to_u32( buf, 0 ); break; - default: return false; - } - return true; + switch(num_bytes) { + case 1: *value = buf[0]; break; + case 2: *value = u8array_to_u16(buf,0); break; + case 4: *value = u8array_to_u32(buf,0); break; + default: return false; + } + return true; } /* Writes some data to a cache page, making sure it is loaded into memory first. */ -bool _NTFS_cache_writePartialSector ( NTFS_CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size ) +bool _NTFS_cache_writePartialSector (NTFS_CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size) { - sec_t sec; - NTFS_CACHE_ENTRY *entry; + sec_t sec; + NTFS_CACHE_ENTRY *entry; - if ( offset + size > cache->sectorSize ) return false; + if (offset + size > cache->sectorSize) return false; - entry = _NTFS_cache_getPage( cache, sector ); - if ( entry == NULL ) return false; + entry = _NTFS_cache_getPage(cache,sector); + if(entry==NULL) return false; - sec = sector - entry->sector; - memcpy( entry->cache + ( ( sec*cache->sectorSize ) + offset ), buffer, size ); + sec = sector - entry->sector; + memcpy(entry->cache + ((sec*cache->sectorSize) + offset),buffer,size); - entry->dirty = true; - return true; + entry->dirty = true; + return true; } -bool _NTFS_cache_writeLittleEndianValue ( NTFS_CACHE* cache, const uint32_t value, sec_t sector, unsigned int offset, int size ) -{ - uint8_t buf[4] = {0, 0, 0, 0}; +bool _NTFS_cache_writeLittleEndianValue (NTFS_CACHE* cache, const uint32_t value, sec_t sector, unsigned int offset, int size) { + uint8_t buf[4] = {0, 0, 0, 0}; - switch ( size ) - { - case 1: buf[0] = value; break; - case 2: u16_to_u8array( buf, 0, value ); break; - case 4: u32_to_u8array( buf, 0, value ); break; - default: return false; - } + switch(size) { + case 1: buf[0] = value; break; + case 2: u16_to_u8array(buf, 0, value); break; + case 4: u32_to_u8array(buf, 0, value); break; + default: return false; + } - return _NTFS_cache_writePartialSector( cache, buf, sector, offset, size ); + return _NTFS_cache_writePartialSector(cache, buf, sector, offset, size); } /* Writes some data to a cache page, zeroing out the page first */ -bool _NTFS_cache_eraseWritePartialSector ( NTFS_CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size ) +bool _NTFS_cache_eraseWritePartialSector (NTFS_CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size) { - sec_t sec; - NTFS_CACHE_ENTRY *entry; + sec_t sec; + NTFS_CACHE_ENTRY *entry; - if ( offset + size > cache->sectorSize ) return false; + if (offset + size > cache->sectorSize) return false; - entry = _NTFS_cache_getPage( cache, sector ); - if ( entry == NULL ) return false; + entry = _NTFS_cache_getPage(cache,sector); + if(entry==NULL) return false; - sec = sector - entry->sector; - memset( entry->cache + ( sec*cache->sectorSize ), 0, cache->sectorSize ); - memcpy( entry->cache + ( ( sec*cache->sectorSize ) + offset ), buffer, size ); + sec = sector - entry->sector; + memset(entry->cache + (sec*cache->sectorSize),0,cache->sectorSize); + memcpy(entry->cache + ((sec*cache->sectorSize) + offset),buffer,size); - entry->dirty = true; - return true; + entry->dirty = true; + return true; } -bool _NTFS_cache_writeSectors ( NTFS_CACHE* cache, sec_t sector, sec_t numSectors, const void* buffer ) +bool _NTFS_cache_writeSectors (NTFS_CACHE* cache, sec_t sector, sec_t numSectors, const void* buffer) { - sec_t sec; - sec_t secs_to_write; - NTFS_CACHE_ENTRY* entry; - const uint8_t *src = buffer; + sec_t sec; + sec_t secs_to_write; + NTFS_CACHE_ENTRY* entry; + const uint8_t *src = buffer; - while ( numSectors > 0 ) - { - entry = _NTFS_cache_findPage( cache, sector, numSectors ); + while(numSectors>0) + { + entry = _NTFS_cache_findPage(cache,sector,numSectors); - if ( entry != NULL ) - { + if(entry!=NULL) { - if ( entry->sector > sector ) - { + if ( entry->sector > sector) { - secs_to_write = entry->sector - sector; + secs_to_write = entry->sector - sector; - cache->disc->writeSectors( sector, secs_to_write, src ); - src += ( secs_to_write * cache->sectorSize ); - sector += secs_to_write; - numSectors -= secs_to_write; - } + cache->disc->writeSectors(sector,secs_to_write,src); + src += (secs_to_write*cache->sectorSize); + sector += secs_to_write; + numSectors -= secs_to_write; + } - sec = sector - entry->sector; - secs_to_write = entry->count - sec; + sec = sector - entry->sector; + secs_to_write = entry->count - sec; - if ( secs_to_write > numSectors ) secs_to_write = numSectors; + if(secs_to_write>numSectors) secs_to_write = numSectors; - memcpy( entry->cache + ( sec*cache->sectorSize ), src, ( secs_to_write*cache->sectorSize ) ); + memcpy(entry->cache + (sec*cache->sectorSize),src,(secs_to_write*cache->sectorSize)); - src += ( secs_to_write * cache->sectorSize ); - sector += secs_to_write; - numSectors -= secs_to_write; + src += (secs_to_write*cache->sectorSize); + sector += secs_to_write; + numSectors -= secs_to_write; - entry->dirty = true; + entry->dirty = true; - } - else - { - cache->disc->writeSectors( sector, numSectors, src ); - numSectors = 0; - } - } - return true; + } else { + cache->disc->writeSectors(sector,numSectors,src); + numSectors=0; + } + } + return true; } /* Flushes all dirty pages to disc, clearing the dirty flag. */ -bool _NTFS_cache_flush ( NTFS_CACHE* cache ) -{ - unsigned int i; - if ( cache == NULL ) return true; +bool _NTFS_cache_flush (NTFS_CACHE* cache) { + unsigned int i; + if(cache==NULL) return true; - for ( i = 0; i < cache->numberOfPages; i++ ) - { - if ( cache->cacheEntries[i].dirty ) - { - if ( !cache->disc->writeSectors ( cache->cacheEntries[i].sector, cache->cacheEntries[i].count, cache->cacheEntries[i].cache ) ) - { - return false; - } - } - cache->cacheEntries[i].dirty = false; - } + for (i = 0; i < cache->numberOfPages; i++) { + if (cache->cacheEntries[i].dirty) { + if (!cache->disc->writeSectors (cache->cacheEntries[i].sector, cache->cacheEntries[i].count, cache->cacheEntries[i].cache)) { + return false; + } + } + cache->cacheEntries[i].dirty = false; + } - return true; + return true; } -void _NTFS_cache_invalidate ( NTFS_CACHE* cache ) -{ - unsigned int i; - if ( cache == NULL ) +void _NTFS_cache_invalidate (NTFS_CACHE* cache) { + unsigned int i; + if(cache==NULL) return; - _NTFS_cache_flush( cache ); - for ( i = 0; i < cache->numberOfPages; i++ ) - { - cache->cacheEntries[i].sector = CACHE_FREE; - cache->cacheEntries[i].last_access = 0; - cache->cacheEntries[i].count = 0; - cache->cacheEntries[i].dirty = false; - } + _NTFS_cache_flush(cache); + for (i = 0; i < cache->numberOfPages; i++) { + cache->cacheEntries[i].sector = CACHE_FREE; + cache->cacheEntries[i].last_access = 0; + cache->cacheEntries[i].count = 0; + cache->cacheEntries[i].dirty = false; + } } diff --git a/source/libntfs/cache2.h b/source/libntfs/cache2.h index e2bb045e..21daca7c 100644 --- a/source/libntfs/cache2.h +++ b/source/libntfs/cache2.h @@ -46,23 +46,21 @@ #include #include -typedef struct -{ - sec_t sector; - unsigned int count; - u64 last_access; - bool dirty; - u8* cache; +typedef struct { + sec_t sector; + unsigned int count; + u64 last_access; + bool dirty; + u8* cache; } NTFS_CACHE_ENTRY; -typedef struct -{ - const DISC_INTERFACE* disc; - sec_t endOfPartition; - unsigned int numberOfPages; - unsigned int sectorsPerPage; - sec_t sectorSize; - NTFS_CACHE_ENTRY* cacheEntries; +typedef struct { + const DISC_INTERFACE* disc; + sec_t endOfPartition; + unsigned int numberOfPages; + unsigned int sectorsPerPage; + sec_t sectorSize; + NTFS_CACHE_ENTRY* cacheEntries; } NTFS_CACHE; /* @@ -101,37 +99,37 @@ Precondition: offset + size <= BYTES_PER_READ /* Read several sectors from the NTFS_CACHE */ -bool _NTFS_cache_readSectors ( NTFS_CACHE* NTFS_CACHE, sec_t sector, sec_t numSectors, void* buffer ); +bool _NTFS_cache_readSectors (NTFS_CACHE* NTFS_CACHE, sec_t sector, sec_t numSectors, void* buffer); /* Read a full sector from the NTFS_CACHE */ //static inline bool _NTFS_cache_readSector (NTFS_CACHE* NTFS_CACHE, void* buffer, sec_t sector) { -// return _NTFS_cache_readPartialSector (NTFS_CACHE, buffer, sector, 0, BYTES_PER_READ); +// return _NTFS_cache_readPartialSector (NTFS_CACHE, buffer, sector, 0, BYTES_PER_READ); //} /* Write a full sector to the NTFS_CACHE */ //static inline bool _NTFS_cache_writeSector (NTFS_CACHE* NTFS_CACHE, const void* buffer, sec_t sector) { -// return _NTFS_cache_writePartialSector (NTFS_CACHE, buffer, sector, 0, BYTES_PER_READ); +// return _NTFS_cache_writePartialSector (NTFS_CACHE, buffer, sector, 0, BYTES_PER_READ); //} -bool _NTFS_cache_writeSectors ( NTFS_CACHE* NTFS_CACHE, sec_t sector, sec_t numSectors, const void* buffer ); +bool _NTFS_cache_writeSectors (NTFS_CACHE* NTFS_CACHE, sec_t sector, sec_t numSectors, const void* buffer); /* Write any dirty sectors back to disc and clear out the contents of the NTFS_CACHE */ -bool _NTFS_cache_flush ( NTFS_CACHE* NTFS_CACHE ); +bool _NTFS_cache_flush (NTFS_CACHE* NTFS_CACHE); /* Clear out the contents of the NTFS_CACHE without writing any dirty sectors first */ -void _NTFS_cache_invalidate ( NTFS_CACHE* NTFS_CACHE ); +void _NTFS_cache_invalidate (NTFS_CACHE* NTFS_CACHE); -NTFS_CACHE* _NTFS_cache_constructor ( unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition, sec_t sectorSize ); +NTFS_CACHE* _NTFS_cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition, sec_t sectorSize); -void _NTFS_cache_destructor ( NTFS_CACHE* NTFS_CACHE ); +void _NTFS_cache_destructor (NTFS_CACHE* NTFS_CACHE); #endif // _CACHE_H diff --git a/source/libntfs/collate.c b/source/libntfs/collate.c index 9835c338..5f7a015a 100644 --- a/source/libntfs/collate.c +++ b/source/libntfs/collate.c @@ -52,23 +52,22 @@ * * Returns: */ -static int ntfs_collate_binary( ntfs_volume *vol __attribute__( ( unused ) ), - const void *data1, const int data1_len, - const void *data2, const int data2_len ) +static int ntfs_collate_binary(ntfs_volume *vol __attribute__((unused)), + const void *data1, const int data1_len, + const void *data2, const int data2_len) { - int rc; + int rc; - ntfs_log_trace( "Entering.\n" ); - rc = memcmp( data1, data2, min( data1_len, data2_len ) ); - if ( !rc && ( data1_len != data2_len ) ) - { - if ( data1_len < data2_len ) - rc = -1; - else - rc = 1; - } - ntfs_log_trace( "Done, returning %i.\n", rc ); - return rc; + ntfs_log_trace("Entering.\n"); + rc = memcmp(data1, data2, min(data1_len, data2_len)); + if (!rc && (data1_len != data2_len)) { + if (data1_len < data2_len) + rc = -1; + else + rc = 1; + } + ntfs_log_trace("Done, returning %i.\n", rc); + return rc; } /** @@ -83,32 +82,30 @@ static int ntfs_collate_binary( ntfs_volume *vol __attribute__( ( unused ) ), * * Returns: */ -static int ntfs_collate_ntofs_ulong( ntfs_volume *vol __attribute__( ( unused ) ), - const void *data1, const int data1_len, - const void *data2, const int data2_len ) +static int ntfs_collate_ntofs_ulong(ntfs_volume *vol __attribute__((unused)), + const void *data1, const int data1_len, + const void *data2, const int data2_len) { - int rc; - u32 d1, d2; + int rc; + u32 d1, d2; - ntfs_log_trace( "Entering.\n" ); - if ( data1_len != data2_len || data1_len != 4 ) - { - ntfs_log_error( "data1_len or/and data2_len not equal to 4.\n" ); - return NTFS_COLLATION_ERROR; - } - d1 = le32_to_cpup( data1 ); - d2 = le32_to_cpup( data2 ); - if ( d1 < d2 ) - rc = -1; - else - { - if ( d1 == d2 ) - rc = 0; - else - rc = 1; - } - ntfs_log_trace( "Done, returning %i.\n", rc ); - return rc; + ntfs_log_trace("Entering.\n"); + if (data1_len != data2_len || data1_len != 4) { + ntfs_log_error("data1_len or/and data2_len not equal to 4.\n"); + return NTFS_COLLATION_ERROR; + } + d1 = le32_to_cpup(data1); + d2 = le32_to_cpup(data2); + if (d1 < d2) + rc = -1; + else { + if (d1 == d2) + rc = 0; + else + rc = 1; + } + ntfs_log_trace("Done, returning %i.\n", rc); + return rc; } /** @@ -117,43 +114,39 @@ static int ntfs_collate_ntofs_ulong( ntfs_volume *vol __attribute__( ( unused ) * Returns: -1, 0 or 1 depending of how the arrays compare */ -static int ntfs_collate_ntofs_ulongs( ntfs_volume *vol __attribute__( ( unused ) ), - const void *data1, const int data1_len, - const void *data2, const int data2_len ) +static int ntfs_collate_ntofs_ulongs(ntfs_volume *vol __attribute__((unused)), + const void *data1, const int data1_len, + const void *data2, const int data2_len) { - int rc; - int len; - const le32 *p1, *p2; - u32 d1, d2; + int rc; + int len; + const le32 *p1, *p2; + u32 d1, d2; - ntfs_log_trace( "Entering.\n" ); - if ( ( data1_len != data2_len ) || ( data1_len <= 0 ) || ( data1_len & 3 ) ) - { - ntfs_log_error( "data1_len or data2_len not valid\n" ); - return NTFS_COLLATION_ERROR; - } - p1 = ( const le32* )data1; - p2 = ( const le32* )data2; - len = data1_len; - do - { - d1 = le32_to_cpup( p1 ); - p1++; - d2 = le32_to_cpup( p2 ); - p2++; - } - while ( ( d1 == d2 ) && ( ( len -= 4 ) > 0 ) ); - if ( d1 < d2 ) - rc = -1; - else - { - if ( d1 == d2 ) - rc = 0; - else - rc = 1; - } - ntfs_log_trace( "Done, returning %i.\n", rc ); - return rc; + ntfs_log_trace("Entering.\n"); + if ((data1_len != data2_len) || (data1_len <= 0) || (data1_len & 3)) { + ntfs_log_error("data1_len or data2_len not valid\n"); + return NTFS_COLLATION_ERROR; + } + p1 = (const le32*)data1; + p2 = (const le32*)data2; + len = data1_len; + do { + d1 = le32_to_cpup(p1); + p1++; + d2 = le32_to_cpup(p2); + p2++; + } while ((d1 == d2) && ((len -= 4) > 0)); + if (d1 < d2) + rc = -1; + else { + if (d1 == d2) + rc = 0; + else + rc = 1; + } + ntfs_log_trace("Done, returning %i.\n", rc); + return rc; } /** @@ -169,49 +162,45 @@ static int ntfs_collate_ntofs_ulongs( ntfs_volume *vol __attribute__( ( unused ) * * Returns: -1, 0 or 1 depending of how the keys compare */ -static int ntfs_collate_ntofs_security_hash( ntfs_volume *vol __attribute__( ( unused ) ), - const void *data1, const int data1_len, - const void *data2, const int data2_len ) +static int ntfs_collate_ntofs_security_hash(ntfs_volume *vol __attribute__((unused)), + const void *data1, const int data1_len, + const void *data2, const int data2_len) { - int rc; - u32 d1, d2; - const le32 *p1, *p2; + int rc; + u32 d1, d2; + const le32 *p1, *p2; - ntfs_log_trace( "Entering.\n" ); - if ( data1_len != data2_len || data1_len != 8 ) - { - ntfs_log_error( "data1_len or/and data2_len not equal to 8.\n" ); - return NTFS_COLLATION_ERROR; - } - p1 = ( const le32* )data1; - p2 = ( const le32* )data2; - d1 = le32_to_cpup( p1 ); - d2 = le32_to_cpup( p2 ); - if ( d1 < d2 ) - rc = -1; - else - { - if ( d1 > d2 ) - rc = 1; - else - { - p1++; - p2++; - d1 = le32_to_cpup( p1 ); - d2 = le32_to_cpup( p2 ); - if ( d1 < d2 ) - rc = -1; - else - { - if ( d1 > d2 ) - rc = 1; - else - rc = 0; - } - } - } - ntfs_log_trace( "Done, returning %i.\n", rc ); - return rc; + ntfs_log_trace("Entering.\n"); + if (data1_len != data2_len || data1_len != 8) { + ntfs_log_error("data1_len or/and data2_len not equal to 8.\n"); + return NTFS_COLLATION_ERROR; + } + p1 = (const le32*)data1; + p2 = (const le32*)data2; + d1 = le32_to_cpup(p1); + d2 = le32_to_cpup(p2); + if (d1 < d2) + rc = -1; + else { + if (d1 > d2) + rc = 1; + else { + p1++; + p2++; + d1 = le32_to_cpup(p1); + d2 = le32_to_cpup(p2); + if (d1 < d2) + rc = -1; + else { + if (d1 > d2) + rc = 1; + else + rc = 0; + } + } + } + ntfs_log_trace("Done, returning %i.\n", rc); + return rc; } /** @@ -226,58 +215,57 @@ static int ntfs_collate_ntofs_security_hash( ntfs_volume *vol __attribute__( ( u * * Returns: */ -static int ntfs_collate_file_name( ntfs_volume *vol, - const void *data1, const int data1_len __attribute__( ( unused ) ), - const void *data2, const int data2_len __attribute__( ( unused ) ) ) +static int ntfs_collate_file_name(ntfs_volume *vol, + const void *data1, const int data1_len __attribute__((unused)), + const void *data2, const int data2_len __attribute__((unused))) { - const FILE_NAME_ATTR *file_name_attr1; - const FILE_NAME_ATTR *file_name_attr2; - int rc; + const FILE_NAME_ATTR *file_name_attr1; + const FILE_NAME_ATTR *file_name_attr2; + int rc; - ntfs_log_trace( "Entering.\n" ); - file_name_attr1 = ( const FILE_NAME_ATTR* )data1; - file_name_attr2 = ( const FILE_NAME_ATTR* )data2; - rc = ntfs_names_full_collate( - ( ntfschar* ) & file_name_attr1->file_name, - file_name_attr1->file_name_length, - ( ntfschar* ) & file_name_attr2->file_name, - file_name_attr2->file_name_length, - CASE_SENSITIVE, vol->upcase, vol->upcase_len ); - ntfs_log_trace( "Done, returning %i.\n", rc ); - return rc; + ntfs_log_trace("Entering.\n"); + file_name_attr1 = (const FILE_NAME_ATTR*)data1; + file_name_attr2 = (const FILE_NAME_ATTR*)data2; + rc = ntfs_names_full_collate( + (ntfschar*)&file_name_attr1->file_name, + file_name_attr1->file_name_length, + (ntfschar*)&file_name_attr2->file_name, + file_name_attr2->file_name_length, + CASE_SENSITIVE, vol->upcase, vol->upcase_len); + ntfs_log_trace("Done, returning %i.\n", rc); + return rc; } /* - * Get a pointer to appropriate collation function. + * Get a pointer to appropriate collation function. * - * Returns NULL if the needed function is not implemented + * Returns NULL if the needed function is not implemented */ -COLLATE ntfs_get_collate_function( COLLATION_RULES cr ) +COLLATE ntfs_get_collate_function(COLLATION_RULES cr) { - COLLATE collate; + COLLATE collate; - switch ( cr ) - { - case COLLATION_BINARY : - collate = ntfs_collate_binary; - break; - case COLLATION_FILE_NAME : - collate = ntfs_collate_file_name; - break; - case COLLATION_NTOFS_SECURITY_HASH : - collate = ntfs_collate_ntofs_security_hash; - break; - case COLLATION_NTOFS_ULONG : - collate = ntfs_collate_ntofs_ulong; - break; - case COLLATION_NTOFS_ULONGS : - collate = ntfs_collate_ntofs_ulongs; - break; - default : - errno = EOPNOTSUPP; - collate = ( COLLATE )NULL; - break; - } - return ( collate ); + switch (cr) { + case COLLATION_BINARY : + collate = ntfs_collate_binary; + break; + case COLLATION_FILE_NAME : + collate = ntfs_collate_file_name; + break; + case COLLATION_NTOFS_SECURITY_HASH : + collate = ntfs_collate_ntofs_security_hash; + break; + case COLLATION_NTOFS_ULONG : + collate = ntfs_collate_ntofs_ulong; + break; + case COLLATION_NTOFS_ULONGS : + collate = ntfs_collate_ntofs_ulongs; + break; + default : + errno = EOPNOTSUPP; + collate = (COLLATE)NULL; + break; + } + return (collate); } diff --git a/source/libntfs/collate.h b/source/libntfs/collate.h index 59aa0b09..fe383835 100644 --- a/source/libntfs/collate.h +++ b/source/libntfs/collate.h @@ -29,6 +29,6 @@ #define NTFS_COLLATION_ERROR -2 -extern COLLATE ntfs_get_collate_function( COLLATION_RULES ); +extern COLLATE ntfs_get_collate_function(COLLATION_RULES); #endif /* _NTFS_COLLATE_H */ diff --git a/source/libntfs/compat.c b/source/libntfs/compat.c index 2d1bdd49..63114a48 100644 --- a/source/libntfs/compat.c +++ b/source/libntfs/compat.c @@ -35,38 +35,33 @@ * * Returns: */ -int ffs( int x ) +int ffs(int x) { - int r = 1; + int r = 1; - if ( !x ) - return 0; - if ( !( x & 0xffff ) ) - { - x >>= 16; - r += 16; - } - if ( !( x & 0xff ) ) - { - x >>= 8; - r += 8; - } - if ( !( x & 0xf ) ) - { - x >>= 4; - r += 4; - } - if ( !( x & 3 ) ) - { - x >>= 2; - r += 2; - } - if ( !( x & 1 ) ) - { - x >>= 1; - r += 1; - } - return r; + if (!x) + return 0; + if (!(x & 0xffff)) { + x >>= 16; + r += 16; + } + if (!(x & 0xff)) { + x >>= 8; + r += 8; + } + if (!(x & 0xf)) { + x >>= 4; + r += 4; + } + if (!(x & 3)) { + x >>= 2; + r += 2; + } + if (!(x & 1)) { + x >>= 1; + r += 1; + } + return r; } #endif /* HAVE_FFS */ @@ -87,7 +82,7 @@ static const char rcsid[] = "$Id: compat.c,v 1.1.1.1.2.1 2008-08-16 15:17:44 jpa /* * Copyright (c) 1990, 1993 - * The Regents of the University of California. All rights reserved. + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -99,8 +94,8 @@ static const char rcsid[] = "$Id: compat.c,v 1.1.1.1.2.1 2008-08-16 15:17:44 jpa * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. + * This product includes software developed by the University of + * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. @@ -125,37 +120,34 @@ static const char rcsid[] = "$Id: compat.c,v 1.1.1.1.2.1 2008-08-16 15:17:44 jpa #include #endif -int daemon( int nochdir, int noclose ) -{ - int fd; +int daemon(int nochdir, int noclose) { + int fd; - switch ( fork() ) - { - case -1: - return ( -1 ); - case 0: - break; - default: - _exit( 0 ); - } + switch (fork()) { + case -1: + return (-1); + case 0: + break; + default: + _exit(0); + } - if ( setsid() == -1 ) - return ( -1 ); + if (setsid() == -1) + return (-1); - if ( !nochdir ) - ( void )chdir( "/" ); + if (!nochdir) + (void)chdir("/"); - if ( !noclose && ( fd = open( "/dev/null", O_RDWR, 0 ) ) != -1 ) - { - ( void )dup2( fd, 0 ); - ( void )dup2( fd, 1 ); - ( void )dup2( fd, 2 ); - if ( fd > 2 ) - ( void )close ( fd ); - } - return ( 0 ); + if (!noclose && (fd = open("/dev/null", O_RDWR, 0)) != -1) { + (void)dup2(fd, 0); + (void)dup2(fd, 1); + (void)dup2(fd, 2); + if (fd > 2) + (void)close (fd); + } + return (0); } -/* +/* * End: src/lib/libresolv2/common/bsd/daemon.c *************************************************************/ #endif /* HAVE_DAEMON */ @@ -177,7 +169,7 @@ static const char rcsid[] = "$Id: compat.c,v 1.1.1.1.2.1 2008-08-16 15:17:44 jpa /* * Copyright (c) 1990, 1993 - * The Regents of the University of California. All rights reserved. + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -189,8 +181,8 @@ static const char rcsid[] = "$Id: compat.c,v 1.1.1.1.2.1 2008-08-16 15:17:44 jpa * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. + * This product includes software developed by the University of + * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. @@ -217,7 +209,7 @@ static const char rcsid[] = "$Id: compat.c,v 1.1.1.1.2.1 2008-08-16 15:17:44 jpa /* * Get next token from string *stringp, where tokens are possibly-empty - * strings separated by characters from delim. + * strings separated by characters from delim. * * Writes NULs into the string at *stringp to end tokens. * delim need not remain constant from call to call. @@ -226,37 +218,32 @@ static const char rcsid[] = "$Id: compat.c,v 1.1.1.1.2.1 2008-08-16 15:17:44 jpa * * If *stringp is NULL, strsep returns NULL. */ -char *strsep( char **stringp, const char *delim ) -{ - char *s; - const char *spanp; - int c, sc; - char *tok; +char *strsep(char **stringp, const char *delim) { + char *s; + const char *spanp; + int c, sc; + char *tok; - if ( ( s = *stringp ) == NULL ) - return ( NULL ); - for ( tok = s;; ) - { - c = *s++; - spanp = delim; - do - { - if ( ( sc = *spanp++ ) == c ) - { - if ( c == 0 ) - s = NULL; - else - s[-1] = 0; - *stringp = s; - return ( tok ); - } - } - while ( sc != 0 ); - } - /* NOTREACHED */ + if ((s = *stringp) == NULL) + return (NULL); + for (tok = s;;) { + c = *s++; + spanp = delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) + s = NULL; + else + s[-1] = 0; + *stringp = s; + return (tok); + } + } while (sc != 0); + } + /* NOTREACHED */ } -/* +/* * End: src/lib/libresolv2/common/bsd/strsep.c *************************************************************/ #endif /* HAVE_STRSEP */ diff --git a/source/libntfs/compat.h b/source/libntfs/compat.h index 5e854c27..957752a0 100644 --- a/source/libntfs/compat.h +++ b/source/libntfs/compat.h @@ -36,38 +36,38 @@ #endif #ifndef HAVE_FFS -extern int ffs( int i ); +extern int ffs(int i); #endif /* HAVE_FFS */ #ifndef HAVE_DAEMON -extern int daemon( int nochdir, int noclose ); +extern int daemon(int nochdir, int noclose); #endif /* HAVE_DAEMON */ #ifndef HAVE_STRSEP -extern char *strsep( char **stringp, const char *delim ); +extern char *strsep(char **stringp, const char *delim); #endif /* HAVE_STRSEP */ #ifdef WINDOWS -#define HAVE_STDIO_H /* mimic config.h */ +#define HAVE_STDIO_H /* mimic config.h */ #define HAVE_STDARG_H -#define atoll _atoi64 -#define fdatasync commit -#define __inline__ inline -#define __attribute__(X) /*nothing*/ +#define atoll _atoi64 +#define fdatasync commit +#define __inline__ inline +#define __attribute__(X) /*nothing*/ #else /* !defined WINDOWS */ #ifndef O_BINARY -#define O_BINARY 0 /* unix is binary by default */ +#define O_BINARY 0 /* unix is binary by default */ #endif #ifdef GEKKO #include "mem_allocate.h" -#define XATTR_CREATE 1 +#define XATTR_CREATE 1 #define XATTR_REPLACE 2 #define MINORBITS 20 @@ -75,7 +75,7 @@ extern char *strsep( char **stringp, const char *delim ); #define major(dev) ((unsigned int) ((dev) >> MINORBITS)) #define minor(dev) ((unsigned int) ((dev) & MINORMASK)) -#define mkdev(ma,mi) (((ma) << MINORBITS) | (mi)) +#define mkdev(ma,mi) (((ma) << MINORBITS) | (mi)) #define random rand #endif /* defined GEKKO */ diff --git a/source/libntfs/compress.c b/source/libntfs/compress.c index 986685e5..fbd30ba9 100644 --- a/source/libntfs/compress.c +++ b/source/libntfs/compress.c @@ -1,6 +1,6 @@ /** * compress.c - Compressed attribute handling code. Originated from the Linux-NTFS - * project. + * project. * * Copyright (c) 2004-2005 Anton Altaparmakov * Copyright (c) 2004-2006 Szabolcs Szakacsits @@ -28,7 +28,7 @@ * this was put into public domain in 1988 by Haruhiko OKUMURA). * * LZHUF.C English version 1.0 - * Based on Japanese version 29-NOV-1988 + * Based on Japanese version 29-NOV-1988 * LZSS coded by Haruhiko OKUMURA * Adaptive Huffman Coding coded by Haruyasu YOSHIZAKI * Edited and translated to English by Kenji RIKITAKE @@ -65,329 +65,291 @@ /** * enum ntfs_compression_constants - constants used in the compression code */ -typedef enum -{ - /* Token types and access mask. */ - NTFS_SYMBOL_TOKEN = 0, - NTFS_PHRASE_TOKEN = 1, - NTFS_TOKEN_MASK = 1, +typedef enum { + /* Token types and access mask. */ + NTFS_SYMBOL_TOKEN = 0, + NTFS_PHRASE_TOKEN = 1, + NTFS_TOKEN_MASK = 1, - /* Compression sub-block constants. */ - NTFS_SB_SIZE_MASK = 0x0fff, - NTFS_SB_SIZE = 0x1000, - NTFS_SB_IS_COMPRESSED = 0x8000, + /* Compression sub-block constants. */ + NTFS_SB_SIZE_MASK = 0x0fff, + NTFS_SB_SIZE = 0x1000, + NTFS_SB_IS_COMPRESSED = 0x8000, } ntfs_compression_constants; #define THRESHOLD 3 /* minimal match length for compression */ #define NIL NTFS_SB_SIZE /* End of tree's node */ -struct COMPRESS_CONTEXT -{ - const unsigned char *inbuf; - unsigned int len; - unsigned int nbt; - int match_position; - unsigned int match_length; - u16 lson[NTFS_SB_SIZE + 1]; - u16 rson[NTFS_SB_SIZE + 257]; - u16 dad[NTFS_SB_SIZE + 1]; +struct COMPRESS_CONTEXT { + const unsigned char *inbuf; + unsigned int len; + unsigned int nbt; + int match_position; + unsigned int match_length; + u16 lson[NTFS_SB_SIZE + 1]; + u16 rson[NTFS_SB_SIZE + 257]; + u16 dad[NTFS_SB_SIZE + 1]; } ; /* - * Initialize the match tree + * Initialize the match tree */ -static void ntfs_init_compress_tree( struct COMPRESS_CONTEXT *pctx ) +static void ntfs_init_compress_tree(struct COMPRESS_CONTEXT *pctx) { - int i; + int i; - for ( i = NTFS_SB_SIZE + 1; i <= NTFS_SB_SIZE + 256; i++ ) - pctx->rson[i] = NIL; /* root */ - for ( i = 0; i < NTFS_SB_SIZE; i++ ) - pctx->dad[i] = NIL; /* node */ + for (i = NTFS_SB_SIZE + 1; i <= NTFS_SB_SIZE + 256; i++) + pctx->rson[i] = NIL; /* root */ + for (i = 0; i < NTFS_SB_SIZE; i++) + pctx->dad[i] = NIL; /* node */ } /* - * Insert a new node into match tree for quickly locating - * further similar strings + * Insert a new node into match tree for quickly locating + * further similar strings */ -static void ntfs_new_node ( struct COMPRESS_CONTEXT *pctx, - unsigned int r ) +static void ntfs_new_node (struct COMPRESS_CONTEXT *pctx, + unsigned int r) { - unsigned int pp; - BOOL less; - BOOL done; - const unsigned char *key; - int c; - unsigned long mxi; - unsigned int mxl; + unsigned int pp; + BOOL less; + BOOL done; + const unsigned char *key; + int c; + unsigned long mxi; + unsigned int mxl; - mxl = ( 1 << ( 16 - pctx->nbt ) ) + 2; - less = FALSE; - done = FALSE; - key = &pctx->inbuf[r]; - pp = NTFS_SB_SIZE + 1 + key[0]; - pctx->rson[r] = pctx->lson[r] = NIL; - pctx->match_length = 0; - do - { - if ( !less ) - { - if ( pctx->rson[pp] != NIL ) - pp = pctx->rson[pp]; - else - { - pctx->rson[pp] = r; - pctx->dad[r] = pp; - done = TRUE; - } - } - else - { - if ( pctx->lson[pp] != NIL ) - pp = pctx->lson[pp]; - else - { - pctx->lson[pp] = r; - pctx->dad[r] = pp; - done = TRUE; - } - } - if ( !done ) - { - register unsigned long i; - register const unsigned char *p1, *p2; + mxl = (1 << (16 - pctx->nbt)) + 2; + less = FALSE; + done = FALSE; + key = &pctx->inbuf[r]; + pp = NTFS_SB_SIZE + 1 + key[0]; + pctx->rson[r] = pctx->lson[r] = NIL; + pctx->match_length = 0; + do { + if (!less) { + if (pctx->rson[pp] != NIL) + pp = pctx->rson[pp]; + else { + pctx->rson[pp] = r; + pctx->dad[r] = pp; + done = TRUE; + } + } else { + if (pctx->lson[pp] != NIL) + pp = pctx->lson[pp]; + else { + pctx->lson[pp] = r; + pctx->dad[r] = pp; + done = TRUE; + } + } + if (!done) { + register unsigned long i; + register const unsigned char *p1,*p2; - i = 1; - mxi = NTFS_SB_SIZE - r; - if ( mxi < 2 ) - less = FALSE; - else - { - p1 = key; - p2 = &pctx->inbuf[pp]; - /* this loop has a significant impact on performances */ - do - { - } - while ( ( p1[i] == p2[i] ) && ( ++i < mxi ) ); - less = ( i < mxi ) && ( p1[i] < p2[i] ); - } - if ( i >= THRESHOLD ) - { - if ( i > pctx->match_length ) - { - pctx->match_position = - r - pp + 2 * NTFS_SB_SIZE - 1; - if ( ( pctx->match_length = i ) > mxl ) - { - i = pctx->rson[pp]; - pctx->rson[r] = i; - pctx->dad[i] = r; - i = pctx->lson[pp]; - pctx->lson[r] = i; - pctx->dad[i] = r; - i = pctx->dad[pp]; - pctx->dad[r] = i; - if ( pctx->rson[i] == pp ) - pctx->rson[i] = r; - else - pctx->lson[i] = r; - /* remove pp */ - pctx->dad[pp] = NIL; - done = TRUE; - pctx->match_length = mxl; - } - } - else if ( ( i == pctx->match_length ) - && ( ( c = ( r - pp + 2 * NTFS_SB_SIZE - 1 ) ) - < pctx->match_position ) ) - pctx->match_position = c; - } - } - } - while ( !done ); + i = 1; + mxi = NTFS_SB_SIZE - r; + if (mxi < 2) + less = FALSE; + else { + p1 = key; + p2 = &pctx->inbuf[pp]; + /* this loop has a significant impact on performances */ + do { + } while ((p1[i] == p2[i]) && (++i < mxi)); + less = (i < mxi) && (p1[i] < p2[i]); + } + if (i >= THRESHOLD) { + if (i > pctx->match_length) { + pctx->match_position = + r - pp + 2*NTFS_SB_SIZE - 1; + if ((pctx->match_length = i) > mxl) { + i = pctx->rson[pp]; + pctx->rson[r] = i; + pctx->dad[i] = r; + i = pctx->lson[pp]; + pctx->lson[r] = i; + pctx->dad[i] = r; + i = pctx->dad[pp]; + pctx->dad[r] = i; + if (pctx->rson[i] == pp) + pctx->rson[i] = r; + else + pctx->lson[i] = r; + /* remove pp */ + pctx->dad[pp] = NIL; + done = TRUE; + pctx->match_length = mxl; + } + } else + if ((i == pctx->match_length) + && ((c = (r - pp + 2*NTFS_SB_SIZE - 1)) + < pctx->match_position)) + pctx->match_position = c; + } + } + } while (!done); } /* - * Search for the longest previous string matching the - * current one + * Search for the longest previous string matching the + * current one * - * Returns the end of the longest current string which matched - * or zero if there was a bug + * Returns the end of the longest current string which matched + * or zero if there was a bug */ -static unsigned int ntfs_nextmatch( struct COMPRESS_CONTEXT *pctx, - unsigned int rr, int dd ) +static unsigned int ntfs_nextmatch(struct COMPRESS_CONTEXT *pctx, + unsigned int rr, int dd) { - unsigned int bestlen = 0; + unsigned int bestlen = 0; - do - { - rr++; - if ( pctx->match_length > 0 ) - pctx->match_length--; - if ( !pctx->len ) - { - ntfs_log_error( "compress bug : void run\n" ); - goto bug; - } - if ( --pctx->len ) - { - if ( rr >= NTFS_SB_SIZE ) - { - ntfs_log_error( "compress bug : buffer overflow\n" ); - goto bug; - } - if ( ( ( rr + bestlen ) < NTFS_SB_SIZE ) ) - { - while ( ( unsigned int )( 1 << pctx->nbt ) - <= ( rr - 1 ) ) - pctx->nbt++; - ntfs_new_node( pctx, rr ); - if ( pctx->match_length > bestlen ) - bestlen = pctx->match_length; - } - else if ( dd > 0 ) - { - rr += dd; - if ( ( int )pctx->match_length > dd ) - pctx->match_length -= dd; - else - pctx->match_length = 0; - if ( ( int )pctx->len < dd ) - { - ntfs_log_error( "compress bug : run overflows\n" ); - goto bug; - } - pctx->len -= dd; - dd = 0; - } - } - } - while ( dd-- > 0 ); - return ( rr ); + do { + rr++; + if (pctx->match_length > 0) + pctx->match_length--; + if (!pctx->len) { + ntfs_log_error("compress bug : void run\n"); + goto bug; + } + if (--pctx->len) { + if (rr >= NTFS_SB_SIZE) { + ntfs_log_error("compress bug : buffer overflow\n"); + goto bug; + } + if (((rr + bestlen) < NTFS_SB_SIZE)) { + while ((unsigned int)(1 << pctx->nbt) + <= (rr - 1)) + pctx->nbt++; + ntfs_new_node(pctx,rr); + if (pctx->match_length > bestlen) + bestlen = pctx->match_length; + } else + if (dd > 0) { + rr += dd; + if ((int)pctx->match_length > dd) + pctx->match_length -= dd; + else + pctx->match_length = 0; + if ((int)pctx->len < dd) { + ntfs_log_error("compress bug : run overflows\n"); + goto bug; + } + pctx->len -= dd; + dd = 0; + } + } + } while (dd-- > 0); + return (rr); bug : - return ( 0 ); + return (0); } /* - * Compress an input block + * Compress an input block * - * Returns the size of the compressed block (including header) - * or zero if there was an error + * Returns the size of the compressed block (including header) + * or zero if there was an error */ -static unsigned int ntfs_compress_block( const char *inbuf, - unsigned int size, char *outbuf ) +static unsigned int ntfs_compress_block(const char *inbuf, + unsigned int size, char *outbuf) { - struct COMPRESS_CONTEXT *pctx; - char *ptag; - int dd; - unsigned int rr; - unsigned int last_match_length; - unsigned int q; - unsigned int xout; - unsigned int ntag; + struct COMPRESS_CONTEXT *pctx; + char *ptag; + int dd; + unsigned int rr; + unsigned int last_match_length; + unsigned int q; + unsigned int xout; + unsigned int ntag; - pctx = ( struct COMPRESS_CONTEXT* )malloc( sizeof( struct COMPRESS_CONTEXT ) ); - if ( pctx ) - { - pctx->inbuf = ( const unsigned char* )inbuf; - ntfs_init_compress_tree( pctx ); - xout = 2; - ntag = 0; - ptag = &outbuf[xout++]; - *ptag = 0; - rr = 0; - pctx->nbt = 4; - pctx->len = size; - pctx->match_length = 0; - ntfs_new_node( pctx, 0 ); - do - { - if ( pctx->match_length > pctx->len ) - pctx->match_length = pctx->len; - if ( pctx->match_length < THRESHOLD ) - { - pctx->match_length = 1; - if ( ntag >= 8 ) - { - ntag = 0; - ptag = &outbuf[xout++]; - *ptag = 0; - } - outbuf[xout++] = inbuf[rr]; - ntag++; - } - else - { - while ( ( unsigned int )( 1 << pctx->nbt ) - <= ( rr - 1 ) ) - pctx->nbt++; - q = ( pctx->match_position << ( 16 - pctx->nbt ) ) - + pctx->match_length - THRESHOLD; - if ( ntag >= 8 ) - { - ntag = 0; - ptag = &outbuf[xout++]; - *ptag = 0; - } - *ptag |= 1 << ntag++; - outbuf[xout++] = q & 255; - outbuf[xout++] = ( q >> 8 ) & 255; - } - last_match_length = pctx->match_length; - dd = last_match_length; - if ( dd-- > 0 ) - { - rr = ntfs_nextmatch( pctx, rr, dd ); - if ( !rr ) - goto bug; - } - /* - * stop if input is exhausted or output has exceeded - * the maximum size. Two extra bytes have to be - * reserved in output buffer, as 3 bytes may be - * output in a loop. - */ - } - while ( ( pctx->len > 0 ) - && ( rr < size ) && ( xout < ( NTFS_SB_SIZE + 2 ) ) ); - /* uncompressed must be full size, so accept if better */ - if ( xout < ( NTFS_SB_SIZE + 2 ) ) - { - outbuf[0] = ( xout - 3 ) & 255; - outbuf[1] = 0xb0 + ( ( ( xout - 3 ) >> 8 ) & 15 ); - } - else - { - memcpy( &outbuf[2], inbuf, size ); - if ( size < NTFS_SB_SIZE ) - memset( &outbuf[size+2], 0, NTFS_SB_SIZE - size ); - outbuf[0] = 0xff; - outbuf[1] = 0x3f; - xout = NTFS_SB_SIZE + 2; - } - free( pctx ); - } - else - { - xout = 0; - errno = ENOMEM; - } - return ( xout ); /* 0 for an error, > size if cannot compress */ + pctx = (struct COMPRESS_CONTEXT*)malloc(sizeof(struct COMPRESS_CONTEXT)); + if (pctx) { + pctx->inbuf = (const unsigned char*)inbuf; + ntfs_init_compress_tree(pctx); + xout = 2; + ntag = 0; + ptag = &outbuf[xout++]; + *ptag = 0; + rr = 0; + pctx->nbt = 4; + pctx->len = size; + pctx->match_length = 0; + ntfs_new_node(pctx,0); + do { + if (pctx->match_length > pctx->len) + pctx->match_length = pctx->len; + if (pctx->match_length < THRESHOLD) { + pctx->match_length = 1; + if (ntag >= 8) { + ntag = 0; + ptag = &outbuf[xout++]; + *ptag = 0; + } + outbuf[xout++] = inbuf[rr]; + ntag++; + } else { + while ((unsigned int)(1 << pctx->nbt) + <= (rr - 1)) + pctx->nbt++; + q = (pctx->match_position << (16 - pctx->nbt)) + + pctx->match_length - THRESHOLD; + if (ntag >= 8) { + ntag = 0; + ptag = &outbuf[xout++]; + *ptag = 0; + } + *ptag |= 1 << ntag++; + outbuf[xout++] = q & 255; + outbuf[xout++] = (q >> 8) & 255; + } + last_match_length = pctx->match_length; + dd = last_match_length; + if (dd-- > 0) { + rr = ntfs_nextmatch(pctx,rr,dd); + if (!rr) + goto bug; + } + /* + * stop if input is exhausted or output has exceeded + * the maximum size. Two extra bytes have to be + * reserved in output buffer, as 3 bytes may be + * output in a loop. + */ + } while ((pctx->len > 0) + && (rr < size) && (xout < (NTFS_SB_SIZE + 2))); + /* uncompressed must be full size, so accept if better */ + if (xout < (NTFS_SB_SIZE + 2)) { + outbuf[0] = (xout - 3) & 255; + outbuf[1] = 0xb0 + (((xout - 3) >> 8) & 15); + } else { + memcpy(&outbuf[2],inbuf,size); + if (size < NTFS_SB_SIZE) + memset(&outbuf[size+2],0,NTFS_SB_SIZE - size); + outbuf[0] = 0xff; + outbuf[1] = 0x3f; + xout = NTFS_SB_SIZE + 2; + } + free(pctx); + } else { + xout = 0; + errno = ENOMEM; + } + return (xout); /* 0 for an error, > size if cannot compress */ bug : - return ( 0 ); + return (0); } /** * ntfs_decompress - decompress a compression block into an array of pages - * @dest: buffer to which to write the decompressed data - * @dest_size: size of buffer @dest in bytes - * @cb_start: compression block to decompress - * @cb_size: size of compression block @cb_start in bytes + * @dest: buffer to which to write the decompressed data + * @dest_size: size of buffer @dest in bytes + * @cb_start: compression block to decompress + * @cb_size: size of compression block @cb_start in bytes * * This decompresses the compression block @cb_start into the destination * buffer @dest. @@ -397,182 +359,173 @@ bug : * * Return 0 if success or -EOVERFLOW on error in the compressed stream. */ -static int ntfs_decompress( u8 *dest, const u32 dest_size, - u8 *const cb_start, const u32 cb_size ) +static int ntfs_decompress(u8 *dest, const u32 dest_size, + u8 *const cb_start, const u32 cb_size) { - /* - * Pointers into the compressed data, i.e. the compression block (cb), - * and the therein contained sub-blocks (sb). - */ - u8 *cb_end = cb_start + cb_size; /* End of cb. */ - u8 *cb = cb_start; /* Current position in cb. */ - u8 *cb_sb_start = cb; /* Beginning of the current sb in the cb. */ - u8 *cb_sb_end; /* End of current sb / beginning of next sb. */ - /* Variables for uncompressed data / destination. */ - u8 *dest_end = dest + dest_size; /* End of dest buffer. */ - u8 *dest_sb_start; /* Start of current sub-block in dest. */ - u8 *dest_sb_end; /* End of current sb in dest. */ - /* Variables for tag and token parsing. */ - u8 tag; /* Current tag. */ - int token; /* Loop counter for the eight tokens in tag. */ + /* + * Pointers into the compressed data, i.e. the compression block (cb), + * and the therein contained sub-blocks (sb). + */ + u8 *cb_end = cb_start + cb_size; /* End of cb. */ + u8 *cb = cb_start; /* Current position in cb. */ + u8 *cb_sb_start = cb; /* Beginning of the current sb in the cb. */ + u8 *cb_sb_end; /* End of current sb / beginning of next sb. */ + /* Variables for uncompressed data / destination. */ + u8 *dest_end = dest + dest_size; /* End of dest buffer. */ + u8 *dest_sb_start; /* Start of current sub-block in dest. */ + u8 *dest_sb_end; /* End of current sb in dest. */ + /* Variables for tag and token parsing. */ + u8 tag; /* Current tag. */ + int token; /* Loop counter for the eight tokens in tag. */ - ntfs_log_trace( "Entering, cb_size = 0x%x.\n", ( unsigned )cb_size ); + ntfs_log_trace("Entering, cb_size = 0x%x.\n", (unsigned)cb_size); do_next_sb: - ntfs_log_debug( "Beginning sub-block at offset = %d in the cb.\n", - ( int )( cb - cb_start ) ); - /* - * Have we reached the end of the compression block or the end of the - * decompressed data? The latter can happen for example if the current - * position in the compression block is one byte before its end so the - * first two checks do not detect it. - */ - if ( cb == cb_end || !le16_to_cpup( ( le16* )cb ) || dest == dest_end ) - { - ntfs_log_debug( "Completed. Returning success (0).\n" ); - return 0; - } - /* Setup offset for the current sub-block destination. */ - dest_sb_start = dest; - dest_sb_end = dest + NTFS_SB_SIZE; - /* Check that we are still within allowed boundaries. */ - if ( dest_sb_end > dest_end ) - goto return_overflow; - /* Does the minimum size of a compressed sb overflow valid range? */ - if ( cb + 6 > cb_end ) - goto return_overflow; - /* Setup the current sub-block source pointers and validate range. */ - cb_sb_start = cb; - cb_sb_end = cb_sb_start + ( le16_to_cpup( ( le16* )cb ) & NTFS_SB_SIZE_MASK ) - + 3; - if ( cb_sb_end > cb_end ) - goto return_overflow; - /* Now, we are ready to process the current sub-block (sb). */ - if ( !( le16_to_cpup( ( le16* )cb ) & NTFS_SB_IS_COMPRESSED ) ) - { - ntfs_log_debug( "Found uncompressed sub-block.\n" ); - /* This sb is not compressed, just copy it into destination. */ - /* Advance source position to first data byte. */ - cb += 2; - /* An uncompressed sb must be full size. */ - if ( cb_sb_end - cb != NTFS_SB_SIZE ) - goto return_overflow; - /* Copy the block and advance the source position. */ - memcpy( dest, cb, NTFS_SB_SIZE ); - cb += NTFS_SB_SIZE; - /* Advance destination position to next sub-block. */ - dest += NTFS_SB_SIZE; - goto do_next_sb; - } - ntfs_log_debug( "Found compressed sub-block.\n" ); - /* This sb is compressed, decompress it into destination. */ - /* Forward to the first tag in the sub-block. */ - cb += 2; + ntfs_log_debug("Beginning sub-block at offset = %d in the cb.\n", + (int)(cb - cb_start)); + /* + * Have we reached the end of the compression block or the end of the + * decompressed data? The latter can happen for example if the current + * position in the compression block is one byte before its end so the + * first two checks do not detect it. + */ + if (cb == cb_end || !le16_to_cpup((le16*)cb) || dest == dest_end) { + ntfs_log_debug("Completed. Returning success (0).\n"); + return 0; + } + /* Setup offset for the current sub-block destination. */ + dest_sb_start = dest; + dest_sb_end = dest + NTFS_SB_SIZE; + /* Check that we are still within allowed boundaries. */ + if (dest_sb_end > dest_end) + goto return_overflow; + /* Does the minimum size of a compressed sb overflow valid range? */ + if (cb + 6 > cb_end) + goto return_overflow; + /* Setup the current sub-block source pointers and validate range. */ + cb_sb_start = cb; + cb_sb_end = cb_sb_start + (le16_to_cpup((le16*)cb) & NTFS_SB_SIZE_MASK) + + 3; + if (cb_sb_end > cb_end) + goto return_overflow; + /* Now, we are ready to process the current sub-block (sb). */ + if (!(le16_to_cpup((le16*)cb) & NTFS_SB_IS_COMPRESSED)) { + ntfs_log_debug("Found uncompressed sub-block.\n"); + /* This sb is not compressed, just copy it into destination. */ + /* Advance source position to first data byte. */ + cb += 2; + /* An uncompressed sb must be full size. */ + if (cb_sb_end - cb != NTFS_SB_SIZE) + goto return_overflow; + /* Copy the block and advance the source position. */ + memcpy(dest, cb, NTFS_SB_SIZE); + cb += NTFS_SB_SIZE; + /* Advance destination position to next sub-block. */ + dest += NTFS_SB_SIZE; + goto do_next_sb; + } + ntfs_log_debug("Found compressed sub-block.\n"); + /* This sb is compressed, decompress it into destination. */ + /* Forward to the first tag in the sub-block. */ + cb += 2; do_next_tag: - if ( cb == cb_sb_end ) - { - /* Check if the decompressed sub-block was not full-length. */ - if ( dest < dest_sb_end ) - { - int nr_bytes = dest_sb_end - dest; + if (cb == cb_sb_end) { + /* Check if the decompressed sub-block was not full-length. */ + if (dest < dest_sb_end) { + int nr_bytes = dest_sb_end - dest; - ntfs_log_debug( "Filling incomplete sub-block with zeroes.\n" ); - /* Zero remainder and update destination position. */ - memset( dest, 0, nr_bytes ); - dest += nr_bytes; - } - /* We have finished the current sub-block. */ - goto do_next_sb; - } - /* Check we are still in range. */ - if ( cb > cb_sb_end || dest > dest_sb_end ) - goto return_overflow; - /* Get the next tag and advance to first token. */ - tag = *cb++; - /* Parse the eight tokens described by the tag. */ - for ( token = 0; token < 8; token++, tag >>= 1 ) - { - u16 lg, pt, length, max_non_overlap; - register u16 i; - u8 *dest_back_addr; + ntfs_log_debug("Filling incomplete sub-block with zeroes.\n"); + /* Zero remainder and update destination position. */ + memset(dest, 0, nr_bytes); + dest += nr_bytes; + } + /* We have finished the current sub-block. */ + goto do_next_sb; + } + /* Check we are still in range. */ + if (cb > cb_sb_end || dest > dest_sb_end) + goto return_overflow; + /* Get the next tag and advance to first token. */ + tag = *cb++; + /* Parse the eight tokens described by the tag. */ + for (token = 0; token < 8; token++, tag >>= 1) { + u16 lg, pt, length, max_non_overlap; + register u16 i; + u8 *dest_back_addr; - /* Check if we are done / still in range. */ - if ( cb >= cb_sb_end || dest > dest_sb_end ) - break; - /* Determine token type and parse appropriately.*/ - if ( ( tag & NTFS_TOKEN_MASK ) == NTFS_SYMBOL_TOKEN ) - { - /* - * We have a symbol token, copy the symbol across, and - * advance the source and destination positions. - */ - *dest++ = *cb++; - /* Continue with the next token. */ - continue; - } - /* - * We have a phrase token. Make sure it is not the first tag in - * the sb as this is illegal and would confuse the code below. - */ - if ( dest == dest_sb_start ) - goto return_overflow; - /* - * Determine the number of bytes to go back (p) and the number - * of bytes to copy (l). We use an optimized algorithm in which - * we first calculate log2(current destination position in sb), - * which allows determination of l and p in O(1) rather than - * O(n). We just need an arch-optimized log2() function now. - */ - lg = 0; - for ( i = dest - dest_sb_start - 1; i >= 0x10; i >>= 1 ) - lg++; - /* Get the phrase token into i. */ - pt = le16_to_cpup( ( le16* )cb ); - /* - * Calculate starting position of the byte sequence in - * the destination using the fact that p = (pt >> (12 - lg)) + 1 - * and make sure we don't go too far back. - */ - dest_back_addr = dest - ( pt >> ( 12 - lg ) ) - 1; - if ( dest_back_addr < dest_sb_start ) - goto return_overflow; - /* Now calculate the length of the byte sequence. */ - length = ( pt & ( 0xfff >> lg ) ) + 3; - /* Verify destination is in range. */ - if ( dest + length > dest_sb_end ) - goto return_overflow; - /* The number of non-overlapping bytes. */ - max_non_overlap = dest - dest_back_addr; - if ( length <= max_non_overlap ) - { - /* The byte sequence doesn't overlap, just copy it. */ - memcpy( dest, dest_back_addr, length ); - /* Advance destination pointer. */ - dest += length; - } - else - { - /* - * The byte sequence does overlap, copy non-overlapping - * part and then do a slow byte by byte copy for the - * overlapping part. Also, advance the destination - * pointer. - */ - memcpy( dest, dest_back_addr, max_non_overlap ); - dest += max_non_overlap; - dest_back_addr += max_non_overlap; - length -= max_non_overlap; - while ( length-- ) - *dest++ = *dest_back_addr++; - } - /* Advance source position and continue with the next token. */ - cb += 2; - } - /* No tokens left in the current tag. Continue with the next tag. */ - goto do_next_tag; + /* Check if we are done / still in range. */ + if (cb >= cb_sb_end || dest > dest_sb_end) + break; + /* Determine token type and parse appropriately.*/ + if ((tag & NTFS_TOKEN_MASK) == NTFS_SYMBOL_TOKEN) { + /* + * We have a symbol token, copy the symbol across, and + * advance the source and destination positions. + */ + *dest++ = *cb++; + /* Continue with the next token. */ + continue; + } + /* + * We have a phrase token. Make sure it is not the first tag in + * the sb as this is illegal and would confuse the code below. + */ + if (dest == dest_sb_start) + goto return_overflow; + /* + * Determine the number of bytes to go back (p) and the number + * of bytes to copy (l). We use an optimized algorithm in which + * we first calculate log2(current destination position in sb), + * which allows determination of l and p in O(1) rather than + * O(n). We just need an arch-optimized log2() function now. + */ + lg = 0; + for (i = dest - dest_sb_start - 1; i >= 0x10; i >>= 1) + lg++; + /* Get the phrase token into i. */ + pt = le16_to_cpup((le16*)cb); + /* + * Calculate starting position of the byte sequence in + * the destination using the fact that p = (pt >> (12 - lg)) + 1 + * and make sure we don't go too far back. + */ + dest_back_addr = dest - (pt >> (12 - lg)) - 1; + if (dest_back_addr < dest_sb_start) + goto return_overflow; + /* Now calculate the length of the byte sequence. */ + length = (pt & (0xfff >> lg)) + 3; + /* Verify destination is in range. */ + if (dest + length > dest_sb_end) + goto return_overflow; + /* The number of non-overlapping bytes. */ + max_non_overlap = dest - dest_back_addr; + if (length <= max_non_overlap) { + /* The byte sequence doesn't overlap, just copy it. */ + memcpy(dest, dest_back_addr, length); + /* Advance destination pointer. */ + dest += length; + } else { + /* + * The byte sequence does overlap, copy non-overlapping + * part and then do a slow byte by byte copy for the + * overlapping part. Also, advance the destination + * pointer. + */ + memcpy(dest, dest_back_addr, max_non_overlap); + dest += max_non_overlap; + dest_back_addr += max_non_overlap; + length -= max_non_overlap; + while (length--) + *dest++ = *dest_back_addr++; + } + /* Advance source position and continue with the next token. */ + cb += 2; + } + /* No tokens left in the current tag. Continue with the next tag. */ + goto do_next_tag; return_overflow: - errno = EOVERFLOW; - ntfs_log_perror( "Failed to decompress file" ); - return -1; + errno = EOVERFLOW; + ntfs_log_perror("Failed to decompress file"); + return -1; } /** @@ -590,52 +543,50 @@ return_overflow: * code. Might be a bit confusing to debug but there really should never be * errors coming from here. */ -static BOOL ntfs_is_cb_compressed( ntfs_attr *na, runlist_element *rl, - VCN cb_start_vcn, int cb_clusters ) +static BOOL ntfs_is_cb_compressed(ntfs_attr *na, runlist_element *rl, + VCN cb_start_vcn, int cb_clusters) { - /* - * The simplest case: the run starting at @cb_start_vcn contains - * @cb_clusters clusters which are all not sparse, thus the cb is not - * compressed. - */ + /* + * The simplest case: the run starting at @cb_start_vcn contains + * @cb_clusters clusters which are all not sparse, thus the cb is not + * compressed. + */ restart: - cb_clusters -= rl->length - ( cb_start_vcn - rl->vcn ); - while ( cb_clusters > 0 ) - { - /* Go to the next run. */ - rl++; - /* Map the next runlist fragment if it is not mapped. */ - if ( rl->lcn < LCN_HOLE || !rl->length ) - { - cb_start_vcn = rl->vcn; - rl = ntfs_attr_find_vcn( na, rl->vcn ); - if ( !rl || rl->lcn < LCN_HOLE || !rl->length ) - return TRUE; - /* - * If the runs were merged need to deal with the - * resulting partial run so simply restart. - */ - if ( rl->vcn < cb_start_vcn ) - goto restart; - } - /* If the current run is sparse, the cb is compressed. */ - if ( rl->lcn == LCN_HOLE ) - return TRUE; - /* If the whole cb is not sparse, it is not compressed. */ - if ( rl->length >= cb_clusters ) - return FALSE; - cb_clusters -= rl->length; - }; - /* All cb_clusters were not sparse thus the cb is not compressed. */ - return FALSE; + cb_clusters -= rl->length - (cb_start_vcn - rl->vcn); + while (cb_clusters > 0) { + /* Go to the next run. */ + rl++; + /* Map the next runlist fragment if it is not mapped. */ + if (rl->lcn < LCN_HOLE || !rl->length) { + cb_start_vcn = rl->vcn; + rl = ntfs_attr_find_vcn(na, rl->vcn); + if (!rl || rl->lcn < LCN_HOLE || !rl->length) + return TRUE; + /* + * If the runs were merged need to deal with the + * resulting partial run so simply restart. + */ + if (rl->vcn < cb_start_vcn) + goto restart; + } + /* If the current run is sparse, the cb is compressed. */ + if (rl->lcn == LCN_HOLE) + return TRUE; + /* If the whole cb is not sparse, it is not compressed. */ + if (rl->length >= cb_clusters) + return FALSE; + cb_clusters -= rl->length; + }; + /* All cb_clusters were not sparse thus the cb is not compressed. */ + return FALSE; } /** * ntfs_compressed_attr_pread - read from a compressed attribute - * @na: ntfs attribute to read from - * @pos: byte position in the attribute to begin reading from - * @count: number of bytes to read - * @b: output data buffer + * @na: ntfs attribute to read from + * @pos: byte position in the attribute to begin reading from + * @count: number of bytes to read + * @b: output data buffer * * NOTE: You probably want to be using attrib.c::ntfs_attr_pread() instead. * @@ -651,542 +602,500 @@ restart: * to the return code of ntfs_pread(), or to EINVAL in case of invalid * arguments. */ -s64 ntfs_compressed_attr_pread( ntfs_attr *na, s64 pos, s64 count, void *b ) +s64 ntfs_compressed_attr_pread(ntfs_attr *na, s64 pos, s64 count, void *b) { - s64 br, to_read, ofs, total, total2; - u64 cb_size_mask; - VCN start_vcn, vcn, end_vcn; - ntfs_volume *vol; - runlist_element *rl; - u8 *dest, *cb, *cb_pos, *cb_end; - u32 cb_size; - int err; - ATTR_FLAGS data_flags; - FILE_ATTR_FLAGS compression; - unsigned int nr_cbs, cb_clusters; + s64 br, to_read, ofs, total, total2; + u64 cb_size_mask; + VCN start_vcn, vcn, end_vcn; + ntfs_volume *vol; + runlist_element *rl; + u8 *dest, *cb, *cb_pos, *cb_end; + u32 cb_size; + int err; + ATTR_FLAGS data_flags; + FILE_ATTR_FLAGS compression; + unsigned int nr_cbs, cb_clusters; - ntfs_log_trace( "Entering for inode 0x%llx, attr 0x%x, pos 0x%llx, count 0x%llx.\n", - ( unsigned long long )na->ni->mft_no, na->type, - ( long long )pos, ( long long )count ); - data_flags = na->data_flags; - compression = na->ni->flags & FILE_ATTR_COMPRESSED; - if ( !na || !na->ni || !na->ni->vol || !b - || ( ( data_flags & ATTR_COMPRESSION_MASK ) - != ATTR_IS_COMPRESSED ) - || pos < 0 || count < 0 ) - { - errno = EINVAL; - return -1; - } - /* - * Encrypted attributes are not supported. We return access denied, - * which is what Windows NT4 does, too. - */ - if ( NAttrEncrypted( na ) ) - { - errno = EACCES; - return -1; - } - if ( !count ) - return 0; - /* Truncate reads beyond end of attribute. */ - if ( pos + count > na->data_size ) - { - if ( pos >= na->data_size ) - { - return 0; - } - count = na->data_size - pos; - } - /* If it is a resident attribute, simply use ntfs_attr_pread(). */ - if ( !NAttrNonResident( na ) ) - return ntfs_attr_pread( na, pos, count, b ); - total = total2 = 0; - /* Zero out reads beyond initialized size. */ - if ( pos + count > na->initialized_size ) - { - if ( pos >= na->initialized_size ) - { - memset( b, 0, count ); - return count; - } - total2 = pos + count - na->initialized_size; - count -= total2; - memset( ( u8* )b + count, 0, total2 ); - } - vol = na->ni->vol; - cb_size = na->compression_block_size; - cb_size_mask = cb_size - 1UL; - cb_clusters = na->compression_block_clusters; - - /* Need a temporary buffer for each loaded compression block. */ - cb = ( u8* )ntfs_malloc( cb_size ); - if ( !cb ) - return -1; - - /* Need a temporary buffer for each uncompressed block. */ - dest = ( u8* )ntfs_malloc( cb_size ); - if ( !dest ) - { - free( cb ); - return -1; - } - /* - * The first vcn in the first compression block (cb) which we need to - * decompress. - */ - start_vcn = ( pos & ~cb_size_mask ) >> vol->cluster_size_bits; - /* Offset in the uncompressed cb at which to start reading data. */ - ofs = pos & cb_size_mask; - /* - * The first vcn in the cb after the last cb which we need to - * decompress. - */ - end_vcn = ( ( pos + count + cb_size - 1 ) & ~cb_size_mask ) >> - vol->cluster_size_bits; - /* Number of compression blocks (cbs) in the wanted vcn range. */ - nr_cbs = ( end_vcn - start_vcn ) << vol->cluster_size_bits >> - na->compression_block_size_bits; - cb_end = cb + cb_size; + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, pos 0x%llx, count 0x%llx.\n", + (unsigned long long)na->ni->mft_no, na->type, + (long long)pos, (long long)count); + data_flags = na->data_flags; + compression = na->ni->flags & FILE_ATTR_COMPRESSED; + if (!na || !na->ni || !na->ni->vol || !b + || ((data_flags & ATTR_COMPRESSION_MASK) + != ATTR_IS_COMPRESSED) + || pos < 0 || count < 0) { + errno = EINVAL; + return -1; + } + /* + * Encrypted attributes are not supported. We return access denied, + * which is what Windows NT4 does, too. + */ + if (NAttrEncrypted(na)) { + errno = EACCES; + return -1; + } + if (!count) + return 0; + /* Truncate reads beyond end of attribute. */ + if (pos + count > na->data_size) { + if (pos >= na->data_size) { + return 0; + } + count = na->data_size - pos; + } + /* If it is a resident attribute, simply use ntfs_attr_pread(). */ + if (!NAttrNonResident(na)) + return ntfs_attr_pread(na, pos, count, b); + total = total2 = 0; + /* Zero out reads beyond initialized size. */ + if (pos + count > na->initialized_size) { + if (pos >= na->initialized_size) { + memset(b, 0, count); + return count; + } + total2 = pos + count - na->initialized_size; + count -= total2; + memset((u8*)b + count, 0, total2); + } + vol = na->ni->vol; + cb_size = na->compression_block_size; + cb_size_mask = cb_size - 1UL; + cb_clusters = na->compression_block_clusters; + + /* Need a temporary buffer for each loaded compression block. */ + cb = (u8*)ntfs_malloc(cb_size); + if (!cb) + return -1; + + /* Need a temporary buffer for each uncompressed block. */ + dest = (u8*)ntfs_malloc(cb_size); + if (!dest) { + free(cb); + return -1; + } + /* + * The first vcn in the first compression block (cb) which we need to + * decompress. + */ + start_vcn = (pos & ~cb_size_mask) >> vol->cluster_size_bits; + /* Offset in the uncompressed cb at which to start reading data. */ + ofs = pos & cb_size_mask; + /* + * The first vcn in the cb after the last cb which we need to + * decompress. + */ + end_vcn = ((pos + count + cb_size - 1) & ~cb_size_mask) >> + vol->cluster_size_bits; + /* Number of compression blocks (cbs) in the wanted vcn range. */ + nr_cbs = (end_vcn - start_vcn) << vol->cluster_size_bits >> + na->compression_block_size_bits; + cb_end = cb + cb_size; do_next_cb: - nr_cbs--; - cb_pos = cb; - vcn = start_vcn; - start_vcn += cb_clusters; + nr_cbs--; + cb_pos = cb; + vcn = start_vcn; + start_vcn += cb_clusters; - /* Check whether the compression block is sparse. */ - rl = ntfs_attr_find_vcn( na, vcn ); - if ( !rl || rl->lcn < LCN_HOLE ) - { - free( cb ); - free( dest ); - if ( total ) - return total; - /* FIXME: Do we want EIO or the error code? (AIA) */ - errno = EIO; - return -1; - } - if ( rl->lcn == LCN_HOLE ) - { - /* Sparse cb, zero out destination range overlapping the cb. */ - ntfs_log_debug( "Found sparse compression block.\n" ); - to_read = min( count, cb_size - ofs ); - memset( b, 0, to_read ); - ofs = 0; - total += to_read; - count -= to_read; - b = ( u8* )b + to_read; - } - else if ( !ntfs_is_cb_compressed( na, rl, vcn, cb_clusters ) ) - { - s64 tdata_size, tinitialized_size; - /* - * Uncompressed cb, read it straight into the destination range - * overlapping the cb. - */ - ntfs_log_debug( "Found uncompressed compression block.\n" ); - /* - * Read the uncompressed data into the destination buffer. - * NOTE: We cheat a little bit here by marking the attribute as - * not compressed in the ntfs_attr structure so that we can - * read the data by simply using ntfs_attr_pread(). (-8 - * NOTE: we have to modify data_size and initialized_size - * temporarily as well... - */ - to_read = min( count, cb_size - ofs ); - ofs += vcn << vol->cluster_size_bits; - NAttrClearCompressed( na ); - na->data_flags &= ~ATTR_COMPRESSION_MASK; - tdata_size = na->data_size; - tinitialized_size = na->initialized_size; - na->data_size = na->initialized_size = na->allocated_size; - do - { - br = ntfs_attr_pread( na, ofs, to_read, b ); - if ( br <= 0 ) - { - if ( !br ) - { - ntfs_log_error( "Failed to read an" - " uncompressed cluster," - " inode %lld offs 0x%llx\n", - ( long long )na->ni->mft_no, - ( long long )ofs ); - errno = EIO; - } - err = errno; - na->data_size = tdata_size; - na->initialized_size = tinitialized_size; - na->ni->flags |= compression; - na->data_flags = data_flags; - free( cb ); - free( dest ); - if ( total ) - return total; - errno = err; - return br; - } - total += br; - count -= br; - b = ( u8* )b + br; - to_read -= br; - ofs += br; - } - while ( to_read > 0 ); - na->data_size = tdata_size; - na->initialized_size = tinitialized_size; - na->ni->flags |= compression; - na->data_flags = data_flags; - ofs = 0; - } - else - { - s64 tdata_size, tinitialized_size; + /* Check whether the compression block is sparse. */ + rl = ntfs_attr_find_vcn(na, vcn); + if (!rl || rl->lcn < LCN_HOLE) { + free(cb); + free(dest); + if (total) + return total; + /* FIXME: Do we want EIO or the error code? (AIA) */ + errno = EIO; + return -1; + } + if (rl->lcn == LCN_HOLE) { + /* Sparse cb, zero out destination range overlapping the cb. */ + ntfs_log_debug("Found sparse compression block.\n"); + to_read = min(count, cb_size - ofs); + memset(b, 0, to_read); + ofs = 0; + total += to_read; + count -= to_read; + b = (u8*)b + to_read; + } else if (!ntfs_is_cb_compressed(na, rl, vcn, cb_clusters)) { + s64 tdata_size, tinitialized_size; + /* + * Uncompressed cb, read it straight into the destination range + * overlapping the cb. + */ + ntfs_log_debug("Found uncompressed compression block.\n"); + /* + * Read the uncompressed data into the destination buffer. + * NOTE: We cheat a little bit here by marking the attribute as + * not compressed in the ntfs_attr structure so that we can + * read the data by simply using ntfs_attr_pread(). (-8 + * NOTE: we have to modify data_size and initialized_size + * temporarily as well... + */ + to_read = min(count, cb_size - ofs); + ofs += vcn << vol->cluster_size_bits; + NAttrClearCompressed(na); + na->data_flags &= ~ATTR_COMPRESSION_MASK; + tdata_size = na->data_size; + tinitialized_size = na->initialized_size; + na->data_size = na->initialized_size = na->allocated_size; + do { + br = ntfs_attr_pread(na, ofs, to_read, b); + if (br <= 0) { + if (!br) { + ntfs_log_error("Failed to read an" + " uncompressed cluster," + " inode %lld offs 0x%llx\n", + (long long)na->ni->mft_no, + (long long)ofs); + errno = EIO; + } + err = errno; + na->data_size = tdata_size; + na->initialized_size = tinitialized_size; + na->ni->flags |= compression; + na->data_flags = data_flags; + free(cb); + free(dest); + if (total) + return total; + errno = err; + return br; + } + total += br; + count -= br; + b = (u8*)b + br; + to_read -= br; + ofs += br; + } while (to_read > 0); + na->data_size = tdata_size; + na->initialized_size = tinitialized_size; + na->ni->flags |= compression; + na->data_flags = data_flags; + ofs = 0; + } else { + s64 tdata_size, tinitialized_size; - /* - * Compressed cb, decompress it into the temporary buffer, then - * copy the data to the destination range overlapping the cb. - */ - ntfs_log_debug( "Found compressed compression block.\n" ); - /* - * Read the compressed data into the temporary buffer. - * NOTE: We cheat a little bit here by marking the attribute as - * not compressed in the ntfs_attr structure so that we can - * read the raw, compressed data by simply using - * ntfs_attr_pread(). (-8 - * NOTE: We have to modify data_size and initialized_size - * temporarily as well... - */ - to_read = cb_size; - NAttrClearCompressed( na ); - na->data_flags &= ~ATTR_COMPRESSION_MASK; - tdata_size = na->data_size; - tinitialized_size = na->initialized_size; - na->data_size = na->initialized_size = na->allocated_size; - do - { - br = ntfs_attr_pread( na, - ( vcn << vol->cluster_size_bits ) + - ( cb_pos - cb ), to_read, cb_pos ); - if ( br <= 0 ) - { - if ( !br ) - { - ntfs_log_error( "Failed to read a" - " compressed cluster, " - " inode %lld offs 0x%llx\n", - ( long long )na->ni->mft_no, - ( long long )( vcn << vol->cluster_size_bits ) ); - errno = EIO; - } - err = errno; - na->data_size = tdata_size; - na->initialized_size = tinitialized_size; - na->ni->flags |= compression; - na->data_flags = data_flags; - free( cb ); - free( dest ); - if ( total ) - return total; - errno = err; - return br; - } - cb_pos += br; - to_read -= br; - } - while ( to_read > 0 ); - na->data_size = tdata_size; - na->initialized_size = tinitialized_size; - na->ni->flags |= compression; - na->data_flags = data_flags; - /* Just a precaution. */ - if ( cb_pos + 2 <= cb_end ) - *( u16* )cb_pos = 0; - ntfs_log_debug( "Successfully read the compression block.\n" ); - if ( ntfs_decompress( dest, cb_size, cb, cb_size ) < 0 ) - { - err = errno; - free( cb ); - free( dest ); - if ( total ) - return total; - errno = err; - return -1; - } - to_read = min( count, cb_size - ofs ); - memcpy( b, dest + ofs, to_read ); - total += to_read; - count -= to_read; - b = ( u8* )b + to_read; - ofs = 0; - } - /* Do we have more work to do? */ - if ( nr_cbs ) - goto do_next_cb; - /* We no longer need the buffers. */ - free( cb ); - free( dest ); - /* Return number of bytes read. */ - return total + total2; + /* + * Compressed cb, decompress it into the temporary buffer, then + * copy the data to the destination range overlapping the cb. + */ + ntfs_log_debug("Found compressed compression block.\n"); + /* + * Read the compressed data into the temporary buffer. + * NOTE: We cheat a little bit here by marking the attribute as + * not compressed in the ntfs_attr structure so that we can + * read the raw, compressed data by simply using + * ntfs_attr_pread(). (-8 + * NOTE: We have to modify data_size and initialized_size + * temporarily as well... + */ + to_read = cb_size; + NAttrClearCompressed(na); + na->data_flags &= ~ATTR_COMPRESSION_MASK; + tdata_size = na->data_size; + tinitialized_size = na->initialized_size; + na->data_size = na->initialized_size = na->allocated_size; + do { + br = ntfs_attr_pread(na, + (vcn << vol->cluster_size_bits) + + (cb_pos - cb), to_read, cb_pos); + if (br <= 0) { + if (!br) { + ntfs_log_error("Failed to read a" + " compressed cluster, " + " inode %lld offs 0x%llx\n", + (long long)na->ni->mft_no, + (long long)(vcn << vol->cluster_size_bits)); + errno = EIO; + } + err = errno; + na->data_size = tdata_size; + na->initialized_size = tinitialized_size; + na->ni->flags |= compression; + na->data_flags = data_flags; + free(cb); + free(dest); + if (total) + return total; + errno = err; + return br; + } + cb_pos += br; + to_read -= br; + } while (to_read > 0); + na->data_size = tdata_size; + na->initialized_size = tinitialized_size; + na->ni->flags |= compression; + na->data_flags = data_flags; + /* Just a precaution. */ + if (cb_pos + 2 <= cb_end) + *(u16*)cb_pos = 0; + ntfs_log_debug("Successfully read the compression block.\n"); + if (ntfs_decompress(dest, cb_size, cb, cb_size) < 0) { + err = errno; + free(cb); + free(dest); + if (total) + return total; + errno = err; + return -1; + } + to_read = min(count, cb_size - ofs); + memcpy(b, dest + ofs, to_read); + total += to_read; + count -= to_read; + b = (u8*)b + to_read; + ofs = 0; + } + /* Do we have more work to do? */ + if (nr_cbs) + goto do_next_cb; + /* We no longer need the buffers. */ + free(cb); + free(dest); + /* Return number of bytes read. */ + return total + total2; } /* - * Read data from a set of clusters + * Read data from a set of clusters * - * Returns the amount of data read + * Returns the amount of data read */ -static u32 read_clusters( ntfs_volume *vol, const runlist_element *rl, - s64 offs, u32 to_read, char *inbuf ) +static u32 read_clusters(ntfs_volume *vol, const runlist_element *rl, + s64 offs, u32 to_read, char *inbuf) { - u32 count; - int xgot; - u32 got; - s64 xpos; - BOOL first; - char *xinbuf; - const runlist_element *xrl; + u32 count; + int xgot; + u32 got; + s64 xpos; + BOOL first; + char *xinbuf; + const runlist_element *xrl; - got = 0; - xrl = rl; - xinbuf = inbuf; - first = TRUE; - do - { - count = xrl->length << vol->cluster_size_bits; - xpos = xrl->lcn << vol->cluster_size_bits; - if ( first ) - { - count -= offs; - xpos += offs; - } - if ( ( to_read - got ) < count ) - count = to_read - got; - xgot = ntfs_pread( vol->dev, xpos, count, xinbuf ); - if ( xgot == ( int )count ) - { - got += count; - xpos += count; - xinbuf += count; - xrl++; - } - first = FALSE; - } - while ( ( xgot == ( int )count ) && ( got < to_read ) ); - return ( got ); + got = 0; + xrl = rl; + xinbuf = inbuf; + first = TRUE; + do { + count = xrl->length << vol->cluster_size_bits; + xpos = xrl->lcn << vol->cluster_size_bits; + if (first) { + count -= offs; + xpos += offs; + } + if ((to_read - got) < count) + count = to_read - got; + xgot = ntfs_pread(vol->dev, xpos, count, xinbuf); + if (xgot == (int)count) { + got += count; + xpos += count; + xinbuf += count; + xrl++; + } + first = FALSE; + } while ((xgot == (int)count) && (got < to_read)); + return (got); } /* - * Write data to a set of clusters + * Write data to a set of clusters * - * Returns the amount of data written + * Returns the amount of data written */ -static s32 write_clusters( ntfs_volume *vol, const runlist_element *rl, - s64 offs, s32 to_write, const char *outbuf ) +static s32 write_clusters(ntfs_volume *vol, const runlist_element *rl, + s64 offs, s32 to_write, const char *outbuf) { - s32 count; - s32 put, xput; - s64 xpos; - BOOL first; - const char *xoutbuf; - const runlist_element *xrl; + s32 count; + s32 put, xput; + s64 xpos; + BOOL first; + const char *xoutbuf; + const runlist_element *xrl; - put = 0; - xrl = rl; - xoutbuf = outbuf; - first = TRUE; - do - { - count = xrl->length << vol->cluster_size_bits; - xpos = xrl->lcn << vol->cluster_size_bits; - if ( first ) - { - count -= offs; - xpos += offs; - } - if ( ( to_write - put ) < count ) - count = to_write - put; - xput = ntfs_pwrite( vol->dev, xpos, count, xoutbuf ); - if ( xput == count ) - { - put += count; - xpos += count; - xoutbuf += count; - xrl++; - } - first = FALSE; - } - while ( ( xput == count ) && ( put < to_write ) ); - return ( put ); + put = 0; + xrl = rl; + xoutbuf = outbuf; + first = TRUE; + do { + count = xrl->length << vol->cluster_size_bits; + xpos = xrl->lcn << vol->cluster_size_bits; + if (first) { + count -= offs; + xpos += offs; + } + if ((to_write - put) < count) + count = to_write - put; + xput = ntfs_pwrite(vol->dev, xpos, count, xoutbuf); + if (xput == count) { + put += count; + xpos += count; + xoutbuf += count; + xrl++; + } + first = FALSE; + } while ((xput == count) && (put < to_write)); + return (put); } /* - * Compress and write a set of blocks + * Compress and write a set of blocks * - * returns the size actually written (rounded to a full cluster) - * or 0 if all zeroes (nothing is written) - * or -1 if could not compress (nothing is written) - * or -2 if there were an irrecoverable error (errno set) + * returns the size actually written (rounded to a full cluster) + * or 0 if all zeroes (nothing is written) + * or -1 if could not compress (nothing is written) + * or -2 if there were an irrecoverable error (errno set) */ -static s32 ntfs_comp_set( ntfs_attr *na, runlist_element *rl, - s64 offs, u32 insz, const char *inbuf ) +static s32 ntfs_comp_set(ntfs_attr *na, runlist_element *rl, + s64 offs, u32 insz, const char *inbuf) { - ntfs_volume *vol; - char *outbuf; - char *pbuf; - u32 compsz; - s32 written; - s32 rounded; - unsigned int clsz; - u32 p; - unsigned int sz; - unsigned int bsz; - BOOL fail; - BOOL allzeroes; - /* a single compressed zero */ - static char onezero[] = { 0x01, 0xb0, 0x00, 0x00 } ; - /* a couple of compressed zeroes */ - static char twozeroes[] = { 0x02, 0xb0, 0x00, 0x00, 0x00 } ; - /* more compressed zeroes, to be followed by some count */ - static char morezeroes[] = { 0x03, 0xb0, 0x02, 0x00 } ; + ntfs_volume *vol; + char *outbuf; + char *pbuf; + u32 compsz; + s32 written; + s32 rounded; + unsigned int clsz; + u32 p; + unsigned int sz; + unsigned int bsz; + BOOL fail; + BOOL allzeroes; + /* a single compressed zero */ + static char onezero[] = { 0x01, 0xb0, 0x00, 0x00 } ; + /* a couple of compressed zeroes */ + static char twozeroes[] = { 0x02, 0xb0, 0x00, 0x00, 0x00 } ; + /* more compressed zeroes, to be followed by some count */ + static char morezeroes[] = { 0x03, 0xb0, 0x02, 0x00 } ; - vol = na->ni->vol; - written = -1; /* default return */ - clsz = 1 << vol->cluster_size_bits; - /* may need 2 extra bytes per block and 2 more bytes */ - outbuf = ( char* )ntfs_malloc( na->compression_block_size - + 2 * ( na->compression_block_size / NTFS_SB_SIZE ) - + 2 ); - if ( outbuf ) - { - fail = FALSE; - compsz = 0; - allzeroes = TRUE; - for ( p = 0; ( p < insz ) && !fail; p += NTFS_SB_SIZE ) - { - if ( ( p + NTFS_SB_SIZE ) < insz ) - bsz = NTFS_SB_SIZE; - else - bsz = insz - p; - pbuf = &outbuf[compsz]; - sz = ntfs_compress_block( &inbuf[p], bsz, pbuf ); - /* fail if all the clusters (or more) are needed */ - if ( !sz || ( ( compsz + sz + clsz + 2 ) - > na->compression_block_size ) ) - fail = TRUE; - else - { - if ( allzeroes ) - { - /* check whether this is all zeroes */ - switch ( sz ) - { - case 4 : - allzeroes = !memcmp( - pbuf, onezero, 4 ); - break; - case 5 : - allzeroes = !memcmp( - pbuf, twozeroes, 5 ); - break; - case 6 : - allzeroes = !memcmp( - pbuf, morezeroes, 4 ); - break; - default : - allzeroes = FALSE; - break; - } - } - compsz += sz; - } - } - if ( !fail && !allzeroes ) - { - /* add a couple of null bytes, space has been checked */ - outbuf[compsz++] = 0; - outbuf[compsz++] = 0; - /* write a full cluster, to avoid partial reading */ - rounded = ( ( compsz - 1 ) | ( clsz - 1 ) ) + 1; - written = write_clusters( vol, rl, offs, rounded, outbuf ); - if ( written != rounded ) - { - /* - * TODO : previously written text has been - * spoilt, should return a specific error - */ - ntfs_log_error( "error writing compressed data\n" ); - errno = EIO; - written = -2; - } - } - else if ( !fail ) - written = 0; - free( outbuf ); - } - return ( written ); + vol = na->ni->vol; + written = -1; /* default return */ + clsz = 1 << vol->cluster_size_bits; + /* may need 2 extra bytes per block and 2 more bytes */ + outbuf = (char*)ntfs_malloc(na->compression_block_size + + 2*(na->compression_block_size/NTFS_SB_SIZE) + + 2); + if (outbuf) { + fail = FALSE; + compsz = 0; + allzeroes = TRUE; + for (p=0; (p na->compression_block_size)) + fail = TRUE; + else { + if (allzeroes) { + /* check whether this is all zeroes */ + switch (sz) { + case 4 : + allzeroes = !memcmp( + pbuf,onezero,4); + break; + case 5 : + allzeroes = !memcmp( + pbuf,twozeroes,5); + break; + case 6 : + allzeroes = !memcmp( + pbuf,morezeroes,4); + break; + default : + allzeroes = FALSE; + break; + } + } + compsz += sz; + } + } + if (!fail && !allzeroes) { + /* add a couple of null bytes, space has been checked */ + outbuf[compsz++] = 0; + outbuf[compsz++] = 0; + /* write a full cluster, to avoid partial reading */ + rounded = ((compsz - 1) | (clsz - 1)) + 1; + written = write_clusters(vol, rl, offs, rounded, outbuf); + if (written != rounded) { + /* + * TODO : previously written text has been + * spoilt, should return a specific error + */ + ntfs_log_error("error writing compressed data\n"); + errno = EIO; + written = -2; + } + } else + if (!fail) + written = 0; + free(outbuf); + } + return (written); } /* - * Check the validity of a compressed runlist - * The check starts at the beginning of current run and ends - * at the end of runlist - * errno is set if the runlist is not valid + * Check the validity of a compressed runlist + * The check starts at the beginning of current run and ends + * at the end of runlist + * errno is set if the runlist is not valid */ -static BOOL valid_compressed_run( ntfs_attr *na, runlist_element *rl, - BOOL fullcheck, const char *text ) +static BOOL valid_compressed_run(ntfs_attr *na, runlist_element *rl, + BOOL fullcheck, const char *text) { - runlist_element *xrl; - const char *err; - BOOL ok = TRUE; + runlist_element *xrl; + const char *err; + BOOL ok = TRUE; - xrl = rl; - while ( xrl->vcn & ( na->compression_block_clusters - 1 ) ) - xrl--; - err = ( const char* )NULL; - while ( xrl->length ) - { - if ( ( xrl->vcn + xrl->length ) != xrl[1].vcn ) - err = "Runs not adjacent"; - if ( xrl->lcn == LCN_HOLE ) - { - if ( ( xrl->vcn + xrl->length ) - & ( na->compression_block_clusters - 1 ) ) - { - err = "Invalid hole"; - } - if ( fullcheck && ( xrl[1].lcn == LCN_HOLE ) ) - { - err = "Adjacent holes"; - } - } - if ( err ) - { - ntfs_log_error( "%s at %s index %ld inode %lld\n", - err, text, ( long )( xrl - na->rl ), - ( long long )na->ni->mft_no ); - errno = EIO; - ok = FALSE; - err = ( const char* )NULL; - } - xrl++; - } - return ( ok ); + xrl = rl; + while (xrl->vcn & (na->compression_block_clusters - 1)) + xrl--; + err = (const char*)NULL; + while (xrl->length) { + if ((xrl->vcn + xrl->length) != xrl[1].vcn) + err = "Runs not adjacent"; + if (xrl->lcn == LCN_HOLE) { + if ((xrl->vcn + xrl->length) + & (na->compression_block_clusters - 1)) { + err = "Invalid hole"; + } + if (fullcheck && (xrl[1].lcn == LCN_HOLE)) { + err = "Adjacent holes"; + } + } + if (err) { + ntfs_log_error("%s at %s index %ld inode %lld\n", + err, text, (long)(xrl - na->rl), + (long long)na->ni->mft_no); + errno = EIO; + ok = FALSE; + err = (const char*)NULL; + } + xrl++; + } + return (ok); } /* - * Free unneeded clusters after overwriting compressed data + * Free unneeded clusters after overwriting compressed data * - * This generally requires one or two empty slots at the end of runlist, - * but we do not want to reallocate the runlist here because - * there are many pointers to it. - * So the empty slots have to be reserved beforehand + * This generally requires one or two empty slots at the end of runlist, + * but we do not want to reallocate the runlist here because + * there are many pointers to it. + * So the empty slots have to be reserved beforehand * - * Returns zero unless some error occurred (described by errno) + * Returns zero unless some error occurred (described by errno) * * +======= start of block =====+ * 0 |A chunk may overflow | <-- rl usedcnt : A + B @@ -1206,822 +1115,717 @@ static BOOL valid_compressed_run( ntfs_attr *na, runlist_element *rl, * */ -static int ntfs_compress_overwr_free( ntfs_attr *na, runlist_element *rl, - s32 usedcnt, s32 freecnt, VCN *update_from ) +static int ntfs_compress_overwr_free(ntfs_attr *na, runlist_element *rl, + s32 usedcnt, s32 freecnt, VCN *update_from) { - BOOL beginhole; - BOOL mergeholes; - s32 oldlength; - s32 freelength; - s64 freelcn; - s64 freevcn; - runlist_element *freerl; - ntfs_volume *vol; - s32 carry; - int res; + BOOL beginhole; + BOOL mergeholes; + s32 oldlength; + s32 freelength; + s64 freelcn; + s64 freevcn; + runlist_element *freerl; + ntfs_volume *vol; + s32 carry; + int res; - vol = na->ni->vol; - res = 0; - freelcn = rl->lcn + usedcnt; - freevcn = rl->vcn + usedcnt; - freelength = rl->length - usedcnt; - beginhole = !usedcnt && !rl->vcn; - /* can merge with hole before ? */ - mergeholes = !usedcnt - && rl[0].vcn - && ( rl[-1].lcn == LCN_HOLE ); - /* truncate current run, carry to subsequent hole */ - carry = freelength; - oldlength = rl->length; - if ( mergeholes ) - { - /* merging with a hole before */ - freerl = rl; - } - else - { - rl->length -= freelength; /* warning : can be zero */ - freerl = ++rl; - } - if ( !mergeholes && ( usedcnt || beginhole ) ) - { - s32 freed; - runlist_element *frl; - runlist_element *erl; - int holes = 0; - BOOL threeparts; + vol = na->ni->vol; + res = 0; + freelcn = rl->lcn + usedcnt; + freevcn = rl->vcn + usedcnt; + freelength = rl->length - usedcnt; + beginhole = !usedcnt && !rl->vcn; + /* can merge with hole before ? */ + mergeholes = !usedcnt + && rl[0].vcn + && (rl[-1].lcn == LCN_HOLE); + /* truncate current run, carry to subsequent hole */ + carry = freelength; + oldlength = rl->length; + if (mergeholes) { + /* merging with a hole before */ + freerl = rl; + } else { + rl->length -= freelength; /* warning : can be zero */ + freerl = ++rl; + } + if (!mergeholes && (usedcnt || beginhole)) { + s32 freed; + runlist_element *frl; + runlist_element *erl; + int holes = 0; + BOOL threeparts; - /* free the unneeded clusters from initial run, then freerl */ - threeparts = ( freelength > freecnt ); - freed = 0; - frl = freerl; - if ( freelength ) - { - res = ntfs_cluster_free_basic( vol, freelcn, - ( threeparts ? freecnt : freelength ) ); - if ( !res ) - freed += ( threeparts ? freecnt : freelength ); - if ( !usedcnt ) - { - holes++; - freerl--; - freerl->length += ( threeparts - ? freecnt : freelength ); - if ( freerl->vcn < *update_from ) - *update_from = freerl->vcn; - } - } - while ( !res && frl->length && ( freed < freecnt ) ) - { - if ( frl->length <= ( freecnt - freed ) ) - { - res = ntfs_cluster_free_basic( vol, frl->lcn, - frl->length ); - if ( !res ) - { - freed += frl->length; - frl->lcn = LCN_HOLE; - frl->length += carry; - carry = 0; - holes++; - } - } - else - { - res = ntfs_cluster_free_basic( vol, frl->lcn, - freecnt - freed ); - if ( !res ) - { - frl->lcn += freecnt - freed; - frl->vcn += freecnt - freed; - frl->length -= freecnt - freed; - freed = freecnt; - } - } - frl++; - } - na->compressed_size -= freed << vol->cluster_size_bits; - switch ( holes ) - { - case 0 : - /* there are no hole, must insert one */ - /* space for hole has been prereserved */ - if ( freerl->lcn == LCN_HOLE ) - { - if ( threeparts ) - { - erl = freerl; - while ( erl->length ) - erl++; - do - { - erl[2] = *erl; - } - while ( erl-- != freerl ); + /* free the unneeded clusters from initial run, then freerl */ + threeparts = (freelength > freecnt); + freed = 0; + frl = freerl; + if (freelength) { + res = ntfs_cluster_free_basic(vol,freelcn, + (threeparts ? freecnt : freelength)); + if (!res) + freed += (threeparts ? freecnt : freelength); + if (!usedcnt) { + holes++; + freerl--; + freerl->length += (threeparts + ? freecnt : freelength); + if (freerl->vcn < *update_from) + *update_from = freerl->vcn; + } + } + while (!res && frl->length && (freed < freecnt)) { + if (frl->length <= (freecnt - freed)) { + res = ntfs_cluster_free_basic(vol, frl->lcn, + frl->length); + if (!res) { + freed += frl->length; + frl->lcn = LCN_HOLE; + frl->length += carry; + carry = 0; + holes++; + } + } else { + res = ntfs_cluster_free_basic(vol, frl->lcn, + freecnt - freed); + if (!res) { + frl->lcn += freecnt - freed; + frl->vcn += freecnt - freed; + frl->length -= freecnt - freed; + freed = freecnt; + } + } + frl++; + } + na->compressed_size -= freed << vol->cluster_size_bits; + switch (holes) { + case 0 : + /* there are no hole, must insert one */ + /* space for hole has been prereserved */ + if (freerl->lcn == LCN_HOLE) { + if (threeparts) { + erl = freerl; + while (erl->length) + erl++; + do { + erl[2] = *erl; + } while (erl-- != freerl); - freerl[1].length = freelength - freecnt; - freerl->length = freecnt; - freerl[1].lcn = freelcn + freecnt; - freerl[1].vcn = freevcn + freecnt; - freerl[2].lcn = LCN_HOLE; - freerl[2].vcn = freerl[1].vcn - + freerl[1].length; - freerl->vcn = freevcn; - } - else - { - freerl->vcn = freevcn; - freerl->length += freelength; - } - } - else - { - erl = freerl; - while ( erl->length ) - erl++; - if ( threeparts ) - { - do - { - erl[2] = *erl; - } - while ( erl-- != freerl ); - freerl[1].lcn = freelcn + freecnt; - freerl[1].vcn = freevcn + freecnt; - freerl[1].length = oldlength - usedcnt - freecnt; - } - else - { - do - { - erl[1] = *erl; - } - while ( erl-- != freerl ); - } - freerl->lcn = LCN_HOLE; - freerl->vcn = freevcn; - freerl->length = freecnt; - } - break; - case 1 : - /* there is a single hole, may have to merge */ - freerl->vcn = freevcn; - if ( freerl[1].lcn == LCN_HOLE ) - { - freerl->length += freerl[1].length; - erl = freerl; - do - { - erl++; - *erl = erl[1]; - } - while ( erl->length ); - } - break; - default : - /* there were several holes, must merge them */ - freerl->lcn = LCN_HOLE; - freerl->vcn = freevcn; - freerl->length = freecnt; - if ( freerl[holes].lcn == LCN_HOLE ) - { - freerl->length += freerl[holes].length; - holes++; - } - erl = freerl; - do - { - erl++; - *erl = erl[holes - 1]; - } - while ( erl->length ); - break; - } - } - else - { - s32 freed; - runlist_element *frl; - runlist_element *xrl; + freerl[1].length = freelength - freecnt; + freerl->length = freecnt; + freerl[1].lcn = freelcn + freecnt; + freerl[1].vcn = freevcn + freecnt; + freerl[2].lcn = LCN_HOLE; + freerl[2].vcn = freerl[1].vcn + + freerl[1].length; + freerl->vcn = freevcn; + } else { + freerl->vcn = freevcn; + freerl->length += freelength; + } + } else { + erl = freerl; + while (erl->length) + erl++; + if (threeparts) { + do { + erl[2] = *erl; + } while (erl-- != freerl); + freerl[1].lcn = freelcn + freecnt; + freerl[1].vcn = freevcn + freecnt; + freerl[1].length = oldlength - usedcnt - freecnt; + } else { + do { + erl[1] = *erl; + } while (erl-- != freerl); + } + freerl->lcn = LCN_HOLE; + freerl->vcn = freevcn; + freerl->length = freecnt; + } + break; + case 1 : + /* there is a single hole, may have to merge */ + freerl->vcn = freevcn; + if (freerl[1].lcn == LCN_HOLE) { + freerl->length += freerl[1].length; + erl = freerl; + do { + erl++; + *erl = erl[1]; + } while (erl->length); + } + break; + default : + /* there were several holes, must merge them */ + freerl->lcn = LCN_HOLE; + freerl->vcn = freevcn; + freerl->length = freecnt; + if (freerl[holes].lcn == LCN_HOLE) { + freerl->length += freerl[holes].length; + holes++; + } + erl = freerl; + do { + erl++; + *erl = erl[holes - 1]; + } while (erl->length); + break; + } + } else { + s32 freed; + runlist_element *frl; + runlist_element *xrl; - freed = 0; - frl = freerl--; - if ( freerl->vcn < *update_from ) - *update_from = freerl->vcn; - while ( !res && frl->length && ( freed < freecnt ) ) - { - if ( frl->length <= ( freecnt - freed ) ) - { - freerl->length += frl->length; - freed += frl->length; - res = ntfs_cluster_free_basic( vol, frl->lcn, - frl->length ); - frl++; - } - else - { - freerl->length += freecnt - freed; - res = ntfs_cluster_free_basic( vol, frl->lcn, - freecnt - freed ); - frl->lcn += freecnt - freed; - frl->vcn += freecnt - freed; - frl->length -= freecnt - freed; - freed = freecnt; - } - } - /* remove unneded runlist entries */ - xrl = freerl; - /* group with next run if also a hole */ - if ( frl->length && ( frl->lcn == LCN_HOLE ) ) - { - xrl->length += frl->length; - frl++; - } - while ( frl->length ) - { - *++xrl = *frl++; - } - *++xrl = *frl; /* terminator */ - na->compressed_size -= freed << vol->cluster_size_bits; - } - return ( res ); + freed = 0; + frl = freerl--; + if (freerl->vcn < *update_from) + *update_from = freerl->vcn; + while (!res && frl->length && (freed < freecnt)) { + if (frl->length <= (freecnt - freed)) { + freerl->length += frl->length; + freed += frl->length; + res = ntfs_cluster_free_basic(vol, frl->lcn, + frl->length); + frl++; + } else { + freerl->length += freecnt - freed; + res = ntfs_cluster_free_basic(vol, frl->lcn, + freecnt - freed); + frl->lcn += freecnt - freed; + frl->vcn += freecnt - freed; + frl->length -= freecnt - freed; + freed = freecnt; + } + } + /* remove unneded runlist entries */ + xrl = freerl; + /* group with next run if also a hole */ + if (frl->length && (frl->lcn == LCN_HOLE)) { + xrl->length += frl->length; + frl++; + } + while (frl->length) { + *++xrl = *frl++; + } + *++xrl = *frl; /* terminator */ + na->compressed_size -= freed << vol->cluster_size_bits; + } + return (res); } /* - * Free unneeded clusters after compression + * Free unneeded clusters after compression * - * This generally requires one or two empty slots at the end of runlist, - * but we do not want to reallocate the runlist here because - * there are many pointers to it. - * So the empty slots have to be reserved beforehand + * This generally requires one or two empty slots at the end of runlist, + * but we do not want to reallocate the runlist here because + * there are many pointers to it. + * So the empty slots have to be reserved beforehand * - * Returns zero unless some error occurred (described by errno) + * Returns zero unless some error occurred (described by errno) */ -static int ntfs_compress_free( ntfs_attr *na, runlist_element *rl, - s64 used, s64 reserved, BOOL appending, - VCN *update_from ) +static int ntfs_compress_free(ntfs_attr *na, runlist_element *rl, + s64 used, s64 reserved, BOOL appending, + VCN *update_from) { - s32 freecnt; - s32 usedcnt; - int res; - s64 freelcn; - s64 freevcn; - s32 freelength; - BOOL mergeholes; - BOOL beginhole; - ntfs_volume *vol; - runlist_element *freerl; + s32 freecnt; + s32 usedcnt; + int res; + s64 freelcn; + s64 freevcn; + s32 freelength; + BOOL mergeholes; + BOOL beginhole; + ntfs_volume *vol; + runlist_element *freerl; - res = -1; /* default return */ - vol = na->ni->vol; - freecnt = ( reserved - used ) >> vol->cluster_size_bits; - usedcnt = ( reserved >> vol->cluster_size_bits ) - freecnt; - if ( rl->vcn < *update_from ) - *update_from = rl->vcn; - /* skip entries fully used, if any */ - while ( rl->length && ( rl->length < usedcnt ) ) - { - usedcnt -= rl->length; /* must be > 0 */ - rl++; - } - if ( rl->length ) - { - /* - * Splitting the current allocation block requires - * an extra runlist element to create the hole. - * The required entry has been prereserved when - * mapping the runlist. - */ - /* get the free part in initial run */ - freelcn = rl->lcn + usedcnt; - freevcn = rl->vcn + usedcnt; - /* new count of allocated clusters */ - if ( !( ( freevcn + freecnt ) - & ( na->compression_block_clusters - 1 ) ) ) - { - if ( !appending ) - res = ntfs_compress_overwr_free( na, rl, - usedcnt, freecnt, update_from ); - else - { - freelength = rl->length - usedcnt; - beginhole = !usedcnt && !rl->vcn; - mergeholes = !usedcnt - && rl[0].vcn - && ( rl[-1].lcn == LCN_HOLE ); - if ( mergeholes ) - { - s32 carry; + res = -1; /* default return */ + vol = na->ni->vol; + freecnt = (reserved - used) >> vol->cluster_size_bits; + usedcnt = (reserved >> vol->cluster_size_bits) - freecnt; + if (rl->vcn < *update_from) + *update_from = rl->vcn; + /* skip entries fully used, if any */ + while (rl->length && (rl->length < usedcnt)) { + usedcnt -= rl->length; /* must be > 0 */ + rl++; + } + if (rl->length) { + /* + * Splitting the current allocation block requires + * an extra runlist element to create the hole. + * The required entry has been prereserved when + * mapping the runlist. + */ + /* get the free part in initial run */ + freelcn = rl->lcn + usedcnt; + freevcn = rl->vcn + usedcnt; + /* new count of allocated clusters */ + if (!((freevcn + freecnt) + & (na->compression_block_clusters - 1))) { + if (!appending) + res = ntfs_compress_overwr_free(na,rl, + usedcnt,freecnt,update_from); + else { + freelength = rl->length - usedcnt; + beginhole = !usedcnt && !rl->vcn; + mergeholes = !usedcnt + && rl[0].vcn + && (rl[-1].lcn == LCN_HOLE); + if (mergeholes) { + s32 carry; - /* shorten the runs which have free space */ - carry = freecnt; - freerl = rl; - while ( freerl->length < carry ) - { - carry -= freerl->length; - freerl++; - } - freerl->length = carry; - freerl = rl; - } - else - { - rl->length = usedcnt; /* can be zero ? */ - freerl = ++rl; - } - if ( ( freelength > 0 ) - && !mergeholes - && ( usedcnt || beginhole ) ) - { - /* - * move the unused part to the end. Doing so, - * the vcn will be out of order. This does - * not harm, the vcn are meaningless now, and - * only the lcn are meaningful for freeing. - */ - /* locate current end */ - while ( rl->length ) - rl++; - /* new terminator relocated */ - rl[1].vcn = rl->vcn; - rl[1].lcn = LCN_ENOENT; - rl[1].length = 0; - /* hole, currently allocated */ - rl->vcn = freevcn; - rl->lcn = freelcn; - rl->length = freelength; - } - else - { - /* why is this different from the begin hole case ? */ - if ( ( freelength > 0 ) - && !mergeholes - && !usedcnt ) - { - freerl--; - freerl->length = freelength; - if ( freerl->vcn < *update_from ) - *update_from - = freerl->vcn; - } - } - /* free the hole */ - res = ntfs_cluster_free_from_rl( vol, freerl ); - if ( !res ) - { - na->compressed_size -= freecnt - << vol->cluster_size_bits; - if ( mergeholes ) - { - /* merge with adjacent hole */ - freerl--; - freerl->length += freecnt; - } - else - { - if ( beginhole ) - freerl--; - /* mark hole as free */ - freerl->lcn = LCN_HOLE; - freerl->vcn = freevcn; - freerl->length = freecnt; - } - if ( freerl->vcn < *update_from ) - *update_from = freerl->vcn; - /* and set up the new end */ - freerl[1].lcn = LCN_ENOENT; - freerl[1].vcn = freevcn + freecnt; - freerl[1].length = 0; - } - } - } - else - { - ntfs_log_error( "Bad end of a compression block set\n" ); - errno = EIO; - } - } - else - { - ntfs_log_error( "No cluster to free after compression\n" ); - errno = EIO; - } - return ( res ); + /* shorten the runs which have free space */ + carry = freecnt; + freerl = rl; + while (freerl->length < carry) { + carry -= freerl->length; + freerl++; + } + freerl->length = carry; + freerl = rl; + } else { + rl->length = usedcnt; /* can be zero ? */ + freerl = ++rl; + } + if ((freelength > 0) + && !mergeholes + && (usedcnt || beginhole)) { + /* + * move the unused part to the end. Doing so, + * the vcn will be out of order. This does + * not harm, the vcn are meaningless now, and + * only the lcn are meaningful for freeing. + */ + /* locate current end */ + while (rl->length) + rl++; + /* new terminator relocated */ + rl[1].vcn = rl->vcn; + rl[1].lcn = LCN_ENOENT; + rl[1].length = 0; + /* hole, currently allocated */ + rl->vcn = freevcn; + rl->lcn = freelcn; + rl->length = freelength; + } else { + /* why is this different from the begin hole case ? */ + if ((freelength > 0) + && !mergeholes + && !usedcnt) { + freerl--; + freerl->length = freelength; + if (freerl->vcn < *update_from) + *update_from + = freerl->vcn; + } + } + /* free the hole */ + res = ntfs_cluster_free_from_rl(vol,freerl); + if (!res) { + na->compressed_size -= freecnt + << vol->cluster_size_bits; + if (mergeholes) { + /* merge with adjacent hole */ + freerl--; + freerl->length += freecnt; + } else { + if (beginhole) + freerl--; + /* mark hole as free */ + freerl->lcn = LCN_HOLE; + freerl->vcn = freevcn; + freerl->length = freecnt; + } + if (freerl->vcn < *update_from) + *update_from = freerl->vcn; + /* and set up the new end */ + freerl[1].lcn = LCN_ENOENT; + freerl[1].vcn = freevcn + freecnt; + freerl[1].length = 0; + } + } + } else { + ntfs_log_error("Bad end of a compression block set\n"); + errno = EIO; + } + } else { + ntfs_log_error("No cluster to free after compression\n"); + errno = EIO; + } + return (res); } /* - * Read existing data, decompress and append buffer - * Do nothing if something fails + * Read existing data, decompress and append buffer + * Do nothing if something fails */ -static int ntfs_read_append( ntfs_attr *na, const runlist_element *rl, - s64 offs, u32 compsz, s32 pos, BOOL appending, - char *outbuf, s64 to_write, const void *b ) +static int ntfs_read_append(ntfs_attr *na, const runlist_element *rl, + s64 offs, u32 compsz, s32 pos, BOOL appending, + char *outbuf, s64 to_write, const void *b) { - int fail = 1; - char *compbuf; - u32 decompsz; - u32 got; + int fail = 1; + char *compbuf; + u32 decompsz; + u32 got; - if ( compsz == na->compression_block_size ) - { - /* if the full block was requested, it was a hole */ - memset( outbuf, 0, compsz ); - memcpy( &outbuf[pos], b, to_write ); - fail = 0; - } - else - { - compbuf = ( char* )ntfs_malloc( compsz ); - if ( compbuf ) - { - /* must align to full block for decompression */ - if ( appending ) - decompsz = ( ( pos - 1 ) | ( NTFS_SB_SIZE - 1 ) ) + 1; - else - decompsz = na->compression_block_size; - got = read_clusters( na->ni->vol, rl, offs, - compsz, compbuf ); - if ( ( got == compsz ) - && !ntfs_decompress( ( u8* )outbuf, decompsz, - ( u8* )compbuf, compsz ) ) - { - memcpy( &outbuf[pos], b, to_write ); - fail = 0; - } - free( compbuf ); - } - } - return ( fail ); + if (compsz == na->compression_block_size) { + /* if the full block was requested, it was a hole */ + memset(outbuf,0,compsz); + memcpy(&outbuf[pos],b,to_write); + fail = 0; + } else { + compbuf = (char*)ntfs_malloc(compsz); + if (compbuf) { + /* must align to full block for decompression */ + if (appending) + decompsz = ((pos - 1) | (NTFS_SB_SIZE - 1)) + 1; + else + decompsz = na->compression_block_size; + got = read_clusters(na->ni->vol, rl, offs, + compsz, compbuf); + if ((got == compsz) + && !ntfs_decompress((u8*)outbuf,decompsz, + (u8*)compbuf,compsz)) { + memcpy(&outbuf[pos],b,to_write); + fail = 0; + } + free(compbuf); + } + } + return (fail); } /* - * Flush a full compression block + * Flush a full compression block * - * returns the size actually written (rounded to a full cluster) - * or 0 if could not compress (and written uncompressed) - * or -1 if there were an irrecoverable error (errno set) + * returns the size actually written (rounded to a full cluster) + * or 0 if could not compress (and written uncompressed) + * or -1 if there were an irrecoverable error (errno set) */ -static int ntfs_flush( ntfs_attr *na, runlist_element *rl, s64 offs, - const char *outbuf, s32 count, BOOL compress, - BOOL appending, VCN *update_from ) +static int ntfs_flush(ntfs_attr *na, runlist_element *rl, s64 offs, + const char *outbuf, s32 count, BOOL compress, + BOOL appending, VCN *update_from) { - int rounded; - int written; - int clsz; + int rounded; + int written; + int clsz; - if ( compress ) - { - written = ntfs_comp_set( na, rl, offs, count, outbuf ); - if ( written == -1 ) - compress = FALSE; - if ( ( written >= 0 ) - && ntfs_compress_free( na, rl, offs + written, - offs + na->compression_block_size, appending, - update_from ) ) - written = -1; - } - else - written = 0; - if ( !compress ) - { - clsz = 1 << na->ni->vol->cluster_size_bits; - rounded = ( ( count - 1 ) | ( clsz - 1 ) ) + 1; - written = write_clusters( na->ni->vol, rl, - offs, rounded, outbuf ); - if ( written != rounded ) - written = -1; - } - return ( written ); + if (compress) { + written = ntfs_comp_set(na, rl, offs, count, outbuf); + if (written == -1) + compress = FALSE; + if ((written >= 0) + && ntfs_compress_free(na,rl,offs + written, + offs + na->compression_block_size, appending, + update_from)) + written = -1; + } else + written = 0; + if (!compress) { + clsz = 1 << na->ni->vol->cluster_size_bits; + rounded = ((count - 1) | (clsz - 1)) + 1; + written = write_clusters(na->ni->vol, rl, + offs, rounded, outbuf); + if (written != rounded) + written = -1; + } + return (written); } /* - * Write some data to be compressed. - * Compression only occurs when a few clusters (usually 16) are - * full. When this occurs an extra runlist slot may be needed, so - * it has to be reserved beforehand. + * Write some data to be compressed. + * Compression only occurs when a few clusters (usually 16) are + * full. When this occurs an extra runlist slot may be needed, so + * it has to be reserved beforehand. * - * Returns the size of uncompressed data written, - * or negative if an error occurred. - * When the returned size is less than requested, new clusters have - * to be allocated before the function is called again. + * Returns the size of uncompressed data written, + * or negative if an error occurred. + * When the returned size is less than requested, new clusters have + * to be allocated before the function is called again. */ -s64 ntfs_compressed_pwrite( ntfs_attr *na, runlist_element *wrl, s64 wpos, - s64 offs, s64 to_write, s64 rounded, - const void *b, int compressed_part, - VCN *update_from ) +s64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *wrl, s64 wpos, + s64 offs, s64 to_write, s64 rounded, + const void *b, int compressed_part, + VCN *update_from) { - ntfs_volume *vol; - runlist_element *brl; /* entry containing the beginning of block */ - int compression_length; - s64 written; - s64 to_read; - s64 to_flush; - s64 roffs; - s64 got; - s64 start_vcn; - s64 nextblock; - s64 endwrite; - u32 compsz; - char *inbuf; - char *outbuf; - BOOL fail; - BOOL done; - BOOL compress; - BOOL appending; + ntfs_volume *vol; + runlist_element *brl; /* entry containing the beginning of block */ + int compression_length; + s64 written; + s64 to_read; + s64 to_flush; + s64 roffs; + s64 got; + s64 start_vcn; + s64 nextblock; + s64 endwrite; + u32 compsz; + char *inbuf; + char *outbuf; + BOOL fail; + BOOL done; + BOOL compress; + BOOL appending; - if ( !valid_compressed_run( na, wrl, FALSE, "begin compressed write" ) ) - { - return ( -1 ); - } - if ( ( *update_from < 0 ) - || ( compressed_part < 0 ) - || ( compressed_part > ( int )na->compression_block_clusters ) ) - { - ntfs_log_error( "Bad update vcn or compressed_part %d for compressed write\n", - compressed_part ); - errno = EIO; - return ( -1 ); - } - /* make sure there are two unused entries in runlist */ - if ( na->unused_runs < 2 ) - { - ntfs_log_error( "No unused runs for compressed write\n" ); - errno = EIO; - return ( -1 ); - } - if ( wrl->vcn < *update_from ) - *update_from = wrl->vcn; - written = -1; /* default return */ - vol = na->ni->vol; - compression_length = na->compression_block_clusters; - compress = FALSE; - done = FALSE; - /* - * Cannot accept writing beyond the current compression set - * because when compression occurs, clusters are freed - * and have to be reallocated. - * (cannot happen with standard fuse 4K buffers) - * Caller has to avoid this situation, or face consequences. - */ - nextblock = ( ( offs + ( wrl->vcn << vol->cluster_size_bits ) ) - | ( na->compression_block_size - 1 ) ) + 1; - /* determine whether we are appending to file */ - endwrite = offs + to_write + ( wrl->vcn << vol->cluster_size_bits ); - appending = endwrite >= na->initialized_size; - if ( endwrite >= nextblock ) - { - /* it is time to compress */ - compress = TRUE; - /* only process what we can */ - to_write = rounded = nextblock - - ( offs + ( wrl->vcn << vol->cluster_size_bits ) ); - } - start_vcn = 0; - fail = FALSE; - brl = wrl; - roffs = 0; - /* - * If we are about to compress or we need to decompress - * existing data, we have to process a full set of blocks. - * So relocate the parameters to the beginning of allocation - * containing the first byte of the set of blocks. - */ - if ( compress || compressed_part ) - { - /* find the beginning of block */ - start_vcn = ( wrl->vcn + ( offs >> vol->cluster_size_bits ) ) - & -compression_length; - if ( start_vcn < *update_from ) - *update_from = start_vcn; - while ( brl->vcn && ( brl->vcn > start_vcn ) ) - { - /* jumping back a hole means big trouble */ - if ( brl->lcn == ( LCN )LCN_HOLE ) - { - ntfs_log_error( "jump back over a hole when appending\n" ); - fail = TRUE; - errno = EIO; - } - brl--; - offs += brl->length << vol->cluster_size_bits; - } - roffs = ( start_vcn - brl->vcn ) << vol->cluster_size_bits; - } - if ( compressed_part && !fail ) - { - /* - * The set of compression blocks contains compressed data - * (we are reopening an existing file to append to it) - * Decompress the data and append - */ - compsz = compressed_part << vol->cluster_size_bits; - outbuf = ( char* )ntfs_malloc( na->compression_block_size ); - if ( outbuf ) - { - if ( appending ) - { - to_read = offs - roffs; - to_flush = to_read + to_write; - } - else - { - to_read = na->data_size - - ( brl->vcn << vol->cluster_size_bits ); - if ( to_read > na->compression_block_size ) - to_read = na->compression_block_size; - to_flush = to_read; - } - if ( !ntfs_read_append( na, brl, roffs, compsz, - ( s32 )( offs - roffs ), appending, - outbuf, to_write, b ) ) - { - written = ntfs_flush( na, brl, roffs, - outbuf, to_flush, compress, appending, - update_from ); - if ( written >= 0 ) - { - written = to_write; - done = TRUE; - } - } - free( outbuf ); - } - } - else - { - if ( compress && !fail ) - { - /* - * we are filling up a block, read the full set - * of blocks and compress it - */ - inbuf = ( char* )ntfs_malloc( na->compression_block_size ); - if ( inbuf ) - { - to_read = offs - roffs; - if ( to_read ) - got = read_clusters( vol, brl, roffs, - to_read, inbuf ); - else - got = 0; - if ( got == to_read ) - { - memcpy( &inbuf[to_read], b, to_write ); - written = ntfs_comp_set( na, brl, roffs, - to_read + to_write, inbuf ); - /* - * if compression was not successful, - * only write the part which was requested - */ - if ( ( written >= 0 ) - /* free the unused clusters */ - && !ntfs_compress_free( na, brl, - written + roffs, - na->compression_block_size - + roffs, - appending, update_from ) ) - { - done = TRUE; - written = to_write; - } - } - free( inbuf ); - } - } - if ( !done ) - { - /* - * if the compression block is not full, or - * if compression failed for whatever reason, - * write uncompressed - */ - /* check we are not overflowing current allocation */ - if ( ( wpos + rounded ) - > ( ( wrl->lcn + wrl->length ) - << vol->cluster_size_bits ) ) - { - ntfs_log_error( "writing on unallocated clusters\n" ); - errno = EIO; - } - else - { - written = ntfs_pwrite( vol->dev, wpos, - rounded, b ); - if ( written == rounded ) - written = to_write; - } - } - } - if ( ( written >= 0 ) - && !valid_compressed_run( na, wrl, TRUE, "end compressed write" ) ) - written = -1; - return ( written ); + if (!valid_compressed_run(na,wrl,FALSE,"begin compressed write")) { + return (-1); + } + if ((*update_from < 0) + || (compressed_part < 0) + || (compressed_part > (int)na->compression_block_clusters)) { + ntfs_log_error("Bad update vcn or compressed_part %d for compressed write\n", + compressed_part); + errno = EIO; + return (-1); + } + /* make sure there are two unused entries in runlist */ + if (na->unused_runs < 2) { + ntfs_log_error("No unused runs for compressed write\n"); + errno = EIO; + return (-1); + } + if (wrl->vcn < *update_from) + *update_from = wrl->vcn; + written = -1; /* default return */ + vol = na->ni->vol; + compression_length = na->compression_block_clusters; + compress = FALSE; + done = FALSE; + /* + * Cannot accept writing beyond the current compression set + * because when compression occurs, clusters are freed + * and have to be reallocated. + * (cannot happen with standard fuse 4K buffers) + * Caller has to avoid this situation, or face consequences. + */ + nextblock = ((offs + (wrl->vcn << vol->cluster_size_bits)) + | (na->compression_block_size - 1)) + 1; + /* determine whether we are appending to file */ + endwrite = offs + to_write + (wrl->vcn << vol->cluster_size_bits); + appending = endwrite >= na->initialized_size; + if (endwrite >= nextblock) { + /* it is time to compress */ + compress = TRUE; + /* only process what we can */ + to_write = rounded = nextblock + - (offs + (wrl->vcn << vol->cluster_size_bits)); + } + start_vcn = 0; + fail = FALSE; + brl = wrl; + roffs = 0; + /* + * If we are about to compress or we need to decompress + * existing data, we have to process a full set of blocks. + * So relocate the parameters to the beginning of allocation + * containing the first byte of the set of blocks. + */ + if (compress || compressed_part) { + /* find the beginning of block */ + start_vcn = (wrl->vcn + (offs >> vol->cluster_size_bits)) + & -compression_length; + if (start_vcn < *update_from) + *update_from = start_vcn; + while (brl->vcn && (brl->vcn > start_vcn)) { + /* jumping back a hole means big trouble */ + if (brl->lcn == (LCN)LCN_HOLE) { + ntfs_log_error("jump back over a hole when appending\n"); + fail = TRUE; + errno = EIO; + } + brl--; + offs += brl->length << vol->cluster_size_bits; + } + roffs = (start_vcn - brl->vcn) << vol->cluster_size_bits; + } + if (compressed_part && !fail) { + /* + * The set of compression blocks contains compressed data + * (we are reopening an existing file to append to it) + * Decompress the data and append + */ + compsz = compressed_part << vol->cluster_size_bits; + outbuf = (char*)ntfs_malloc(na->compression_block_size); + if (outbuf) { + if (appending) { + to_read = offs - roffs; + to_flush = to_read + to_write; + } else { + to_read = na->data_size + - (brl->vcn << vol->cluster_size_bits); + if (to_read > na->compression_block_size) + to_read = na->compression_block_size; + to_flush = to_read; + } + if (!ntfs_read_append(na, brl, roffs, compsz, + (s32)(offs - roffs), appending, + outbuf, to_write, b)) { + written = ntfs_flush(na, brl, roffs, + outbuf, to_flush, compress, appending, + update_from); + if (written >= 0) { + written = to_write; + done = TRUE; + } + } + free(outbuf); + } + } else { + if (compress && !fail) { + /* + * we are filling up a block, read the full set + * of blocks and compress it + */ + inbuf = (char*)ntfs_malloc(na->compression_block_size); + if (inbuf) { + to_read = offs - roffs; + if (to_read) + got = read_clusters(vol, brl, roffs, + to_read, inbuf); + else + got = 0; + if (got == to_read) { + memcpy(&inbuf[to_read],b,to_write); + written = ntfs_comp_set(na, brl, roffs, + to_read + to_write, inbuf); + /* + * if compression was not successful, + * only write the part which was requested + */ + if ((written >= 0) + /* free the unused clusters */ + && !ntfs_compress_free(na,brl, + written + roffs, + na->compression_block_size + + roffs, + appending, update_from)) { + done = TRUE; + written = to_write; + } + } + free(inbuf); + } + } + if (!done) { + /* + * if the compression block is not full, or + * if compression failed for whatever reason, + * write uncompressed + */ + /* check we are not overflowing current allocation */ + if ((wpos + rounded) + > ((wrl->lcn + wrl->length) + << vol->cluster_size_bits)) { + ntfs_log_error("writing on unallocated clusters\n"); + errno = EIO; + } else { + written = ntfs_pwrite(vol->dev, wpos, + rounded, b); + if (written == rounded) + written = to_write; + } + } + } + if ((written >= 0) + && !valid_compressed_run(na,wrl,TRUE,"end compressed write")) + written = -1; + return (written); } /* - * Close a file written compressed. - * This compresses the last partial compression block of the file. - * Two empty runlist slots have to be reserved beforehand. + * Close a file written compressed. + * This compresses the last partial compression block of the file. + * Two empty runlist slots have to be reserved beforehand. * - * Returns zero if closing is successful. + * Returns zero if closing is successful. */ -int ntfs_compressed_close( ntfs_attr *na, runlist_element *wrl, s64 offs, - VCN *update_from ) +int ntfs_compressed_close(ntfs_attr *na, runlist_element *wrl, s64 offs, + VCN *update_from) { - ntfs_volume *vol; - runlist_element *brl; /* entry containing the beginning of block */ - int compression_length; - s64 written; - s64 to_read; - s64 roffs; - s64 got; - s64 start_vcn; - char *inbuf; - BOOL fail; - BOOL done; + ntfs_volume *vol; + runlist_element *brl; /* entry containing the beginning of block */ + int compression_length; + s64 written; + s64 to_read; + s64 roffs; + s64 got; + s64 start_vcn; + char *inbuf; + BOOL fail; + BOOL done; - if ( na->unused_runs < 2 ) - { - ntfs_log_error( "No unused runs for compressed close\n" ); - errno = EIO; - return ( -1 ); - } - if ( *update_from < 0 ) - { - ntfs_log_error( "Bad update vcn for compressed close\n" ); - errno = EIO; - return ( -1 ); - } - if ( wrl->vcn < *update_from ) - *update_from = wrl->vcn; - vol = na->ni->vol; - compression_length = na->compression_block_clusters; - done = FALSE; - /* - * There generally is an uncompressed block at end of file, - * read the full block and compress it - */ - inbuf = ( char* )ntfs_malloc( na->compression_block_size ); - if ( inbuf ) - { - start_vcn = ( wrl->vcn + ( offs >> vol->cluster_size_bits ) ) - & -compression_length; - if ( start_vcn < *update_from ) - *update_from = start_vcn; - to_read = offs + ( ( wrl->vcn - start_vcn ) - << vol->cluster_size_bits ); - brl = wrl; - fail = FALSE; - while ( brl->vcn && ( brl->vcn > start_vcn ) ) - { - if ( brl->lcn == ( LCN )LCN_HOLE ) - { - ntfs_log_error( "jump back over a hole when closing\n" ); - fail = TRUE; - errno = EIO; - } - brl--; - } - if ( !fail ) - { - /* roffs can be an offset from another uncomp block */ - roffs = ( start_vcn - brl->vcn ) - << vol->cluster_size_bits; - if ( to_read ) - { - got = read_clusters( vol, brl, roffs, to_read, - inbuf ); - if ( got == to_read ) - { - written = ntfs_comp_set( na, brl, roffs, - to_read, inbuf ); - if ( ( written >= 0 ) - /* free the unused clusters */ - && !ntfs_compress_free( na, brl, - written + roffs, - na->compression_block_size + roffs, - TRUE, update_from ) ) - { - done = TRUE; - } - else - /* if compression failed, leave uncompressed */ - if ( written == -1 ) - done = TRUE; - } - } - else - done = TRUE; - free( inbuf ); - } - } - if ( done && !valid_compressed_run( na, wrl, TRUE, "end compressed close" ) ) - done = FALSE; - return ( !done ); + if (na->unused_runs < 2) { + ntfs_log_error("No unused runs for compressed close\n"); + errno = EIO; + return (-1); + } + if (*update_from < 0) { + ntfs_log_error("Bad update vcn for compressed close\n"); + errno = EIO; + return (-1); + } + if (wrl->vcn < *update_from) + *update_from = wrl->vcn; + vol = na->ni->vol; + compression_length = na->compression_block_clusters; + done = FALSE; + /* + * There generally is an uncompressed block at end of file, + * read the full block and compress it + */ + inbuf = (char*)ntfs_malloc(na->compression_block_size); + if (inbuf) { + start_vcn = (wrl->vcn + (offs >> vol->cluster_size_bits)) + & -compression_length; + if (start_vcn < *update_from) + *update_from = start_vcn; + to_read = offs + ((wrl->vcn - start_vcn) + << vol->cluster_size_bits); + brl = wrl; + fail = FALSE; + while (brl->vcn && (brl->vcn > start_vcn)) { + if (brl->lcn == (LCN)LCN_HOLE) { + ntfs_log_error("jump back over a hole when closing\n"); + fail = TRUE; + errno = EIO; + } + brl--; + } + if (!fail) { + /* roffs can be an offset from another uncomp block */ + roffs = (start_vcn - brl->vcn) + << vol->cluster_size_bits; + if (to_read) { + got = read_clusters(vol, brl, roffs, to_read, + inbuf); + if (got == to_read) { + written = ntfs_comp_set(na, brl, roffs, + to_read, inbuf); + if ((written >= 0) + /* free the unused clusters */ + && !ntfs_compress_free(na,brl, + written + roffs, + na->compression_block_size + roffs, + TRUE, update_from)) { + done = TRUE; + } else + /* if compression failed, leave uncompressed */ + if (written == -1) + done = TRUE; + } + } else + done = TRUE; + free(inbuf); + } + } + if (done && !valid_compressed_run(na,wrl,TRUE,"end compressed close")) + done = FALSE; + return (!done); } diff --git a/source/libntfs/compress.h b/source/libntfs/compress.h index b2f7dba1..c2569321 100644 --- a/source/libntfs/compress.h +++ b/source/libntfs/compress.h @@ -1,6 +1,6 @@ /* - * compress.h - Exports for compressed attribute handling. - * Originated from the Linux-NTFS project. + * compress.h - Exports for compressed attribute handling. + * Originated from the Linux-NTFS project. * * Copyright (c) 2004 Anton Altaparmakov * @@ -26,16 +26,16 @@ #include "types.h" #include "attrib.h" -extern s64 ntfs_compressed_attr_pread( ntfs_attr *na, s64 pos, s64 count, - void *b ); +extern s64 ntfs_compressed_attr_pread(ntfs_attr *na, s64 pos, s64 count, + void *b); -extern s64 ntfs_compressed_pwrite( ntfs_attr *na, runlist_element *brl, s64 wpos, - s64 offs, s64 to_write, s64 rounded, - const void *b, int compressed_part, - VCN *update_from ); +extern s64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *brl, s64 wpos, + s64 offs, s64 to_write, s64 rounded, + const void *b, int compressed_part, + VCN *update_from); -extern int ntfs_compressed_close( ntfs_attr *na, runlist_element *brl, - s64 offs, VCN *update_from ); +extern int ntfs_compressed_close(ntfs_attr *na, runlist_element *brl, + s64 offs, VCN *update_from); #endif /* defined _NTFS_COMPRESS_H */ diff --git a/source/libntfs/debug.c b/source/libntfs/debug.c index 4ba2a855..f1934833 100644 --- a/source/libntfs/debug.c +++ b/source/libntfs/debug.c @@ -42,43 +42,37 @@ * * Returns: */ -void ntfs_debug_runlist_dump( const runlist_element *rl ) +void ntfs_debug_runlist_dump(const runlist_element *rl) { - int i = 0; - const char *lcn_str[5] = { "LCN_HOLE ", "LCN_RL_NOT_MAPPED", - "LCN_ENOENT ", "LCN_EINVAL ", - "LCN_unknown " - }; + int i = 0; + const char *lcn_str[5] = { "LCN_HOLE ", "LCN_RL_NOT_MAPPED", + "LCN_ENOENT ", "LCN_EINVAL ", + "LCN_unknown " }; - ntfs_log_debug( "NTFS-fs DEBUG: Dumping runlist (values in hex):\n" ); - if ( !rl ) - { - ntfs_log_debug( "Run list not present.\n" ); - return; - } - ntfs_log_debug( "VCN LCN Run length\n" ); - do - { - LCN lcn = ( rl + i )->lcn; + ntfs_log_debug("NTFS-fs DEBUG: Dumping runlist (values in hex):\n"); + if (!rl) { + ntfs_log_debug("Run list not present.\n"); + return; + } + ntfs_log_debug("VCN LCN Run length\n"); + do { + LCN lcn = (rl + i)->lcn; - if ( lcn < ( LCN )0 ) - { - int idx = -lcn - 1; + if (lcn < (LCN)0) { + int idx = -lcn - 1; - if ( idx > -LCN_EINVAL - 1 ) - idx = 4; - ntfs_log_debug( "%-16lld %s %-16lld%s\n", - ( long long )rl[i].vcn, lcn_str[idx], - ( long long )rl[i].length, - rl[i].length ? "" : " (runlist end)" ); - } - else - ntfs_log_debug( "%-16lld %-16lld %-16lld%s\n", - ( long long )rl[i].vcn, ( long long )rl[i].lcn, - ( long long )rl[i].length, - rl[i].length ? "" : " (runlist end)" ); - } - while ( rl[i++].length ); + if (idx > -LCN_EINVAL - 1) + idx = 4; + ntfs_log_debug("%-16lld %s %-16lld%s\n", + (long long)rl[i].vcn, lcn_str[idx], + (long long)rl[i].length, + rl[i].length ? "" : " (runlist end)"); + } else + ntfs_log_debug("%-16lld %-16lld %-16lld%s\n", + (long long)rl[i].vcn, (long long)rl[i].lcn, + (long long)rl[i].length, + rl[i].length ? "" : " (runlist end)"); + } while (rl[i++].length); } #endif diff --git a/source/libntfs/debug.h b/source/libntfs/debug.h index 1569f892..cf39b625 100644 --- a/source/libntfs/debug.h +++ b/source/libntfs/debug.h @@ -31,17 +31,17 @@ struct _runlist_element; #ifdef DEBUG -extern void ntfs_debug_runlist_dump( const struct _runlist_element *rl ); +extern void ntfs_debug_runlist_dump(const struct _runlist_element *rl); #else -static __inline__ void ntfs_debug_runlist_dump( const struct _runlist_element *rl __attribute__( ( unused ) ) ) {} +static __inline__ void ntfs_debug_runlist_dump(const struct _runlist_element *rl __attribute__((unused))) {} #endif -#define NTFS_BUG(msg) \ -{ \ - int ___i; \ - ntfs_log_critical("Bug in %s(): %s\n", __FUNCTION__, msg); \ - ntfs_log_debug("Forcing segmentation fault!"); \ - ___i = ((int*)NULL)[1]; \ +#define NTFS_BUG(msg) \ +{ \ + int ___i; \ + ntfs_log_critical("Bug in %s(): %s\n", __FUNCTION__, msg); \ + ntfs_log_debug("Forcing segmentation fault!"); \ + ___i = ((int*)NULL)[1]; \ } #endif /* defined _NTFS_DEBUG_H */ diff --git a/source/libntfs/device.c b/source/libntfs/device.c index 611b949a..c77d8f95 100644 --- a/source/libntfs/device.c +++ b/source/libntfs/device.c @@ -72,27 +72,27 @@ #include "misc.h" #if defined(linux) && defined(_IO) && !defined(BLKGETSIZE) -#define BLKGETSIZE _IO(0x12,96) /* Get device size in 512-byte blocks. */ +#define BLKGETSIZE _IO(0x12,96) /* Get device size in 512-byte blocks. */ #endif #if defined(linux) && defined(_IOR) && !defined(BLKGETSIZE64) -#define BLKGETSIZE64 _IOR(0x12,114,size_t) /* Get device size in bytes. */ +#define BLKGETSIZE64 _IOR(0x12,114,size_t) /* Get device size in bytes. */ #endif #if defined(linux) && !defined(HDIO_GETGEO) -#define HDIO_GETGEO 0x0301 /* Get device geometry. */ +#define HDIO_GETGEO 0x0301 /* Get device geometry. */ #endif #if defined(linux) && defined(_IO) && !defined(BLKSSZGET) -# define BLKSSZGET _IO(0x12,104) /* Get device sector size in bytes. */ +# define BLKSSZGET _IO(0x12,104) /* Get device sector size in bytes. */ #endif #if defined(linux) && defined(_IO) && !defined(BLKBSZSET) -# define BLKBSZSET _IOW(0x12,113,size_t) /* Set device block size in bytes. */ +# define BLKBSZSET _IOW(0x12,113,size_t) /* Set device block size in bytes. */ #endif /** * ntfs_device_alloc - allocate an ntfs device structure and pre-initialize it - * @name: name of the device (must be present) - * @state: initial device state (usually zero) - * @dops: ntfs device operations to use with the device (must be present) - * @priv_data: pointer to private data (optional) + * @name: name of the device (must be present) + * @state: initial device state (usually zero) + * @dops: ntfs device operations to use with the device (must be present) + * @priv_data: pointer to private data (optional) * * Allocate an ntfs device structure and pre-initialize it with the user- * specified device operations @dops, device state @state, device name @name, @@ -103,68 +103,63 @@ * On success return a pointer to the allocated ntfs device structure and on * error return NULL with errno set to the error code returned by ntfs_malloc(). */ -struct ntfs_device *ntfs_device_alloc( const char *name, const long state, - struct ntfs_device_operations *dops, void *priv_data ) +struct ntfs_device *ntfs_device_alloc(const char *name, const long state, + struct ntfs_device_operations *dops, void *priv_data) { - struct ntfs_device *dev; + struct ntfs_device *dev; - if ( !name ) - { - errno = EINVAL; - return NULL; - } + if (!name) { + errno = EINVAL; + return NULL; + } - dev = ntfs_malloc( sizeof( struct ntfs_device ) ); - if ( dev ) - { - if ( !( dev->d_name = strdup( name ) ) ) - { - int eo = errno; - free( dev ); - errno = eo; - return NULL; - } - dev->d_ops = dops; - dev->d_state = state; - dev->d_private = priv_data; - } - return dev; + dev = ntfs_malloc(sizeof(struct ntfs_device)); + if (dev) { + if (!(dev->d_name = strdup(name))) { + int eo = errno; + free(dev); + errno = eo; + return NULL; + } + dev->d_ops = dops; + dev->d_state = state; + dev->d_private = priv_data; + } + return dev; } /** * ntfs_device_free - free an ntfs device structure - * @dev: ntfs device structure to free + * @dev: ntfs device structure to free * * Free the ntfs device structure @dev. * * Return 0 on success or -1 on error with errno set to the error code. The * following error codes are defined: - * EINVAL Invalid pointer @dev. - * EBUSY Device is still open. Close it before freeing it! + * EINVAL Invalid pointer @dev. + * EBUSY Device is still open. Close it before freeing it! */ -int ntfs_device_free( struct ntfs_device *dev ) +int ntfs_device_free(struct ntfs_device *dev) { - if ( !dev ) - { - errno = EINVAL; - return -1; - } - if ( NDevOpen( dev ) ) - { - errno = EBUSY; - return -1; - } - free( dev->d_name ); - free( dev ); - return 0; + if (!dev) { + errno = EINVAL; + return -1; + } + if (NDevOpen(dev)) { + errno = EBUSY; + return -1; + } + free(dev->d_name); + free(dev); + return 0; } /** * ntfs_pread - positioned read from disk - * @dev: device to read from - * @pos: position in device to read from - * @count: number of bytes to read - * @b: output data buffer + * @dev: device to read from + * @pos: position in device to read from + * @count: number of bytes to read + * @b: output data buffer * * This function will read @count bytes from device @dev at position @pos into * the data buffer @b. @@ -178,45 +173,43 @@ int ntfs_device_free( struct ntfs_device *dev ) * to the return code of either seek, read, or set to EINVAL in case of * invalid arguments. */ -s64 ntfs_pread( struct ntfs_device *dev, const s64 pos, s64 count, void *b ) +s64 ntfs_pread(struct ntfs_device *dev, const s64 pos, s64 count, void *b) { - s64 br, total; - struct ntfs_device_operations *dops; + s64 br, total; + struct ntfs_device_operations *dops; - ntfs_log_trace( "pos %lld, count %lld\n", ( long long )pos, ( long long )count ); + ntfs_log_trace("pos %lld, count %lld\n",(long long)pos,(long long)count); + + if (!b || count < 0 || pos < 0) { + errno = EINVAL; + return -1; + } + if (!count) + return 0; + + dops = dev->d_ops; - if ( !b || count < 0 || pos < 0 ) - { - errno = EINVAL; - return -1; - } - if ( !count ) - return 0; - - dops = dev->d_ops; - - for ( total = 0; count; count -= br, total += br ) - { - br = dops->pread( dev, ( char* )b + total, count, pos + total ); - /* If everything ok, continue. */ - if ( br > 0 ) - continue; - /* If EOF or error return number of bytes read. */ - if ( !br || total ) - return total; - /* Nothing read and error, return error status. */ - return br; - } - /* Finally, return the number of bytes read. */ - return total; + for (total = 0; count; count -= br, total += br) { + br = dops->pread(dev, (char*)b + total, count, pos + total); + /* If everything ok, continue. */ + if (br > 0) + continue; + /* If EOF or error return number of bytes read. */ + if (!br || total) + return total; + /* Nothing read and error, return error status. */ + return br; + } + /* Finally, return the number of bytes read. */ + return total; } /** * ntfs_pwrite - positioned write to disk - * @dev: device to write to - * @pos: position in file descriptor to write to - * @count: number of bytes to write - * @b: data buffer to write to disk + * @dev: device to write to + * @pos: position in file descriptor 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 the device @dev * at position @pos. @@ -230,58 +223,55 @@ s64 ntfs_pread( struct ntfs_device *dev, const s64 pos, s64 count, void *b ) * appropriately to the return code of either seek, write, or set * to EINVAL in case of invalid arguments. */ -s64 ntfs_pwrite( struct ntfs_device *dev, const s64 pos, s64 count, - const void *b ) +s64 ntfs_pwrite(struct ntfs_device *dev, const s64 pos, s64 count, + const void *b) { - s64 written, total, ret = -1; - struct ntfs_device_operations *dops; + s64 written, total, ret = -1; + struct ntfs_device_operations *dops; - ntfs_log_trace( "pos %lld, count %lld\n", ( long long )pos, ( long long )count ); + ntfs_log_trace("pos %lld, count %lld\n",(long long)pos,(long long)count); - if ( !b || count < 0 || pos < 0 ) - { - errno = EINVAL; - goto out; - } - if ( !count ) - return 0; - if ( NDevReadOnly( dev ) ) - { - errno = EROFS; - goto out; - } + if (!b || count < 0 || pos < 0) { + errno = EINVAL; + goto out; + } + if (!count) + return 0; + if (NDevReadOnly(dev)) { + errno = EROFS; + goto out; + } + + dops = dev->d_ops; - dops = dev->d_ops; - - NDevSetDirty( dev ); - for ( total = 0; count; count -= written, total += written ) - { - written = dops->pwrite( dev, ( const char* )b + total, count, - pos + total ); - /* If everything ok, continue. */ - if ( written > 0 ) - continue; - /* - * If nothing written or error return number of bytes written. - */ - if ( !written || total ) - break; - /* Nothing written and error, return error status. */ - total = written; - break; - } - ret = total; -out: - return ret; + NDevSetDirty(dev); + for (total = 0; count; count -= written, total += written) { + written = dops->pwrite(dev, (const char*)b + total, count, + pos + total); + /* If everything ok, continue. */ + if (written > 0) + continue; + /* + * If nothing written or error return number of bytes written. + */ + if (!written || total) + break; + /* Nothing written and error, return error status. */ + total = written; + break; + } + ret = total; +out: + return ret; } /** * ntfs_mst_pread - multi sector transfer (mst) positioned read - * @dev: device to read from - * @pos: position in file descriptor to read from - * @count: number of blocks to read - * @bksize: size of each block that needs mst deprotecting - * @b: output data buffer + * @dev: device to read from + * @pos: position in file descriptor to read from + * @count: number of blocks to read + * @bksize: size of each block that needs mst deprotecting + * @b: output data buffer * * Multi sector transfer (mst) positioned read. This function will read @count * blocks of size @bksize bytes each from device @dev at position @pos into the @@ -304,41 +294,40 @@ out: * sector transfer error. This should be detected by the caller by checking for * the magic being "BAAD". */ -s64 ntfs_mst_pread( struct ntfs_device *dev, const s64 pos, s64 count, - const u32 bksize, void *b ) +s64 ntfs_mst_pread(struct ntfs_device *dev, const s64 pos, s64 count, + const u32 bksize, void *b) { - s64 br, i; + s64 br, i; - if ( bksize & ( bksize - 1 ) || bksize % NTFS_BLOCK_SIZE ) - { - errno = EINVAL; - return -1; - } - /* Do the read. */ - br = ntfs_pread( dev, pos, count * bksize, b ); - if ( br < 0 ) - return br; - /* - * Apply fixups to successfully read data, disregarding any errors - * returned from the MST fixup function. This is because we want to - * fixup everything possible and we rely on the fact that the "BAAD" - * magic will be detected later on. - */ - count = br / bksize; - for ( i = 0; i < count; ++i ) - ntfs_mst_post_read_fixup( ( NTFS_RECORD* ) - ( ( u8* )b + i * bksize ), bksize ); - /* Finally, return the number of complete blocks read. */ - return count; + if (bksize & (bksize - 1) || bksize % NTFS_BLOCK_SIZE) { + errno = EINVAL; + return -1; + } + /* Do the read. */ + br = ntfs_pread(dev, pos, count * bksize, b); + if (br < 0) + return br; + /* + * Apply fixups to successfully read data, disregarding any errors + * returned from the MST fixup function. This is because we want to + * fixup everything possible and we rely on the fact that the "BAAD" + * magic will be detected later on. + */ + count = br / bksize; + for (i = 0; i < count; ++i) + ntfs_mst_post_read_fixup((NTFS_RECORD*) + ((u8*)b + i * bksize), bksize); + /* Finally, return the number of complete blocks read. */ + return count; } /** * ntfs_mst_pwrite - multi sector transfer (mst) positioned write - * @dev: device to write to - * @pos: position in file descriptor to write to - * @count: number of blocks to write - * @bksize: size of each block that needs mst protecting - * @b: data buffer to write to disk + * @dev: device to write to + * @pos: position in file descriptor to write to + * @count: number of blocks to write + * @bksize: size of each block that needs mst protecting + * @b: data buffer to write to disk * * Multi sector transfer (mst) positioned write. This function will write * @count blocks of size @bksize bytes each from data buffer @b to the device @@ -362,150 +351,141 @@ s64 ntfs_mst_pread( struct ntfs_device *dev, const s64 pos, s64 count, * simulating an mst read on the written data. This way cache coherency is * achieved. */ -s64 ntfs_mst_pwrite( struct ntfs_device *dev, const s64 pos, s64 count, - const u32 bksize, void *b ) +s64 ntfs_mst_pwrite(struct ntfs_device *dev, const s64 pos, s64 count, + const u32 bksize, void *b) { - s64 written, i; + s64 written, i; - if ( count < 0 || bksize % NTFS_BLOCK_SIZE ) - { - errno = EINVAL; - return -1; - } - if ( !count ) - return 0; - /* Prepare data for writing. */ - for ( i = 0; i < count; ++i ) - { - int err; + if (count < 0 || bksize % NTFS_BLOCK_SIZE) { + errno = EINVAL; + return -1; + } + if (!count) + return 0; + /* Prepare data for writing. */ + for (i = 0; i < count; ++i) { + int err; - err = ntfs_mst_pre_write_fixup( ( NTFS_RECORD* ) - ( ( u8* )b + i * bksize ), bksize ); - if ( err < 0 ) - { - /* Abort write at this position. */ - if ( !i ) - return err; - count = i; - break; - } - } - /* Write the prepared data. */ - written = ntfs_pwrite( dev, pos, count * bksize, b ); - /* Quickly deprotect the data again. */ - for ( i = 0; i < count; ++i ) - ntfs_mst_post_write_fixup( ( NTFS_RECORD* )( ( u8* )b + i * bksize ) ); - if ( written <= 0 ) - return written; - /* Finally, return the number of complete blocks written. */ - return written / bksize; + err = ntfs_mst_pre_write_fixup((NTFS_RECORD*) + ((u8*)b + i * bksize), bksize); + if (err < 0) { + /* Abort write at this position. */ + if (!i) + return err; + count = i; + break; + } + } + /* Write the prepared data. */ + written = ntfs_pwrite(dev, pos, count * bksize, b); + /* Quickly deprotect the data again. */ + for (i = 0; i < count; ++i) + ntfs_mst_post_write_fixup((NTFS_RECORD*)((u8*)b + i * bksize)); + if (written <= 0) + return written; + /* Finally, return the number of complete blocks written. */ + return written / bksize; } /** * ntfs_cluster_read - read ntfs clusters - * @vol: volume to read from - * @lcn: starting logical cluster number - * @count: number of clusters to read - * @b: output data buffer + * @vol: volume to read from + * @lcn: starting logical cluster number + * @count: number of clusters to read + * @b: output data buffer * * Read @count ntfs clusters starting at logical cluster number @lcn from * volume @vol into buffer @b. Return number of clusters read or -1 on error, * with errno set to the error code. */ -s64 ntfs_cluster_read( const ntfs_volume *vol, const s64 lcn, const s64 count, - void *b ) +s64 ntfs_cluster_read(const ntfs_volume *vol, const s64 lcn, const s64 count, + void *b) { - s64 br; + s64 br; - if ( !vol || lcn < 0 || count < 0 ) - { - errno = EINVAL; - return -1; - } - if ( vol->nr_clusters < lcn + count ) - { - errno = ESPIPE; - ntfs_log_perror( "Trying to read outside of volume " - "(%lld < %lld)", ( long long )vol->nr_clusters, - ( long long )lcn + count ); - return -1; - } - br = ntfs_pread( vol->dev, lcn << vol->cluster_size_bits, - count << vol->cluster_size_bits, b ); - if ( br < 0 ) - { - ntfs_log_perror( "Error reading cluster(s)" ); - return br; - } - return br >> vol->cluster_size_bits; + if (!vol || lcn < 0 || count < 0) { + errno = EINVAL; + return -1; + } + if (vol->nr_clusters < lcn + count) { + errno = ESPIPE; + ntfs_log_perror("Trying to read outside of volume " + "(%lld < %lld)", (long long)vol->nr_clusters, + (long long)lcn + count); + return -1; + } + br = ntfs_pread(vol->dev, lcn << vol->cluster_size_bits, + count << vol->cluster_size_bits, b); + if (br < 0) { + ntfs_log_perror("Error reading cluster(s)"); + return br; + } + return br >> vol->cluster_size_bits; } /** * ntfs_cluster_write - write ntfs clusters - * @vol: volume to write to - * @lcn: starting logical cluster number - * @count: number of clusters to write - * @b: data buffer to write to disk + * @vol: volume to write to + * @lcn: starting logical cluster number + * @count: number of clusters to write + * @b: data buffer to write to disk * * Write @count ntfs clusters starting at logical cluster number @lcn from * buffer @b to volume @vol. Return the number of clusters written or -1 on * error, with errno set to the error code. */ -s64 ntfs_cluster_write( const ntfs_volume *vol, const s64 lcn, - const s64 count, const void *b ) +s64 ntfs_cluster_write(const ntfs_volume *vol, const s64 lcn, + const s64 count, const void *b) { - s64 bw; + s64 bw; - if ( !vol || lcn < 0 || count < 0 ) - { - errno = EINVAL; - return -1; - } - if ( vol->nr_clusters < lcn + count ) - { - errno = ESPIPE; - ntfs_log_perror( "Trying to write outside of volume " - "(%lld < %lld)", ( long long )vol->nr_clusters, - ( long long )lcn + count ); - return -1; - } - if ( !NVolReadOnly( vol ) ) - bw = ntfs_pwrite( vol->dev, lcn << vol->cluster_size_bits, - count << vol->cluster_size_bits, b ); - else - bw = count << vol->cluster_size_bits; - if ( bw < 0 ) - { - ntfs_log_perror( "Error writing cluster(s)" ); - return bw; - } - return bw >> vol->cluster_size_bits; + if (!vol || lcn < 0 || count < 0) { + errno = EINVAL; + return -1; + } + if (vol->nr_clusters < lcn + count) { + errno = ESPIPE; + ntfs_log_perror("Trying to write outside of volume " + "(%lld < %lld)", (long long)vol->nr_clusters, + (long long)lcn + count); + return -1; + } + if (!NVolReadOnly(vol)) + bw = ntfs_pwrite(vol->dev, lcn << vol->cluster_size_bits, + count << vol->cluster_size_bits, b); + else + bw = count << vol->cluster_size_bits; + if (bw < 0) { + ntfs_log_perror("Error writing cluster(s)"); + return bw; + } + return bw >> vol->cluster_size_bits; } /** * ntfs_device_offset_valid - test if a device offset is valid - * @dev: open device - * @ofs: offset to test for validity + * @dev: open device + * @ofs: offset to test for validity * * Test if the offset @ofs is an existing location on the device described * by the open device structure @dev. * * Return 0 if it is valid and -1 if it is not valid. */ -static int ntfs_device_offset_valid( struct ntfs_device *dev, s64 ofs ) +static int ntfs_device_offset_valid(struct ntfs_device *dev, s64 ofs) { - char ch; + char ch; - if ( dev->d_ops->seek( dev, ofs, SEEK_SET ) >= 0 && - dev->d_ops->read( dev, &ch, 1 ) == 1 ) - return 0; - return -1; + if (dev->d_ops->seek(dev, ofs, SEEK_SET) >= 0 && + dev->d_ops->read(dev, &ch, 1) == 1) + return 0; + return -1; } /** * ntfs_device_size_get - return the size of a device in blocks - * @dev: open device - * @block_size: block size in bytes in which to return the result + * @dev: open device + * @block_size: block size in bytes in which to return the result * * Return the number of @block_size sized blocks in the device described by the * open device @dev. @@ -514,252 +494,237 @@ static int ntfs_device_offset_valid( struct ntfs_device *dev, s64 ofs ) * * On error return -1 with errno set to the error code. */ -s64 ntfs_device_size_get( struct ntfs_device *dev, int block_size ) +s64 ntfs_device_size_get(struct ntfs_device *dev, int block_size) { - s64 high, low; + s64 high, low; - if ( !dev || block_size <= 0 || ( block_size - 1 ) & block_size ) - { - errno = EINVAL; - return -1; - } + if (!dev || block_size <= 0 || (block_size - 1) & block_size) { + errno = EINVAL; + return -1; + } #ifdef BLKGETSIZE64 - { u64 size; + { u64 size; - if ( dev->d_ops->ioctl( dev, BLKGETSIZE64, &size ) >= 0 ) - { - ntfs_log_debug( "BLKGETSIZE64 nr bytes = %llu (0x%llx)\n", - ( unsigned long long )size, - ( unsigned long long )size ); - return ( s64 )size / block_size; - } - } + if (dev->d_ops->ioctl(dev, BLKGETSIZE64, &size) >= 0) { + ntfs_log_debug("BLKGETSIZE64 nr bytes = %llu (0x%llx)\n", + (unsigned long long)size, + (unsigned long long)size); + return (s64)size / block_size; + } + } #endif #ifdef BLKGETSIZE - { unsigned long size; + { unsigned long size; - if ( dev->d_ops->ioctl( dev, BLKGETSIZE, &size ) >= 0 ) - { - ntfs_log_debug( "BLKGETSIZE nr 512 byte blocks = %lu (0x%lx)\n", - size, size ); - return ( s64 )size * 512 / block_size; - } - } + if (dev->d_ops->ioctl(dev, BLKGETSIZE, &size) >= 0) { + ntfs_log_debug("BLKGETSIZE nr 512 byte blocks = %lu (0x%lx)\n", + size, size); + return (s64)size * 512 / block_size; + } + } #endif #ifdef FDGETPRM - { struct floppy_struct this_floppy; + { struct floppy_struct this_floppy; - if ( dev->d_ops->ioctl( dev, FDGETPRM, &this_floppy ) >= 0 ) - { - ntfs_log_debug( "FDGETPRM nr 512 byte blocks = %lu (0x%lx)\n", - ( unsigned long )this_floppy.size, - ( unsigned long )this_floppy.size ); - return ( s64 )this_floppy.size * 512 / block_size; - } - } + if (dev->d_ops->ioctl(dev, FDGETPRM, &this_floppy) >= 0) { + ntfs_log_debug("FDGETPRM nr 512 byte blocks = %lu (0x%lx)\n", + (unsigned long)this_floppy.size, + (unsigned long)this_floppy.size); + return (s64)this_floppy.size * 512 / block_size; + } + } #endif - /* - * We couldn't figure it out by using a specialized ioctl, - * so do binary search to find the size of the device. - */ - low = 0LL; - for ( high = 1024LL; !ntfs_device_offset_valid( dev, high ); high <<= 1 ) - low = high; - while ( low < high - 1LL ) - { - const s64 mid = ( low + high ) / 2; + /* + * We couldn't figure it out by using a specialized ioctl, + * so do binary search to find the size of the device. + */ + low = 0LL; + for (high = 1024LL; !ntfs_device_offset_valid(dev, high); high <<= 1) + low = high; + while (low < high - 1LL) { + const s64 mid = (low + high) / 2; - if ( !ntfs_device_offset_valid( dev, mid ) ) - low = mid; - else - high = mid; - } - dev->d_ops->seek( dev, 0LL, SEEK_SET ); - return ( low + 1LL ) / block_size; + if (!ntfs_device_offset_valid(dev, mid)) + low = mid; + else + high = mid; + } + dev->d_ops->seek(dev, 0LL, SEEK_SET); + return (low + 1LL) / block_size; } /** * ntfs_device_partition_start_sector_get - get starting sector of a partition - * @dev: open device + * @dev: open device * * On success, return the starting sector of the partition @dev in the parent * block device of @dev. On error return -1 with errno set to the error code. * * The following error codes are defined: - * EINVAL Input parameter error - * EOPNOTSUPP System does not support HDIO_GETGEO ioctl - * ENOTTY @dev is a file or a device not supporting HDIO_GETGEO + * EINVAL Input parameter error + * EOPNOTSUPP System does not support HDIO_GETGEO ioctl + * ENOTTY @dev is a file or a device not supporting HDIO_GETGEO */ -s64 ntfs_device_partition_start_sector_get( struct ntfs_device *dev ) +s64 ntfs_device_partition_start_sector_get(struct ntfs_device *dev) { - if ( !dev ) - { - errno = EINVAL; - return -1; - } + if (!dev) { + errno = EINVAL; + return -1; + } #ifdef HDIO_GETGEO - { struct hd_geometry geo; + { struct hd_geometry geo; - if ( !dev->d_ops->ioctl( dev, HDIO_GETGEO, &geo ) ) - { - ntfs_log_debug( "HDIO_GETGEO start_sect = %lu (0x%lx)\n", - geo.start, geo.start ); - return geo.start; - } - } + if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) { + ntfs_log_debug("HDIO_GETGEO start_sect = %lu (0x%lx)\n", + geo.start, geo.start); + return geo.start; + } + } #else - errno = EOPNOTSUPP; + errno = EOPNOTSUPP; #endif - return -1; + return -1; } /** * ntfs_device_heads_get - get number of heads of device - * @dev: open device + * @dev: open device * * On success, return the number of heads on the device @dev. On error return * -1 with errno set to the error code. * * The following error codes are defined: - * EINVAL Input parameter error - * EOPNOTSUPP System does not support HDIO_GETGEO ioctl - * ENOTTY @dev is a file or a device not supporting HDIO_GETGEO + * EINVAL Input parameter error + * EOPNOTSUPP System does not support HDIO_GETGEO ioctl + * ENOTTY @dev is a file or a device not supporting HDIO_GETGEO */ -int ntfs_device_heads_get( struct ntfs_device *dev ) +int ntfs_device_heads_get(struct ntfs_device *dev) { - if ( !dev ) - { - errno = EINVAL; - return -1; - } + if (!dev) { + errno = EINVAL; + return -1; + } #ifdef HDIO_GETGEO - { struct hd_geometry geo; + { struct hd_geometry geo; - if ( !dev->d_ops->ioctl( dev, HDIO_GETGEO, &geo ) ) - { - ntfs_log_debug( "HDIO_GETGEO heads = %u (0x%x)\n", - ( unsigned )geo.heads, - ( unsigned )geo.heads ); - return geo.heads; - } - } + if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) { + ntfs_log_debug("HDIO_GETGEO heads = %u (0x%x)\n", + (unsigned)geo.heads, + (unsigned)geo.heads); + return geo.heads; + } + } #else - errno = EOPNOTSUPP; + errno = EOPNOTSUPP; #endif - return -1; + return -1; } /** * ntfs_device_sectors_per_track_get - get number of sectors per track of device - * @dev: open device + * @dev: open device * * On success, return the number of sectors per track on the device @dev. On * error return -1 with errno set to the error code. * * The following error codes are defined: - * EINVAL Input parameter error - * EOPNOTSUPP System does not support HDIO_GETGEO ioctl - * ENOTTY @dev is a file or a device not supporting HDIO_GETGEO + * EINVAL Input parameter error + * EOPNOTSUPP System does not support HDIO_GETGEO ioctl + * ENOTTY @dev is a file or a device not supporting HDIO_GETGEO */ -int ntfs_device_sectors_per_track_get( struct ntfs_device *dev ) +int ntfs_device_sectors_per_track_get(struct ntfs_device *dev) { - if ( !dev ) - { - errno = EINVAL; - return -1; - } + if (!dev) { + errno = EINVAL; + return -1; + } #ifdef HDIO_GETGEO - { struct hd_geometry geo; + { struct hd_geometry geo; - if ( !dev->d_ops->ioctl( dev, HDIO_GETGEO, &geo ) ) - { - ntfs_log_debug( "HDIO_GETGEO sectors_per_track = %u (0x%x)\n", - ( unsigned )geo.sectors, - ( unsigned )geo.sectors ); - return geo.sectors; - } - } + if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) { + ntfs_log_debug("HDIO_GETGEO sectors_per_track = %u (0x%x)\n", + (unsigned)geo.sectors, + (unsigned)geo.sectors); + return geo.sectors; + } + } #else - errno = EOPNOTSUPP; + errno = EOPNOTSUPP; #endif - return -1; + return -1; } /** * ntfs_device_sector_size_get - get sector size of a device - * @dev: open device + * @dev: open device * * On success, return the sector size in bytes of the device @dev. * On error return -1 with errno set to the error code. * * The following error codes are defined: - * EINVAL Input parameter error - * EOPNOTSUPP System does not support BLKSSZGET ioctl - * ENOTTY @dev is a file or a device not supporting BLKSSZGET + * EINVAL Input parameter error + * EOPNOTSUPP System does not support BLKSSZGET ioctl + * ENOTTY @dev is a file or a device not supporting BLKSSZGET */ -int ntfs_device_sector_size_get( struct ntfs_device *dev ) +int ntfs_device_sector_size_get(struct ntfs_device *dev) { - if ( !dev ) - { - errno = EINVAL; - return -1; - } + if (!dev) { + errno = EINVAL; + return -1; + } #ifdef BLKSSZGET - { - int sect_size = 0; + { + int sect_size = 0; - if ( !dev->d_ops->ioctl( dev, BLKSSZGET, §_size ) ) - { - ntfs_log_debug( "BLKSSZGET sector size = %d bytes\n", - sect_size ); - return sect_size; - } - } + if (!dev->d_ops->ioctl(dev, BLKSSZGET, §_size)) { + ntfs_log_debug("BLKSSZGET sector size = %d bytes\n", + sect_size); + return sect_size; + } + } #else - errno = EOPNOTSUPP; + errno = EOPNOTSUPP; #endif - return -1; + return -1; } /** * ntfs_device_block_size_set - set block size of a device - * @dev: open device + * @dev: open device * @block_size: block size to set @dev to * * On success, return 0. * On error return -1 with errno set to the error code. * * The following error codes are defined: - * EINVAL Input parameter error - * EOPNOTSUPP System does not support BLKBSZSET ioctl - * ENOTTY @dev is a file or a device not supporting BLKBSZSET + * EINVAL Input parameter error + * EOPNOTSUPP System does not support BLKBSZSET ioctl + * ENOTTY @dev is a file or a device not supporting BLKBSZSET */ -int ntfs_device_block_size_set( struct ntfs_device *dev, - int block_size __attribute__( ( unused ) ) ) +int ntfs_device_block_size_set(struct ntfs_device *dev, + int block_size __attribute__((unused))) { - if ( !dev ) - { - errno = EINVAL; - return -1; - } + if (!dev) { + errno = EINVAL; + return -1; + } #ifdef BLKBSZSET - { - size_t s_block_size = block_size; - if ( !dev->d_ops->ioctl( dev, BLKBSZSET, &s_block_size ) ) - { - ntfs_log_debug( "Used BLKBSZSET to set block size to " - "%d bytes.\n", block_size ); - return 0; - } - /* If not a block device, pretend it was successful. */ - if ( !NDevBlock( dev ) ) - return 0; - } + { + size_t s_block_size = block_size; + if (!dev->d_ops->ioctl(dev, BLKBSZSET, &s_block_size)) { + ntfs_log_debug("Used BLKBSZSET to set block size to " + "%d bytes.\n", block_size); + return 0; + } + /* If not a block device, pretend it was successful. */ + if (!NDevBlock(dev)) + return 0; + } #else - /* If not a block device, pretend it was successful. */ - if ( !NDevBlock( dev ) ) - return 0; - errno = EOPNOTSUPP; + /* If not a block device, pretend it was successful. */ + if (!NDevBlock(dev)) + return 0; + errno = EOPNOTSUPP; #endif - return -1; + return -1; } diff --git a/source/libntfs/device.h b/source/libntfs/device.h index 6be5ac27..a19d29c4 100644 --- a/source/libntfs/device.h +++ b/source/libntfs/device.h @@ -36,33 +36,32 @@ * * Defined bits for the state field in the ntfs_device structure. */ -typedef enum -{ - ND_Open, /* 1: Device is open. */ - ND_ReadOnly, /* 1: Device is read-only. */ - ND_Dirty, /* 1: Device is dirty, needs sync. */ - ND_Block, /* 1: Device is a block device. */ +typedef enum { + ND_Open, /* 1: Device is open. */ + ND_ReadOnly, /* 1: Device is read-only. */ + ND_Dirty, /* 1: Device is dirty, needs sync. */ + ND_Block, /* 1: Device is a block device. */ } ntfs_device_state_bits; -#define test_ndev_flag(nd, flag) test_bit(ND_##flag, (nd)->d_state) -#define set_ndev_flag(nd, flag) set_bit(ND_##flag, (nd)->d_state) -#define clear_ndev_flag(nd, flag) clear_bit(ND_##flag, (nd)->d_state) +#define test_ndev_flag(nd, flag) test_bit(ND_##flag, (nd)->d_state) +#define set_ndev_flag(nd, flag) set_bit(ND_##flag, (nd)->d_state) +#define clear_ndev_flag(nd, flag) clear_bit(ND_##flag, (nd)->d_state) -#define NDevOpen(nd) test_ndev_flag(nd, Open) -#define NDevSetOpen(nd) set_ndev_flag(nd, Open) -#define NDevClearOpen(nd) clear_ndev_flag(nd, Open) +#define NDevOpen(nd) test_ndev_flag(nd, Open) +#define NDevSetOpen(nd) set_ndev_flag(nd, Open) +#define NDevClearOpen(nd) clear_ndev_flag(nd, Open) -#define NDevReadOnly(nd) test_ndev_flag(nd, ReadOnly) -#define NDevSetReadOnly(nd) set_ndev_flag(nd, ReadOnly) -#define NDevClearReadOnly(nd) clear_ndev_flag(nd, ReadOnly) +#define NDevReadOnly(nd) test_ndev_flag(nd, ReadOnly) +#define NDevSetReadOnly(nd) set_ndev_flag(nd, ReadOnly) +#define NDevClearReadOnly(nd) clear_ndev_flag(nd, ReadOnly) -#define NDevDirty(nd) test_ndev_flag(nd, Dirty) -#define NDevSetDirty(nd) set_ndev_flag(nd, Dirty) -#define NDevClearDirty(nd) clear_ndev_flag(nd, Dirty) +#define NDevDirty(nd) test_ndev_flag(nd, Dirty) +#define NDevSetDirty(nd) set_ndev_flag(nd, Dirty) +#define NDevClearDirty(nd) clear_ndev_flag(nd, Dirty) -#define NDevBlock(nd) test_ndev_flag(nd, Block) -#define NDevSetBlock(nd) set_ndev_flag(nd, Block) -#define NDevClearBlock(nd) clear_ndev_flag(nd, Block) +#define NDevBlock(nd) test_ndev_flag(nd, Block) +#define NDevSetBlock(nd) set_ndev_flag(nd, Block) +#define NDevClearBlock(nd) clear_ndev_flag(nd, Block) /** * struct ntfs_device - @@ -70,13 +69,12 @@ typedef enum * The ntfs device structure defining all operations needed to access the low * level device underlying the ntfs volume. */ -struct ntfs_device -{ - struct ntfs_device_operations *d_ops; /* Device operations. */ - unsigned long d_state; /* State of the device. */ - char *d_name; /* Name of device. */ - void *d_private; /* Private data used by the - device operations. */ +struct ntfs_device { + struct ntfs_device_operations *d_ops; /* Device operations. */ + unsigned long d_state; /* State of the device. */ + char *d_name; /* Name of device. */ + void *d_private; /* Private data used by the + device operations. */ }; struct stat; @@ -87,45 +85,44 @@ struct stat; * The ntfs device operations defining all operations that can be performed on * the low level device described by an ntfs device structure. */ -struct ntfs_device_operations -{ - int ( *open )( struct ntfs_device *dev, int flags ); - int ( *close )( struct ntfs_device *dev ); - s64 ( *seek )( struct ntfs_device *dev, s64 offset, int whence ); - s64 ( *read )( struct ntfs_device *dev, void *buf, s64 count ); - s64 ( *write )( struct ntfs_device *dev, const void *buf, s64 count ); - s64 ( *pread )( struct ntfs_device *dev, void *buf, s64 count, s64 offset ); - s64 ( *pwrite )( struct ntfs_device *dev, const void *buf, s64 count, - s64 offset ); - int ( *sync )( struct ntfs_device *dev ); - int ( *stat )( struct ntfs_device *dev, struct stat *buf ); - int ( *ioctl )( struct ntfs_device *dev, int request, void *argp ); +struct ntfs_device_operations { + int (*open)(struct ntfs_device *dev, int flags); + int (*close)(struct ntfs_device *dev); + s64 (*seek)(struct ntfs_device *dev, s64 offset, int whence); + s64 (*read)(struct ntfs_device *dev, void *buf, s64 count); + s64 (*write)(struct ntfs_device *dev, const void *buf, s64 count); + s64 (*pread)(struct ntfs_device *dev, void *buf, s64 count, s64 offset); + s64 (*pwrite)(struct ntfs_device *dev, const void *buf, s64 count, + s64 offset); + int (*sync)(struct ntfs_device *dev); + int (*stat)(struct ntfs_device *dev, struct stat *buf); + int (*ioctl)(struct ntfs_device *dev, int request, void *argp); }; -extern struct ntfs_device *ntfs_device_alloc( const char *name, const long state, - struct ntfs_device_operations *dops, void *priv_data ); -extern int ntfs_device_free( struct ntfs_device *dev ); +extern struct ntfs_device *ntfs_device_alloc(const char *name, const long state, + struct ntfs_device_operations *dops, void *priv_data); +extern int ntfs_device_free(struct ntfs_device *dev); -extern s64 ntfs_pread( struct ntfs_device *dev, const s64 pos, s64 count, - void *b ); -extern s64 ntfs_pwrite( struct ntfs_device *dev, const s64 pos, s64 count, - const void *b ); +extern s64 ntfs_pread(struct ntfs_device *dev, const s64 pos, s64 count, + void *b); +extern s64 ntfs_pwrite(struct ntfs_device *dev, const s64 pos, s64 count, + const void *b); -extern s64 ntfs_mst_pread( struct ntfs_device *dev, const s64 pos, s64 count, - const u32 bksize, void *b ); -extern s64 ntfs_mst_pwrite( struct ntfs_device *dev, const s64 pos, s64 count, - const u32 bksize, void *b ); +extern s64 ntfs_mst_pread(struct ntfs_device *dev, const s64 pos, s64 count, + const u32 bksize, void *b); +extern s64 ntfs_mst_pwrite(struct ntfs_device *dev, const s64 pos, s64 count, + const u32 bksize, void *b); -extern s64 ntfs_cluster_read( const ntfs_volume *vol, const s64 lcn, - const s64 count, void *b ); -extern s64 ntfs_cluster_write( const ntfs_volume *vol, const s64 lcn, - const s64 count, const void *b ); +extern s64 ntfs_cluster_read(const ntfs_volume *vol, const s64 lcn, + const s64 count, void *b); +extern s64 ntfs_cluster_write(const ntfs_volume *vol, const s64 lcn, + const s64 count, const void *b); -extern s64 ntfs_device_size_get( struct ntfs_device *dev, int block_size ); -extern s64 ntfs_device_partition_start_sector_get( struct ntfs_device *dev ); -extern int ntfs_device_heads_get( struct ntfs_device *dev ); -extern int ntfs_device_sectors_per_track_get( struct ntfs_device *dev ); -extern int ntfs_device_sector_size_get( struct ntfs_device *dev ); -extern int ntfs_device_block_size_set( struct ntfs_device *dev, int block_size ); +extern s64 ntfs_device_size_get(struct ntfs_device *dev, int block_size); +extern s64 ntfs_device_partition_start_sector_get(struct ntfs_device *dev); +extern int ntfs_device_heads_get(struct ntfs_device *dev); +extern int ntfs_device_sectors_per_track_get(struct ntfs_device *dev); +extern int ntfs_device_sector_size_get(struct ntfs_device *dev); +extern int ntfs_device_block_size_set(struct ntfs_device *dev, int block_size); #endif /* defined _NTFS_DEVICE_H */ diff --git a/source/libntfs/device_io.h b/source/libntfs/device_io.h index d9c4c857..fad4d85f 100644 --- a/source/libntfs/device_io.h +++ b/source/libntfs/device_io.h @@ -41,29 +41,28 @@ #else /* __CYGWIN32__ */ #ifndef HDIO_GETGEO -# define HDIO_GETGEO 0x301 +# define HDIO_GETGEO 0x301 /** * struct hd_geometry - */ -struct hd_geometry -{ - unsigned char heads; - unsigned char sectors; - unsigned short cylinders; - unsigned long start; +struct hd_geometry { + unsigned char heads; + unsigned char sectors; + unsigned short cylinders; + unsigned long start; }; #endif #ifndef BLKGETSIZE -# define BLKGETSIZE 0x1260 +# define BLKGETSIZE 0x1260 #endif #ifndef BLKSSZGET -# define BLKSSZGET 0x1268 +# define BLKSSZGET 0x1268 #endif #ifndef BLKGETSIZE64 -# define BLKGETSIZE64 0x80041272 +# define BLKGETSIZE64 0x80041272 #endif #ifndef BLKBSZSET -# define BLKBSZSET 0x40041271 +# define BLKBSZSET 0x40041271 #endif /* On Cygwin; use Win32 low level device operations. */ diff --git a/source/libntfs/dir.c b/source/libntfs/dir.c index d540aa5d..2372f3f8 100644 --- a/source/libntfs/dir.c +++ b/source/libntfs/dir.c @@ -70,99 +70,90 @@ * The little endian Unicode strings "$I30", "$SII", "$SDH", "$O" * and "$Q" as global constants. */ -ntfschar NTFS_INDEX_I30[5] = { const_cpu_to_le16( '$' ), const_cpu_to_le16( 'I' ), - const_cpu_to_le16( '3' ), const_cpu_to_le16( '0' ), - const_cpu_to_le16( '\0' ) - }; -ntfschar NTFS_INDEX_SII[5] = { const_cpu_to_le16( '$' ), const_cpu_to_le16( 'S' ), - const_cpu_to_le16( 'I' ), const_cpu_to_le16( 'I' ), - const_cpu_to_le16( '\0' ) - }; -ntfschar NTFS_INDEX_SDH[5] = { const_cpu_to_le16( '$' ), const_cpu_to_le16( 'S' ), - const_cpu_to_le16( 'D' ), const_cpu_to_le16( 'H' ), - const_cpu_to_le16( '\0' ) - }; -ntfschar NTFS_INDEX_O[3] = { const_cpu_to_le16( '$' ), const_cpu_to_le16( 'O' ), - const_cpu_to_le16( '\0' ) - }; -ntfschar NTFS_INDEX_Q[3] = { const_cpu_to_le16( '$' ), const_cpu_to_le16( 'Q' ), - const_cpu_to_le16( '\0' ) - }; -ntfschar NTFS_INDEX_R[3] = { const_cpu_to_le16( '$' ), const_cpu_to_le16( 'R' ), - const_cpu_to_le16( '\0' ) - }; +ntfschar NTFS_INDEX_I30[5] = { const_cpu_to_le16('$'), const_cpu_to_le16('I'), + const_cpu_to_le16('3'), const_cpu_to_le16('0'), + const_cpu_to_le16('\0') }; +ntfschar NTFS_INDEX_SII[5] = { const_cpu_to_le16('$'), const_cpu_to_le16('S'), + const_cpu_to_le16('I'), const_cpu_to_le16('I'), + const_cpu_to_le16('\0') }; +ntfschar NTFS_INDEX_SDH[5] = { const_cpu_to_le16('$'), const_cpu_to_le16('S'), + const_cpu_to_le16('D'), const_cpu_to_le16('H'), + const_cpu_to_le16('\0') }; +ntfschar NTFS_INDEX_O[3] = { const_cpu_to_le16('$'), const_cpu_to_le16('O'), + const_cpu_to_le16('\0') }; +ntfschar NTFS_INDEX_Q[3] = { const_cpu_to_le16('$'), const_cpu_to_le16('Q'), + const_cpu_to_le16('\0') }; +ntfschar NTFS_INDEX_R[3] = { const_cpu_to_le16('$'), const_cpu_to_le16('R'), + const_cpu_to_le16('\0') }; #if CACHE_INODE_SIZE /* - * Pathname hashing + * Pathname hashing * - * Based on first char and second char (which may be '\0') + * Based on first char and second char (which may be '\0') */ -int ntfs_dir_inode_hash( const struct CACHED_GENERIC *cached ) +int ntfs_dir_inode_hash(const struct CACHED_GENERIC *cached) { - const char *path; - const unsigned char *name; + const char *path; + const unsigned char *name; - path = ( const char* )cached->variable; - if ( !path ) - { - ntfs_log_error( "Bad inode cache entry\n" ); - return ( -1 ); - } - name = ( const unsigned char* )strrchr( path, '/' ); - if ( !name ) - name = ( const unsigned char* )path; - return ( ( ( name[0] << 1 ) + name[1] + strlen( ( const char* )name ) ) - % ( 2*CACHE_INODE_SIZE ) ); + path = (const char*)cached->variable; + if (!path) { + ntfs_log_error("Bad inode cache entry\n"); + return (-1); + } + name = (const unsigned char*)strrchr(path,'/'); + if (!name) + name = (const unsigned char*)path; + return (((name[0] << 1) + name[1] + strlen((const char*)name)) + % (2*CACHE_INODE_SIZE)); } /* - * Pathname comparing for entering/fetching from cache + * Pathname comparing for entering/fetching from cache */ -static int inode_cache_compare( const struct CACHED_GENERIC *cached, - const struct CACHED_GENERIC *wanted ) +static int inode_cache_compare(const struct CACHED_GENERIC *cached, + const struct CACHED_GENERIC *wanted) { - return ( !cached->variable - || strcmp( cached->variable, wanted->variable ) ); + return (!cached->variable + || strcmp(cached->variable, wanted->variable)); } /* - * Pathname comparing for invalidating entries in cache + * Pathname comparing for invalidating entries in cache * - * A partial path is compared in order to invalidate all paths - * related to a renamed directory - * inode numbers are also checked, as deleting a long name may - * imply deleting a short name and conversely + * A partial path is compared in order to invalidate all paths + * related to a renamed directory + * inode numbers are also checked, as deleting a long name may + * imply deleting a short name and conversely * - * Only use associated with a CACHE_NOHASH flag + * Only use associated with a CACHE_NOHASH flag */ -static int inode_cache_inv_compare( const struct CACHED_GENERIC *cached, - const struct CACHED_GENERIC *wanted ) +static int inode_cache_inv_compare(const struct CACHED_GENERIC *cached, + const struct CACHED_GENERIC *wanted) { - int len; - BOOL different; - const struct CACHED_INODE *w; - const struct CACHED_INODE *c; + int len; + BOOL different; + const struct CACHED_INODE *w; + const struct CACHED_INODE *c; - w = ( const struct CACHED_INODE* )wanted; - c = ( const struct CACHED_INODE* )cached; - if ( w->pathname ) - { - len = strlen( w->pathname ); - different = !cached->variable - || ( ( w->inum != MREF( c->inum ) ) - && ( strncmp( c->pathname, w->pathname, len ) - || ( ( c->pathname[len] != '\0' ) - && ( c->pathname[len] != '/' ) ) ) ); - } - else - different = !c->pathname - || ( w->inum != MREF( c->inum ) ); - return ( different ); + w = (const struct CACHED_INODE*)wanted; + c = (const struct CACHED_INODE*)cached; + if (w->pathname) { + len = strlen(w->pathname); + different = !cached->variable + || ((w->inum != MREF(c->inum)) + && (strncmp(c->pathname, w->pathname, len) + || ((c->pathname[len] != '\0') + && (c->pathname[len] != '/')))); + } else + different = !c->pathname + || (w->inum != MREF(c->inum)); + return (different); } #endif @@ -170,68 +161,67 @@ static int inode_cache_inv_compare( const struct CACHED_GENERIC *cached, #if CACHE_LOOKUP_SIZE /* - * File name comparing for entering/fetching from lookup cache + * File name comparing for entering/fetching from lookup cache */ -static int lookup_cache_compare( const struct CACHED_GENERIC *cached, - const struct CACHED_GENERIC *wanted ) +static int lookup_cache_compare(const struct CACHED_GENERIC *cached, + const struct CACHED_GENERIC *wanted) { - const struct CACHED_LOOKUP *c = ( const struct CACHED_LOOKUP* ) cached; - const struct CACHED_LOOKUP *w = ( const struct CACHED_LOOKUP* ) wanted; - return ( !c->name - || ( c->parent != w->parent ) - || ( c->namesize != w->namesize ) - || memcmp( c->name, w->name, c->namesize ) ); + const struct CACHED_LOOKUP *c = (const struct CACHED_LOOKUP*) cached; + const struct CACHED_LOOKUP *w = (const struct CACHED_LOOKUP*) wanted; + return (!c->name + || (c->parent != w->parent) + || (c->namesize != w->namesize) + || memcmp(c->name, w->name, c->namesize)); } /* - * Inode number comparing for invalidating lookup cache + * Inode number comparing for invalidating lookup cache * - * All entries with designated inode number are invalidated + * All entries with designated inode number are invalidated * - * Only use associated with a CACHE_NOHASH flag + * Only use associated with a CACHE_NOHASH flag */ -static int lookup_cache_inv_compare( const struct CACHED_GENERIC *cached, - const struct CACHED_GENERIC *wanted ) +static int lookup_cache_inv_compare(const struct CACHED_GENERIC *cached, + const struct CACHED_GENERIC *wanted) { - const struct CACHED_LOOKUP *c = ( const struct CACHED_LOOKUP* ) cached; - const struct CACHED_LOOKUP *w = ( const struct CACHED_LOOKUP* ) wanted; - return ( !c->name - || ( c->parent != w->parent ) - || ( MREF( c->inum ) != MREF( w->inum ) ) ); + const struct CACHED_LOOKUP *c = (const struct CACHED_LOOKUP*) cached; + const struct CACHED_LOOKUP *w = (const struct CACHED_LOOKUP*) wanted; + return (!c->name + || (c->parent != w->parent) + || (MREF(c->inum) != MREF(w->inum))); } /* - * Lookup hashing + * Lookup hashing * - * Based on first, second and and last char + * Based on first, second and and last char */ -int ntfs_dir_lookup_hash( const struct CACHED_GENERIC *cached ) +int ntfs_dir_lookup_hash(const struct CACHED_GENERIC *cached) { - const unsigned char *name; - int count; - unsigned int val; + const unsigned char *name; + int count; + unsigned int val; - name = ( const unsigned char* )cached->variable; - count = cached->varsize; - if ( !name || !count ) - { - ntfs_log_error( "Bad lookup cache entry\n" ); - return ( -1 ); - } - val = ( name[0] << 2 ) + ( name[1] << 1 ) + name[count - 1] + count; - return ( val % ( 2*CACHE_LOOKUP_SIZE ) ); + name = (const unsigned char*)cached->variable; + count = cached->varsize; + if (!name || !count) { + ntfs_log_error("Bad lookup cache entry\n"); + return (-1); + } + val = (name[0] << 2) + (name[1] << 1) + name[count - 1] + count; + return (val % (2*CACHE_LOOKUP_SIZE)); } #endif /** * ntfs_inode_lookup_by_name - find an inode in a directory given its name - * @dir_ni: ntfs inode of the directory in which to search for the name - * @uname: Unicode name for which to search in the directory - * @uname_len: length of the name @uname in Unicode characters + * @dir_ni: ntfs inode of the directory in which to search for the name + * @uname: Unicode name for which to search in the directory + * @uname_len: length of the name @uname in Unicode characters * * Look for an inode with name @uname in the directory with inode @dir_ni. * ntfs_inode_lookup_by_name() walks the contents of the directory looking for @@ -252,449 +242,411 @@ int ntfs_dir_lookup_hash( const struct CACHED_GENERIC *cached ) * If the volume is mounted with the case sensitive flag set, then we only * allow exact matches. */ -u64 ntfs_inode_lookup_by_name( ntfs_inode *dir_ni, - const ntfschar *uname, const int uname_len ) +u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, + const ntfschar *uname, const int uname_len) { - VCN vcn; - u64 mref = 0; - s64 br; - ntfs_volume *vol = dir_ni->vol; - ntfs_attr_search_ctx *ctx; - INDEX_ROOT *ir; - INDEX_ENTRY *ie; - INDEX_ALLOCATION *ia; - IGNORE_CASE_BOOL case_sensitivity; - u8 *index_end; - ntfs_attr *ia_na; - int eo, rc; - u32 index_block_size, index_vcn_size; - u8 index_vcn_size_bits; + VCN vcn; + u64 mref = 0; + s64 br; + ntfs_volume *vol = dir_ni->vol; + ntfs_attr_search_ctx *ctx; + INDEX_ROOT *ir; + INDEX_ENTRY *ie; + INDEX_ALLOCATION *ia; + IGNORE_CASE_BOOL case_sensitivity; + u8 *index_end; + ntfs_attr *ia_na; + int eo, rc; + u32 index_block_size, index_vcn_size; + u8 index_vcn_size_bits; - ntfs_log_trace( "Entering\n" ); + ntfs_log_trace("Entering\n"); - if ( !dir_ni || !dir_ni->mrec || !uname || uname_len <= 0 ) - { - errno = EINVAL; - return -1; - } + if (!dir_ni || !dir_ni->mrec || !uname || uname_len <= 0) { + errno = EINVAL; + return -1; + } - ctx = ntfs_attr_get_search_ctx( dir_ni, NULL ); - if ( !ctx ) - return -1; + ctx = ntfs_attr_get_search_ctx(dir_ni, NULL); + if (!ctx) + return -1; - /* Find the index root attribute in the mft record. */ - if ( ntfs_attr_lookup( AT_INDEX_ROOT, NTFS_INDEX_I30, 4, CASE_SENSITIVE, 0, NULL, - 0, ctx ) ) - { - ntfs_log_perror( "Index root attribute missing in directory inode " - "%lld", ( unsigned long long )dir_ni->mft_no ); - goto put_err_out; - } - case_sensitivity = ( NVolCaseSensitive( vol ) ? CASE_SENSITIVE : IGNORE_CASE ); - /* Get to the index root value. */ - ir = ( INDEX_ROOT* )( ( u8* )ctx->attr + - le16_to_cpu( ctx->attr->value_offset ) ); - index_block_size = le32_to_cpu( ir->index_block_size ); - if ( index_block_size < NTFS_BLOCK_SIZE || - index_block_size & ( index_block_size - 1 ) ) - { - ntfs_log_error( "Index block size %u is invalid.\n", - ( unsigned )index_block_size ); - goto put_err_out; - } - index_end = ( u8* ) & ir->index + le32_to_cpu( ir->index.index_length ); - /* The first index entry. */ - ie = ( INDEX_ENTRY* )( ( u8* ) & ir->index + - le32_to_cpu( ir->index.entries_offset ) ); - /* - * Loop until we exceed valid memory (corruption case) or until we - * reach the last entry. - */ - for ( ;; ie = ( INDEX_ENTRY* )( ( u8* )ie + le16_to_cpu( ie->length ) ) ) - { - /* Bounds checks. */ - if ( ( u8* )ie < ( u8* )ctx->mrec || ( u8* )ie + - sizeof( INDEX_ENTRY_HEADER ) > index_end || - ( u8* )ie + le16_to_cpu( ie->key_length ) > - index_end ) - { - ntfs_log_error( "Index entry out of bounds in inode %lld" - "\n", ( unsigned long long )dir_ni->mft_no ); - goto put_err_out; - } - /* - * The last entry cannot contain a name. It can however contain - * a pointer to a child node in the B+tree so we just break out. - */ - if ( ie->ie_flags & INDEX_ENTRY_END ) - break; + /* Find the index root attribute in the mft record. */ + if (ntfs_attr_lookup(AT_INDEX_ROOT, NTFS_INDEX_I30, 4, CASE_SENSITIVE, 0, NULL, + 0, ctx)) { + ntfs_log_perror("Index root attribute missing in directory inode " + "%lld", (unsigned long long)dir_ni->mft_no); + goto put_err_out; + } + case_sensitivity = (NVolCaseSensitive(vol) ? CASE_SENSITIVE : IGNORE_CASE); + /* Get to the index root value. */ + ir = (INDEX_ROOT*)((u8*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + index_block_size = le32_to_cpu(ir->index_block_size); + if (index_block_size < NTFS_BLOCK_SIZE || + index_block_size & (index_block_size - 1)) { + ntfs_log_error("Index block size %u is invalid.\n", + (unsigned)index_block_size); + goto put_err_out; + } + index_end = (u8*)&ir->index + le32_to_cpu(ir->index.index_length); + /* The first index entry. */ + ie = (INDEX_ENTRY*)((u8*)&ir->index + + le32_to_cpu(ir->index.entries_offset)); + /* + * Loop until we exceed valid memory (corruption case) or until we + * reach the last entry. + */ + for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) { + /* Bounds checks. */ + if ((u8*)ie < (u8*)ctx->mrec || (u8*)ie + + sizeof(INDEX_ENTRY_HEADER) > index_end || + (u8*)ie + le16_to_cpu(ie->key_length) > + index_end) { + ntfs_log_error("Index entry out of bounds in inode %lld" + "\n", (unsigned long long)dir_ni->mft_no); + goto put_err_out; + } + /* + * The last entry cannot contain a name. It can however contain + * a pointer to a child node in the B+tree so we just break out. + */ + if (ie->ie_flags & INDEX_ENTRY_END) + break; + + if (!le16_to_cpu(ie->length)) { + ntfs_log_error("Zero length index entry in inode %lld" + "\n", (unsigned long long)dir_ni->mft_no); + goto put_err_out; + } + /* + * Not a perfect match, need to do full blown collation so we + * know which way in the B+tree we have to go. + */ + rc = ntfs_names_full_collate(uname, uname_len, + (ntfschar*)&ie->key.file_name.file_name, + ie->key.file_name.file_name_length, + case_sensitivity, vol->upcase, vol->upcase_len); + /* + * If uname collates before the name of the current entry, there + * is definitely no such name in this index but we might need to + * descend into the B+tree so we just break out of the loop. + */ + if (rc == -1) + break; + /* The names are not equal, continue the search. */ + if (rc) + continue; + /* + * Perfect match, this will never happen as the + * ntfs_are_names_equal() call will have gotten a match but we + * still treat it correctly. + */ + mref = le64_to_cpu(ie->indexed_file); + ntfs_attr_put_search_ctx(ctx); + return mref; + } + /* + * We have finished with this index without success. Check for the + * presence of a child node and if not present return error code + * ENOENT, unless we have got the mft reference of a matching name + * cached in mref in which case return mref. + */ + if (!(ie->ie_flags & INDEX_ENTRY_NODE)) { + ntfs_attr_put_search_ctx(ctx); + if (mref) + return mref; + ntfs_log_debug("Entry not found.\n"); + errno = ENOENT; + return -1; + } /* Child node present, descend into it. */ - if ( !le16_to_cpu( ie->length ) ) - { - ntfs_log_error( "Zero length index entry in inode %lld" - "\n", ( unsigned long long )dir_ni->mft_no ); - goto put_err_out; - } - /* - * Not a perfect match, need to do full blown collation so we - * know which way in the B+tree we have to go. - */ - rc = ntfs_names_full_collate( uname, uname_len, - ( ntfschar* ) & ie->key.file_name.file_name, - ie->key.file_name.file_name_length, - case_sensitivity, vol->upcase, vol->upcase_len ); - /* - * If uname collates before the name of the current entry, there - * is definitely no such name in this index but we might need to - * descend into the B+tree so we just break out of the loop. - */ - if ( rc == -1 ) - break; - /* The names are not equal, continue the search. */ - if ( rc ) - continue; - /* - * Perfect match, this will never happen as the - * ntfs_are_names_equal() call will have gotten a match but we - * still treat it correctly. - */ - mref = le64_to_cpu( ie->indexed_file ); - ntfs_attr_put_search_ctx( ctx ); - return mref; - } - /* - * We have finished with this index without success. Check for the - * presence of a child node and if not present return error code - * ENOENT, unless we have got the mft reference of a matching name - * cached in mref in which case return mref. - */ - if ( !( ie->ie_flags & INDEX_ENTRY_NODE ) ) - { - ntfs_attr_put_search_ctx( ctx ); - if ( mref ) - return mref; - ntfs_log_debug( "Entry not found.\n" ); - errno = ENOENT; - return -1; - } /* Child node present, descend into it. */ + /* Open the index allocation attribute. */ + ia_na = ntfs_attr_open(dir_ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4); + if (!ia_na) { + ntfs_log_perror("Failed to open index allocation (inode %lld)", + (unsigned long long)dir_ni->mft_no); + goto put_err_out; + } - /* Open the index allocation attribute. */ - ia_na = ntfs_attr_open( dir_ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4 ); - if ( !ia_na ) - { - ntfs_log_perror( "Failed to open index allocation (inode %lld)", - ( unsigned long long )dir_ni->mft_no ); - goto put_err_out; - } + /* Allocate a buffer for the current index block. */ + ia = ntfs_malloc(index_block_size); + if (!ia) { + ntfs_attr_close(ia_na); + goto put_err_out; + } - /* Allocate a buffer for the current index block. */ - ia = ntfs_malloc( index_block_size ); - if ( !ia ) - { - ntfs_attr_close( ia_na ); - goto put_err_out; - } + /* Determine the size of a vcn in the directory index. */ + if (vol->cluster_size <= index_block_size) { + index_vcn_size = vol->cluster_size; + index_vcn_size_bits = vol->cluster_size_bits; + } else { + index_vcn_size = vol->sector_size; + index_vcn_size_bits = vol->sector_size_bits; + } - /* Determine the size of a vcn in the directory index. */ - if ( vol->cluster_size <= index_block_size ) - { - index_vcn_size = vol->cluster_size; - index_vcn_size_bits = vol->cluster_size_bits; - } - else - { - index_vcn_size = vol->sector_size; - index_vcn_size_bits = vol->sector_size_bits; - } - - /* Get the starting vcn of the index_block holding the child node. */ - vcn = sle64_to_cpup( ( u8* )ie + le16_to_cpu( ie->length ) - 8 ); + /* Get the starting vcn of the index_block holding the child node. */ + vcn = sle64_to_cpup((u8*)ie + le16_to_cpu(ie->length) - 8); descend_into_child_node: - /* Read the index block starting at vcn. */ - br = ntfs_attr_mst_pread( ia_na, vcn << index_vcn_size_bits, 1, - index_block_size, ia ); - if ( br != 1 ) - { - if ( br != -1 ) - errno = EIO; - ntfs_log_perror( "Failed to read vcn 0x%llx", - ( unsigned long long )vcn ); - goto close_err_out; - } + /* Read the index block starting at vcn. */ + br = ntfs_attr_mst_pread(ia_na, vcn << index_vcn_size_bits, 1, + index_block_size, ia); + if (br != 1) { + if (br != -1) + errno = EIO; + ntfs_log_perror("Failed to read vcn 0x%llx", + (unsigned long long)vcn); + goto close_err_out; + } - if ( sle64_to_cpu( ia->index_block_vcn ) != vcn ) - { - ntfs_log_error( "Actual VCN (0x%llx) of index buffer is different " - "from expected VCN (0x%llx).\n", - ( long long )sle64_to_cpu( ia->index_block_vcn ), - ( long long )vcn ); - errno = EIO; - goto close_err_out; - } - if ( le32_to_cpu( ia->index.allocated_size ) + 0x18 != index_block_size ) - { - ntfs_log_error( "Index buffer (VCN 0x%llx) of directory inode 0x%llx " - "has a size (%u) differing from the directory " - "specified size (%u).\n", ( long long )vcn, - ( unsigned long long )dir_ni->mft_no, - ( unsigned ) le32_to_cpu( ia->index.allocated_size ) + 0x18, - ( unsigned )index_block_size ); - errno = EIO; - goto close_err_out; - } - index_end = ( u8* ) & ia->index + le32_to_cpu( ia->index.index_length ); - if ( index_end > ( u8* )ia + index_block_size ) - { - ntfs_log_error( "Size of index buffer (VCN 0x%llx) of directory inode " - "0x%llx exceeds maximum size.\n", - ( long long )vcn, ( unsigned long long )dir_ni->mft_no ); - errno = EIO; - goto close_err_out; - } + if (sle64_to_cpu(ia->index_block_vcn) != vcn) { + ntfs_log_error("Actual VCN (0x%llx) of index buffer is different " + "from expected VCN (0x%llx).\n", + (long long)sle64_to_cpu(ia->index_block_vcn), + (long long)vcn); + errno = EIO; + goto close_err_out; + } + if (le32_to_cpu(ia->index.allocated_size) + 0x18 != index_block_size) { + ntfs_log_error("Index buffer (VCN 0x%llx) of directory inode 0x%llx " + "has a size (%u) differing from the directory " + "specified size (%u).\n", (long long)vcn, + (unsigned long long)dir_ni->mft_no, + (unsigned) le32_to_cpu(ia->index.allocated_size) + 0x18, + (unsigned)index_block_size); + errno = EIO; + goto close_err_out; + } + index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length); + if (index_end > (u8*)ia + index_block_size) { + ntfs_log_error("Size of index buffer (VCN 0x%llx) of directory inode " + "0x%llx exceeds maximum size.\n", + (long long)vcn, (unsigned long long)dir_ni->mft_no); + errno = EIO; + goto close_err_out; + } - /* The first index entry. */ - ie = ( INDEX_ENTRY* )( ( u8* ) & ia->index + - le32_to_cpu( ia->index.entries_offset ) ); - /* - * Iterate similar to above big loop but applied to index buffer, thus - * loop until we exceed valid memory (corruption case) or until we - * reach the last entry. - */ - for ( ;; ie = ( INDEX_ENTRY* )( ( u8* )ie + le16_to_cpu( ie->length ) ) ) - { - /* Bounds check. */ - if ( ( u8* )ie < ( u8* )ia || ( u8* )ie + - sizeof( INDEX_ENTRY_HEADER ) > index_end || - ( u8* )ie + le16_to_cpu( ie->key_length ) > - index_end ) - { - ntfs_log_error( "Index entry out of bounds in directory " - "inode %lld.\n", - ( unsigned long long )dir_ni->mft_no ); - errno = EIO; - goto close_err_out; - } - /* - * The last entry cannot contain a name. It can however contain - * a pointer to a child node in the B+tree so we just break out. - */ - if ( ie->ie_flags & INDEX_ENTRY_END ) - break; - - if ( !le16_to_cpu( ie->length ) ) - { - errno = EIO; - ntfs_log_error( "Zero length index entry in inode %lld" - "\n", ( unsigned long long )dir_ni->mft_no ); - goto close_err_out; - } - /* - * Not a perfect match, need to do full blown collation so we - * know which way in the B+tree we have to go. - */ - rc = ntfs_names_full_collate( uname, uname_len, - ( ntfschar* ) & ie->key.file_name.file_name, - ie->key.file_name.file_name_length, - case_sensitivity, vol->upcase, vol->upcase_len ); - /* - * If uname collates before the name of the current entry, there - * is definitely no such name in this index but we might need to - * descend into the B+tree so we just break out of the loop. - */ - if ( rc == -1 ) - break; - /* The names are not equal, continue the search. */ - if ( rc ) - continue; - mref = le64_to_cpu( ie->indexed_file ); - free( ia ); - ntfs_attr_close( ia_na ); - ntfs_attr_put_search_ctx( ctx ); - return mref; - } - /* - * We have finished with this index buffer without success. Check for - * the presence of a child node. - */ - if ( ie->ie_flags & INDEX_ENTRY_NODE ) - { - if ( ( ia->index.ih_flags & NODE_MASK ) == LEAF_NODE ) - { - ntfs_log_error( "Index entry with child node found in a leaf " - "node in directory inode %lld.\n", - ( unsigned long long )dir_ni->mft_no ); - errno = EIO; - goto close_err_out; - } - /* Child node present, descend into it. */ - vcn = sle64_to_cpup( ( u8* )ie + le16_to_cpu( ie->length ) - 8 ); - if ( vcn >= 0 ) - goto descend_into_child_node; - ntfs_log_error( "Negative child node vcn in directory inode " - "0x%llx.\n", ( unsigned long long )dir_ni->mft_no ); - errno = EIO; - goto close_err_out; - } - free( ia ); - ntfs_attr_close( ia_na ); - ntfs_attr_put_search_ctx( ctx ); - /* - * No child node present, return error code ENOENT, unless we have got - * the mft reference of a matching name cached in mref in which case - * return mref. - */ - if ( mref ) - return mref; - ntfs_log_debug( "Entry not found.\n" ); - errno = ENOENT; - return -1; + /* The first index entry. */ + ie = (INDEX_ENTRY*)((u8*)&ia->index + + le32_to_cpu(ia->index.entries_offset)); + /* + * Iterate similar to above big loop but applied to index buffer, thus + * loop until we exceed valid memory (corruption case) or until we + * reach the last entry. + */ + for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) { + /* Bounds check. */ + if ((u8*)ie < (u8*)ia || (u8*)ie + + sizeof(INDEX_ENTRY_HEADER) > index_end || + (u8*)ie + le16_to_cpu(ie->key_length) > + index_end) { + ntfs_log_error("Index entry out of bounds in directory " + "inode %lld.\n", + (unsigned long long)dir_ni->mft_no); + errno = EIO; + goto close_err_out; + } + /* + * The last entry cannot contain a name. It can however contain + * a pointer to a child node in the B+tree so we just break out. + */ + if (ie->ie_flags & INDEX_ENTRY_END) + break; + + if (!le16_to_cpu(ie->length)) { + errno = EIO; + ntfs_log_error("Zero length index entry in inode %lld" + "\n", (unsigned long long)dir_ni->mft_no); + goto close_err_out; + } + /* + * Not a perfect match, need to do full blown collation so we + * know which way in the B+tree we have to go. + */ + rc = ntfs_names_full_collate(uname, uname_len, + (ntfschar*)&ie->key.file_name.file_name, + ie->key.file_name.file_name_length, + case_sensitivity, vol->upcase, vol->upcase_len); + /* + * If uname collates before the name of the current entry, there + * is definitely no such name in this index but we might need to + * descend into the B+tree so we just break out of the loop. + */ + if (rc == -1) + break; + /* The names are not equal, continue the search. */ + if (rc) + continue; + mref = le64_to_cpu(ie->indexed_file); + free(ia); + ntfs_attr_close(ia_na); + ntfs_attr_put_search_ctx(ctx); + return mref; + } + /* + * We have finished with this index buffer without success. Check for + * the presence of a child node. + */ + if (ie->ie_flags & INDEX_ENTRY_NODE) { + if ((ia->index.ih_flags & NODE_MASK) == LEAF_NODE) { + ntfs_log_error("Index entry with child node found in a leaf " + "node in directory inode %lld.\n", + (unsigned long long)dir_ni->mft_no); + errno = EIO; + goto close_err_out; + } + /* Child node present, descend into it. */ + vcn = sle64_to_cpup((u8*)ie + le16_to_cpu(ie->length) - 8); + if (vcn >= 0) + goto descend_into_child_node; + ntfs_log_error("Negative child node vcn in directory inode " + "0x%llx.\n", (unsigned long long)dir_ni->mft_no); + errno = EIO; + goto close_err_out; + } + free(ia); + ntfs_attr_close(ia_na); + ntfs_attr_put_search_ctx(ctx); + /* + * No child node present, return error code ENOENT, unless we have got + * the mft reference of a matching name cached in mref in which case + * return mref. + */ + if (mref) + return mref; + ntfs_log_debug("Entry not found.\n"); + errno = ENOENT; + return -1; put_err_out: - eo = EIO; - ntfs_log_debug( "Corrupt directory. Aborting lookup.\n" ); + eo = EIO; + ntfs_log_debug("Corrupt directory. Aborting lookup.\n"); eo_put_err_out: - ntfs_attr_put_search_ctx( ctx ); - errno = eo; - return -1; + ntfs_attr_put_search_ctx(ctx); + errno = eo; + return -1; close_err_out: - eo = errno; - free( ia ); - ntfs_attr_close( ia_na ); - goto eo_put_err_out; + eo = errno; + free(ia); + ntfs_attr_close(ia_na); + goto eo_put_err_out; } /* - * Lookup a file in a directory from its UTF-8 name + * Lookup a file in a directory from its UTF-8 name * - * The name is first fetched from cache if one is defined + * The name is first fetched from cache if one is defined * - * Returns the inode number - * or -1 if not possible (errno tells why) + * Returns the inode number + * or -1 if not possible (errno tells why) */ -u64 ntfs_inode_lookup_by_mbsname( ntfs_inode *dir_ni, const char *name ) +u64 ntfs_inode_lookup_by_mbsname(ntfs_inode *dir_ni, const char *name) { - int uname_len; - ntfschar *uname = ( ntfschar* )NULL; - u64 inum; - char *cached_name; - const char *const_name; + int uname_len; + ntfschar *uname = (ntfschar*)NULL; + u64 inum; + char *cached_name; + const char *const_name; - if ( !NVolCaseSensitive( dir_ni->vol ) ) - { - cached_name = ntfs_uppercase_mbs( name, - dir_ni->vol->upcase, dir_ni->vol->upcase_len ); - const_name = cached_name; - } - else - { - cached_name = ( char* )NULL; - const_name = name; - } - if ( const_name ) - { + if (!NVolCaseSensitive(dir_ni->vol)) { + cached_name = ntfs_uppercase_mbs(name, + dir_ni->vol->upcase, dir_ni->vol->upcase_len); + const_name = cached_name; + } else { + cached_name = (char*)NULL; + const_name = name; + } + if (const_name) { #if CACHE_LOOKUP_SIZE - /* - * fetch inode from cache - */ + /* + * fetch inode from cache + */ - if ( dir_ni->vol->lookup_cache ) - { - struct CACHED_LOOKUP item; - struct CACHED_LOOKUP *cached; + if (dir_ni->vol->lookup_cache) { + struct CACHED_LOOKUP item; + struct CACHED_LOOKUP *cached; - item.name = const_name; - item.namesize = strlen( const_name ) + 1; - item.parent = dir_ni->mft_no; - cached = ( struct CACHED_LOOKUP* )ntfs_fetch_cache( - dir_ni->vol->lookup_cache, - GENERIC( &item ), lookup_cache_compare ); - if ( cached ) - { - inum = cached->inum; - if ( inum == ( u64 ) - 1 ) - errno = ENOENT; - } - else - { - /* Generate unicode name. */ - uname_len = ntfs_mbstoucs( name, &uname ); - if ( uname_len >= 0 ) - { - inum = ntfs_inode_lookup_by_name( dir_ni, - uname, uname_len ); - item.inum = inum; - /* enter into cache, even if not found */ - ntfs_enter_cache( dir_ni->vol->lookup_cache, - GENERIC( &item ), - lookup_cache_compare ); - free( uname ); - } - else - inum = ( s64 ) - 1; - } - } - else + item.name = const_name; + item.namesize = strlen(const_name) + 1; + item.parent = dir_ni->mft_no; + cached = (struct CACHED_LOOKUP*)ntfs_fetch_cache( + dir_ni->vol->lookup_cache, + GENERIC(&item), lookup_cache_compare); + if (cached) { + inum = cached->inum; + if (inum == (u64)-1) + errno = ENOENT; + } else { + /* Generate unicode name. */ + uname_len = ntfs_mbstoucs(name, &uname); + if (uname_len >= 0) { + inum = ntfs_inode_lookup_by_name(dir_ni, + uname, uname_len); + item.inum = inum; + /* enter into cache, even if not found */ + ntfs_enter_cache(dir_ni->vol->lookup_cache, + GENERIC(&item), + lookup_cache_compare); + free(uname); + } else + inum = (s64)-1; + } + } else #endif - { - /* Generate unicode name. */ - uname_len = ntfs_mbstoucs( cached_name, &uname ); - if ( uname_len >= 0 ) - inum = ntfs_inode_lookup_by_name( dir_ni, - uname, uname_len ); - else - inum = ( s64 ) - 1; - } - if ( cached_name ) - free( cached_name ); - } - else - inum = ( s64 ) - 1; - return ( inum ); + { + /* Generate unicode name. */ + uname_len = ntfs_mbstoucs(cached_name, &uname); + if (uname_len >= 0) + inum = ntfs_inode_lookup_by_name(dir_ni, + uname, uname_len); + else + inum = (s64)-1; + } + if (cached_name) + free(cached_name); + } else + inum = (s64)-1; + return (inum); } /* - * Update a cache lookup record when a name has been defined + * Update a cache lookup record when a name has been defined * - * The UTF-8 name is required + * The UTF-8 name is required */ -void ntfs_inode_update_mbsname( ntfs_inode *dir_ni, const char *name, u64 inum ) +void ntfs_inode_update_mbsname(ntfs_inode *dir_ni, const char *name, u64 inum) { #if CACHE_LOOKUP_SIZE - struct CACHED_LOOKUP item; - struct CACHED_LOOKUP *cached; - char *cached_name; + struct CACHED_LOOKUP item; + struct CACHED_LOOKUP *cached; + char *cached_name; - if ( dir_ni->vol->lookup_cache ) - { - if ( !NVolCaseSensitive( dir_ni->vol ) ) - { - cached_name = ntfs_uppercase_mbs( name, - dir_ni->vol->upcase, dir_ni->vol->upcase_len ); - item.name = cached_name; - } - else - { - cached_name = ( char* )NULL; - item.name = name; - } - if ( item.name ) - { - item.namesize = strlen( item.name ) + 1; - item.parent = dir_ni->mft_no; - item.inum = inum; - cached = ( struct CACHED_LOOKUP* )ntfs_enter_cache( - dir_ni->vol->lookup_cache, - GENERIC( &item ), lookup_cache_compare ); - if ( cached ) - cached->inum = inum; - if ( cached_name ) - free( cached_name ); - } - } + if (dir_ni->vol->lookup_cache) { + if (!NVolCaseSensitive(dir_ni->vol)) { + cached_name = ntfs_uppercase_mbs(name, + dir_ni->vol->upcase, dir_ni->vol->upcase_len); + item.name = cached_name; + } else { + cached_name = (char*)NULL; + item.name = name; + } + if (item.name) { + item.namesize = strlen(item.name) + 1; + item.parent = dir_ni->mft_no; + item.inum = inum; + cached = (struct CACHED_LOOKUP*)ntfs_enter_cache( + dir_ni->vol->lookup_cache, + GENERIC(&item), lookup_cache_compare); + if (cached) + cached->inum = inum; + if (cached_name) + free(cached_name); + } + } #endif } @@ -709,329 +661,294 @@ void ntfs_inode_update_mbsname( ntfs_inode *dir_ni, const char *name, u64 inum ) * then the root directory '.' will be used as the base for the search. * * Return: inode Success, the pathname was valid - * NULL Error, the pathname was invalid, or some other error occurred + * NULL Error, the pathname was invalid, or some other error occurred */ -ntfs_inode *ntfs_pathname_to_inode( ntfs_volume *vol, ntfs_inode *parent, - const char *pathname ) +ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent, + const char *pathname) { - u64 inum; - int len, err = 0; - char *p, *q; - ntfs_inode *ni; - ntfs_inode *result = NULL; - ntfschar *unicode = NULL; - char *ascii = NULL; + u64 inum; + int len, err = 0; + char *p, *q; + ntfs_inode *ni; + ntfs_inode *result = NULL; + ntfschar *unicode = NULL; + char *ascii = NULL; #if CACHE_INODE_SIZE - struct CACHED_INODE item; - struct CACHED_INODE *cached; - char *fullname; + struct CACHED_INODE item; + struct CACHED_INODE *cached; + char *fullname; #endif - if ( !vol || !pathname ) - { - errno = EINVAL; - return NULL; - } + if (!vol || !pathname) { + errno = EINVAL; + return NULL; + } + + ntfs_log_trace("path: '%s'\n", pathname); + + ascii = strdup(pathname); + if (!ascii) { + ntfs_log_error("Out of memory.\n"); + err = ENOMEM; + goto out; + } - ntfs_log_trace( "path: '%s'\n", pathname ); - - ascii = strdup( pathname ); - if ( !ascii ) - { - ntfs_log_error( "Out of memory.\n" ); - err = ENOMEM; - goto out; - } - - p = ascii; - /* Remove leading /'s. */ - while ( p && *p && *p == PATH_SEP ) - p++; + p = ascii; + /* Remove leading /'s. */ + while (p && *p && *p == PATH_SEP) + p++; #if CACHE_INODE_SIZE - fullname = p; - if ( p[0] && ( p[strlen( p )-1] == PATH_SEP ) ) - ntfs_log_error( "Unnormalized path %s\n", ascii ); + fullname = p; + if (p[0] && (p[strlen(p)-1] == PATH_SEP)) + ntfs_log_error("Unnormalized path %s\n",ascii); #endif - if ( parent ) - { - ni = parent; - } - else - { + if (parent) { + ni = parent; + } else { #if CACHE_INODE_SIZE - /* - * fetch inode for full path from cache - */ - if ( *fullname ) - { - item.pathname = fullname; - item.varsize = strlen( fullname ) + 1; - cached = ( struct CACHED_INODE* )ntfs_fetch_cache( - vol->xinode_cache, GENERIC( &item ), - inode_cache_compare ); - } - else - cached = ( struct CACHED_INODE* )NULL; - if ( cached ) - { - /* - * return opened inode if found in cache - */ - inum = MREF( cached->inum ); - ni = ntfs_inode_open( vol, inum ); - if ( !ni ) - { - ntfs_log_debug( "Cannot open inode %llu: %s.\n", - ( unsigned long long )inum, p ); - err = EIO; - } - result = ni; - goto out; - } + /* + * fetch inode for full path from cache + */ + if (*fullname) { + item.pathname = fullname; + item.varsize = strlen(fullname) + 1; + cached = (struct CACHED_INODE*)ntfs_fetch_cache( + vol->xinode_cache, GENERIC(&item), + inode_cache_compare); + } else + cached = (struct CACHED_INODE*)NULL; + if (cached) { + /* + * return opened inode if found in cache + */ + inum = MREF(cached->inum); + ni = ntfs_inode_open(vol, inum); + if (!ni) { + ntfs_log_debug("Cannot open inode %llu: %s.\n", + (unsigned long long)inum, p); + err = EIO; + } + result = ni; + goto out; + } #endif - ni = ntfs_inode_open( vol, FILE_root ); - if ( !ni ) - { - ntfs_log_debug( "Couldn't open the inode of the root " - "directory.\n" ); - err = EIO; - result = ( ntfs_inode* )NULL; - goto out; - } - } + ni = ntfs_inode_open(vol, FILE_root); + if (!ni) { + ntfs_log_debug("Couldn't open the inode of the root " + "directory.\n"); + err = EIO; + result = (ntfs_inode*)NULL; + goto out; + } + } - while ( p && *p ) - { - /* Find the end of the first token. */ - q = strchr( p, PATH_SEP ); - if ( q != NULL ) - { - *q = '\0'; - } + while (p && *p) { + /* Find the end of the first token. */ + q = strchr(p, PATH_SEP); + if (q != NULL) { + *q = '\0'; + } #if CACHE_INODE_SIZE - /* - * fetch inode for partial path from cache - */ - cached = ( struct CACHED_INODE* )NULL; - if ( !parent ) - { - item.pathname = fullname; - item.varsize = strlen( fullname ) + 1; - cached = ( struct CACHED_INODE* )ntfs_fetch_cache( - vol->xinode_cache, GENERIC( &item ), - inode_cache_compare ); - if ( cached ) - { - inum = cached->inum; - } - } - /* - * if not in cache, translate, search, then - * insert into cache if found - */ - if ( !cached ) - { - len = ntfs_mbstoucs( p, &unicode ); - if ( len < 0 ) - { - ntfs_log_perror( "Could not convert filename to Unicode:" - " '%s'", p ); - err = errno; - goto close; - } - else if ( len > NTFS_MAX_NAME_LEN ) - { - err = ENAMETOOLONG; - goto close; - } - inum = ntfs_inode_lookup_by_name( ni, unicode, len ); - if ( !parent && ( inum != ( u64 ) - 1 ) ) - { - item.inum = inum; - ntfs_enter_cache( vol->xinode_cache, - GENERIC( &item ), - inode_cache_compare ); - } - } + /* + * fetch inode for partial path from cache + */ + cached = (struct CACHED_INODE*)NULL; + if (!parent) { + item.pathname = fullname; + item.varsize = strlen(fullname) + 1; + cached = (struct CACHED_INODE*)ntfs_fetch_cache( + vol->xinode_cache, GENERIC(&item), + inode_cache_compare); + if (cached) { + inum = cached->inum; + } + } + /* + * if not in cache, translate, search, then + * insert into cache if found + */ + if (!cached) { + len = ntfs_mbstoucs(p, &unicode); + if (len < 0) { + ntfs_log_perror("Could not convert filename to Unicode:" + " '%s'", p); + err = errno; + goto close; + } else if (len > NTFS_MAX_NAME_LEN) { + err = ENAMETOOLONG; + goto close; + } + inum = ntfs_inode_lookup_by_name(ni, unicode, len); + if (!parent && (inum != (u64) -1)) { + item.inum = inum; + ntfs_enter_cache(vol->xinode_cache, + GENERIC(&item), + inode_cache_compare); + } + } #else - len = ntfs_mbstoucs( p, &unicode ); - if ( len < 0 ) - { - ntfs_log_perror( "Could not convert filename to Unicode:" - " '%s'", p ); - err = errno; - goto close; - } - else if ( len > NTFS_MAX_NAME_LEN ) - { - err = ENAMETOOLONG; - goto close; - } - inum = ntfs_inode_lookup_by_name( ni, unicode, len ); + len = ntfs_mbstoucs(p, &unicode); + if (len < 0) { + ntfs_log_perror("Could not convert filename to Unicode:" + " '%s'", p); + err = errno; + goto close; + } else if (len > NTFS_MAX_NAME_LEN) { + err = ENAMETOOLONG; + goto close; + } + inum = ntfs_inode_lookup_by_name(ni, unicode, len); #endif - if ( inum == ( u64 ) - 1 ) - { - ntfs_log_debug( "Couldn't find name '%s' in pathname " - "'%s'.\n", p, pathname ); - err = ENOENT; - goto close; - } + if (inum == (u64) -1) { + ntfs_log_debug("Couldn't find name '%s' in pathname " + "'%s'.\n", p, pathname); + err = ENOENT; + goto close; + } - if ( ni != parent ) - if ( ntfs_inode_close( ni ) ) - { - err = errno; - goto out; - } + if (ni != parent) + if (ntfs_inode_close(ni)) { + err = errno; + goto out; + } - inum = MREF( inum ); - ni = ntfs_inode_open( vol, inum ); - if ( !ni ) - { - ntfs_log_debug( "Cannot open inode %llu: %s.\n", - ( unsigned long long )inum, p ); - err = EIO; - goto close; - } + inum = MREF(inum); + ni = ntfs_inode_open(vol, inum); + if (!ni) { + ntfs_log_debug("Cannot open inode %llu: %s.\n", + (unsigned long long)inum, p); + err = EIO; + goto close; + } + + free(unicode); + unicode = NULL; - free( unicode ); - unicode = NULL; + if (q) *q++ = PATH_SEP; /* JPA */ + p = q; + while (p && *p && *p == PATH_SEP) + p++; + } - if ( q ) *q++ = PATH_SEP; /* JPA */ - p = q; - while ( p && *p && *p == PATH_SEP ) - p++; - } - - result = ni; - ni = NULL; + result = ni; + ni = NULL; close: - if ( ni && ( ni != parent ) ) - if ( ntfs_inode_close( ni ) && !err ) - err = errno; + if (ni && (ni != parent)) + if (ntfs_inode_close(ni) && !err) + err = errno; out: - free( ascii ); - free( unicode ); - if ( err ) - errno = err; - return result; + free(ascii); + free(unicode); + if (err) + errno = err; + return result; } /* * The little endian Unicode string ".." for ntfs_readdir(). */ -static const ntfschar dotdot[3] = { const_cpu_to_le16( '.' ), - const_cpu_to_le16( '.' ), - const_cpu_to_le16( '\0' ) - }; +static const ntfschar dotdot[3] = { const_cpu_to_le16('.'), + const_cpu_to_le16('.'), + const_cpu_to_le16('\0') }; /* * union index_union - * More helpers for ntfs_readdir(). */ -typedef union -{ - INDEX_ROOT *ir; - INDEX_ALLOCATION *ia; -} index_union __attribute__( ( __transparent_union__ ) ); +typedef union { + INDEX_ROOT *ir; + INDEX_ALLOCATION *ia; +} index_union __attribute__((__transparent_union__)); /** * enum INDEX_TYPE - * More helpers for ntfs_readdir(). */ -typedef enum -{ - INDEX_TYPE_ROOT, /* index root */ - INDEX_TYPE_ALLOCATION, /* index allocation */ +typedef enum { + INDEX_TYPE_ROOT, /* index root */ + INDEX_TYPE_ALLOCATION, /* index allocation */ } INDEX_TYPE; /** * ntfs_filldir - ntfs specific filldir method - * @dir_ni: ntfs inode of current directory - * @pos: current position in directory - * @ivcn_bits: log(2) of index vcn size - * @index_type: specifies whether @iu is an index root or an index allocation - * @iu: index root or index block to which @ie belongs - * @ie: current index entry - * @dirent: context for filldir callback supplied by the caller - * @filldir: filldir callback supplied by the caller + * @dir_ni: ntfs inode of current directory + * @pos: current position in directory + * @ivcn_bits: log(2) of index vcn size + * @index_type: specifies whether @iu is an index root or an index allocation + * @iu: index root or index block to which @ie belongs + * @ie: current index entry + * @dirent: context for filldir callback supplied by the caller + * @filldir: filldir callback supplied by the caller * * Pass information specifying the current directory entry @ie to the @filldir * callback. */ -static int ntfs_filldir( ntfs_inode *dir_ni, s64 *pos, u8 ivcn_bits, - const INDEX_TYPE index_type, index_union iu, INDEX_ENTRY *ie, - void *dirent, ntfs_filldir_t filldir ) +static int ntfs_filldir(ntfs_inode *dir_ni, s64 *pos, u8 ivcn_bits, + const INDEX_TYPE index_type, index_union iu, INDEX_ENTRY *ie, + void *dirent, ntfs_filldir_t filldir) { - FILE_NAME_ATTR *fn = &ie->key.file_name; - unsigned dt_type; - BOOL metadata; - ntfschar *loname; - int res; - MFT_REF mref; + FILE_NAME_ATTR *fn = &ie->key.file_name; + unsigned dt_type; + BOOL metadata; + ntfschar *loname; + int res; + MFT_REF mref; - ntfs_log_trace( "Entering.\n" ); + ntfs_log_trace("Entering.\n"); + + /* Advance the position even if going to skip the entry. */ + if (index_type == INDEX_TYPE_ALLOCATION) + *pos = (u8*)ie - (u8*)iu.ia + (sle64_to_cpu( + iu.ia->index_block_vcn) << ivcn_bits) + + dir_ni->vol->mft_record_size; + else /* if (index_type == INDEX_TYPE_ROOT) */ + *pos = (u8*)ie - (u8*)iu.ir; + /* Skip root directory self reference entry. */ + if (MREF_LE(ie->indexed_file) == FILE_root) + return 0; + if (ie->key.file_name.file_attributes & FILE_ATTR_I30_INDEX_PRESENT) + dt_type = NTFS_DT_DIR; + else if (fn->file_attributes & FILE_ATTR_SYSTEM) + dt_type = NTFS_DT_UNKNOWN; + else + dt_type = NTFS_DT_REG; - /* Advance the position even if going to skip the entry. */ - if ( index_type == INDEX_TYPE_ALLOCATION ) - *pos = ( u8* )ie - ( u8* )iu.ia + ( sle64_to_cpu( - iu.ia->index_block_vcn ) << ivcn_bits ) + - dir_ni->vol->mft_record_size; - else /* if (index_type == INDEX_TYPE_ROOT) */ - *pos = ( u8* )ie - ( u8* )iu.ir; - /* Skip root directory self reference entry. */ - if ( MREF_LE( ie->indexed_file ) == FILE_root ) - return 0; - if ( ie->key.file_name.file_attributes & FILE_ATTR_I30_INDEX_PRESENT ) - dt_type = NTFS_DT_DIR; - else if ( fn->file_attributes & FILE_ATTR_SYSTEM ) - dt_type = NTFS_DT_UNKNOWN; - else - dt_type = NTFS_DT_REG; - - /* return metadata files and hidden files if requested */ - mref = le64_to_cpu( ie->indexed_file ); - metadata = ( MREF( mref ) != FILE_root ) && ( MREF( mref ) < FILE_first_user ); - if ( ( !metadata && ( NVolShowHidFiles( dir_ni->vol ) - || !( fn->file_attributes & FILE_ATTR_HIDDEN ) ) ) - || ( NVolShowSysFiles( dir_ni->vol ) && ( NVolShowHidFiles( dir_ni->vol ) - || metadata ) ) ) - { - if ( NVolCaseSensitive( dir_ni->vol ) ) - { - res = filldir( dirent, fn->file_name, - fn->file_name_length, - fn->file_name_type, *pos, - mref, dt_type ); - } - else - { - loname = ( ntfschar* )ntfs_malloc( 2 * fn->file_name_length ); - if ( loname ) - { - memcpy( loname, fn->file_name, - 2*fn->file_name_length ); - ntfs_name_locase( loname, fn->file_name_length, - dir_ni->vol->locase, - dir_ni->vol->upcase_len ); - res = filldir( dirent, loname, - fn->file_name_length, - fn->file_name_type, *pos, - mref, dt_type ); - free( loname ); - } - else - res = -1; - } - } - else - res = 0; - return ( res ); + /* return metadata files and hidden files if requested */ + mref = le64_to_cpu(ie->indexed_file); + metadata = (MREF(mref) != FILE_root) && (MREF(mref) < FILE_first_user); + if ((!metadata && (NVolShowHidFiles(dir_ni->vol) + || !(fn->file_attributes & FILE_ATTR_HIDDEN))) + || (NVolShowSysFiles(dir_ni->vol) && (NVolShowHidFiles(dir_ni->vol) + || metadata))) { + if (NVolCaseSensitive(dir_ni->vol)) { + res = filldir(dirent, fn->file_name, + fn->file_name_length, + fn->file_name_type, *pos, + mref, dt_type); + } else { + loname = (ntfschar*)ntfs_malloc(2*fn->file_name_length); + if (loname) { + memcpy(loname, fn->file_name, + 2*fn->file_name_length); + ntfs_name_locase(loname, fn->file_name_length, + dir_ni->vol->locase, + dir_ni->vol->upcase_len); + res = filldir(dirent, loname, + fn->file_name_length, + fn->file_name_type, *pos, + mref, dt_type); + free(loname); + } else + res = -1; + } + } else + res = 0; + return (res); } /** * ntfs_mft_get_parent_ref - find mft reference of parent directory of an inode - * @ni: ntfs inode whose parent directory to find + * @ni: ntfs inode whose parent directory to find * * Find the parent directory of the ntfs inode @ni. To do this, find the first * file name attribute in the mft record of @ni and return the parent mft @@ -1047,63 +964,59 @@ static int ntfs_filldir( ntfs_inode *dir_ni, s64 *pos, u8 ivcn_bits, * Return the mft reference of the parent directory on success or -1 on error * with errno set to the error code. */ -static MFT_REF ntfs_mft_get_parent_ref( ntfs_inode *ni ) +static MFT_REF ntfs_mft_get_parent_ref(ntfs_inode *ni) { - MFT_REF mref; - ntfs_attr_search_ctx *ctx; - FILE_NAME_ATTR *fn; - int eo; + MFT_REF mref; + ntfs_attr_search_ctx *ctx; + FILE_NAME_ATTR *fn; + int eo; - ntfs_log_trace( "Entering.\n" ); + ntfs_log_trace("Entering.\n"); + + if (!ni) { + errno = EINVAL; + return ERR_MREF(-1); + } - if ( !ni ) - { - errno = EINVAL; - return ERR_MREF( -1 ); - } - - ctx = ntfs_attr_get_search_ctx( ni, NULL ); - if ( !ctx ) - return ERR_MREF( -1 ); - if ( ntfs_attr_lookup( AT_FILE_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx ) ) - { - ntfs_log_error( "No file name found in inode %lld\n", - ( unsigned long long )ni->mft_no ); - goto err_out; - } - if ( ctx->attr->non_resident ) - { - ntfs_log_error( "File name attribute must be resident (inode " - "%lld)\n", ( unsigned long long )ni->mft_no ); - goto io_err_out; - } - fn = ( FILE_NAME_ATTR* )( ( u8* )ctx->attr + - le16_to_cpu( ctx->attr->value_offset ) ); - if ( ( u8* )fn + le32_to_cpu( ctx->attr->value_length ) > - ( u8* )ctx->attr + le32_to_cpu( ctx->attr->length ) ) - { - ntfs_log_error( "Corrupt file name attribute in inode %lld.\n", - ( unsigned long long )ni->mft_no ); - goto io_err_out; - } - mref = le64_to_cpu( fn->parent_directory ); - ntfs_attr_put_search_ctx( ctx ); - return mref; + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return ERR_MREF(-1); + if (ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { + ntfs_log_error("No file name found in inode %lld\n", + (unsigned long long)ni->mft_no); + goto err_out; + } + if (ctx->attr->non_resident) { + ntfs_log_error("File name attribute must be resident (inode " + "%lld)\n", (unsigned long long)ni->mft_no); + goto io_err_out; + } + fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + if ((u8*)fn + le32_to_cpu(ctx->attr->value_length) > + (u8*)ctx->attr + le32_to_cpu(ctx->attr->length)) { + ntfs_log_error("Corrupt file name attribute in inode %lld.\n", + (unsigned long long)ni->mft_no); + goto io_err_out; + } + mref = le64_to_cpu(fn->parent_directory); + ntfs_attr_put_search_ctx(ctx); + return mref; io_err_out: - errno = EIO; + errno = EIO; err_out: - eo = errno; - ntfs_attr_put_search_ctx( ctx ); - errno = eo; - return ERR_MREF( -1 ); + eo = errno; + ntfs_attr_put_search_ctx(ctx); + errno = eo; + return ERR_MREF(-1); } /** * ntfs_readdir - read the contents of an ntfs directory - * @dir_ni: ntfs inode of current directory - * @pos: current position in directory - * @dirent: context for filldir callback supplied by the caller - * @filldir: filldir callback supplied by the caller + * @dir_ni: ntfs inode of current directory + * @pos: current position in directory + * @dirent: context for filldir callback supplied by the caller + * @filldir: filldir callback supplied by the caller * * Parse the index root and the index blocks that are marked in use in the * index bitmap and hand each found directory entry to the @filldir callback @@ -1114,395 +1027,368 @@ err_out: * Note: Index blocks are parsed in ascending vcn order, from which follows * that the directory entries are not returned sorted. */ -int ntfs_readdir( ntfs_inode *dir_ni, s64 *pos, - void *dirent, ntfs_filldir_t filldir ) +int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, + void *dirent, ntfs_filldir_t filldir) { - s64 i_size, br, ia_pos, bmp_pos, ia_start; - ntfs_volume *vol; - ntfs_attr *ia_na, *bmp_na = NULL; - ntfs_attr_search_ctx *ctx = NULL; - u8 *index_end, *bmp = NULL; - INDEX_ROOT *ir; - INDEX_ENTRY *ie; - INDEX_ALLOCATION *ia = NULL; - int rc, ir_pos, bmp_buf_size, bmp_buf_pos, eo; - u32 index_block_size, index_vcn_size; - u8 index_block_size_bits, index_vcn_size_bits; + s64 i_size, br, ia_pos, bmp_pos, ia_start; + ntfs_volume *vol; + ntfs_attr *ia_na, *bmp_na = NULL; + ntfs_attr_search_ctx *ctx = NULL; + u8 *index_end, *bmp = NULL; + INDEX_ROOT *ir; + INDEX_ENTRY *ie; + INDEX_ALLOCATION *ia = NULL; + int rc, ir_pos, bmp_buf_size, bmp_buf_pos, eo; + u32 index_block_size, index_vcn_size; + u8 index_block_size_bits, index_vcn_size_bits; - ntfs_log_trace( "Entering.\n" ); + ntfs_log_trace("Entering.\n"); + + if (!dir_ni || !pos || !filldir) { + errno = EINVAL; + return -1; + } - if ( !dir_ni || !pos || !filldir ) - { - errno = EINVAL; - return -1; - } + if (!(dir_ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) { + errno = ENOTDIR; + return -1; + } - if ( !( dir_ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ) ) - { - errno = ENOTDIR; - return -1; - } + vol = dir_ni->vol; - vol = dir_ni->vol; + ntfs_log_trace("Entering for inode %lld, *pos 0x%llx.\n", + (unsigned long long)dir_ni->mft_no, (long long)*pos); - ntfs_log_trace( "Entering for inode %lld, *pos 0x%llx.\n", - ( unsigned long long )dir_ni->mft_no, ( long long )*pos ); + /* Open the index allocation attribute. */ + ia_na = ntfs_attr_open(dir_ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4); + if (!ia_na) { + if (errno != ENOENT) { + ntfs_log_perror("Failed to open index allocation attribute. " + "Directory inode %lld is corrupt or bug", + (unsigned long long)dir_ni->mft_no); + return -1; + } + i_size = 0; + } else + i_size = ia_na->data_size; - /* Open the index allocation attribute. */ - ia_na = ntfs_attr_open( dir_ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4 ); - if ( !ia_na ) - { - if ( errno != ENOENT ) - { - ntfs_log_perror( "Failed to open index allocation attribute. " - "Directory inode %lld is corrupt or bug", - ( unsigned long long )dir_ni->mft_no ); - return -1; - } - i_size = 0; - } - else - i_size = ia_na->data_size; + rc = 0; - rc = 0; + /* Are we at end of dir yet? */ + if (*pos >= i_size + vol->mft_record_size) + goto done; - /* Are we at end of dir yet? */ - if ( *pos >= i_size + vol->mft_record_size ) - goto done; + /* Emulate . and .. for all directories. */ + if (!*pos) { + rc = filldir(dirent, dotdot, 1, FILE_NAME_POSIX, *pos, + MK_MREF(dir_ni->mft_no, + le16_to_cpu(dir_ni->mrec->sequence_number)), + NTFS_DT_DIR); + if (rc) + goto err_out; + ++*pos; + } + if (*pos == 1) { + MFT_REF parent_mref; - /* Emulate . and .. for all directories. */ - if ( !*pos ) - { - rc = filldir( dirent, dotdot, 1, FILE_NAME_POSIX, *pos, - MK_MREF( dir_ni->mft_no, - le16_to_cpu( dir_ni->mrec->sequence_number ) ), - NTFS_DT_DIR ); - if ( rc ) - goto err_out; - ++*pos; - } - if ( *pos == 1 ) - { - MFT_REF parent_mref; + parent_mref = ntfs_mft_get_parent_ref(dir_ni); + if (parent_mref == ERR_MREF(-1)) { + ntfs_log_perror("Parent directory not found"); + goto dir_err_out; + } - parent_mref = ntfs_mft_get_parent_ref( dir_ni ); - if ( parent_mref == ERR_MREF( -1 ) ) - { - ntfs_log_perror( "Parent directory not found" ); - goto dir_err_out; - } + rc = filldir(dirent, dotdot, 2, FILE_NAME_POSIX, *pos, + parent_mref, NTFS_DT_DIR); + if (rc) + goto err_out; + ++*pos; + } - rc = filldir( dirent, dotdot, 2, FILE_NAME_POSIX, *pos, - parent_mref, NTFS_DT_DIR ); - if ( rc ) - goto err_out; - ++*pos; - } + ctx = ntfs_attr_get_search_ctx(dir_ni, NULL); + if (!ctx) + goto err_out; - ctx = ntfs_attr_get_search_ctx( dir_ni, NULL ); - if ( !ctx ) - goto err_out; + /* Get the offset into the index root attribute. */ + ir_pos = (int)*pos; + /* Find the index root attribute in the mft record. */ + if (ntfs_attr_lookup(AT_INDEX_ROOT, NTFS_INDEX_I30, 4, CASE_SENSITIVE, 0, NULL, + 0, ctx)) { + ntfs_log_perror("Index root attribute missing in directory inode " + "%lld", (unsigned long long)dir_ni->mft_no); + goto dir_err_out; + } + /* Get to the index root value. */ + ir = (INDEX_ROOT*)((u8*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); - /* Get the offset into the index root attribute. */ - ir_pos = ( int ) * pos; - /* Find the index root attribute in the mft record. */ - if ( ntfs_attr_lookup( AT_INDEX_ROOT, NTFS_INDEX_I30, 4, CASE_SENSITIVE, 0, NULL, - 0, ctx ) ) - { - ntfs_log_perror( "Index root attribute missing in directory inode " - "%lld", ( unsigned long long )dir_ni->mft_no ); - goto dir_err_out; - } - /* Get to the index root value. */ - ir = ( INDEX_ROOT* )( ( u8* )ctx->attr + - le16_to_cpu( ctx->attr->value_offset ) ); + /* Determine the size of a vcn in the directory index. */ + index_block_size = le32_to_cpu(ir->index_block_size); + if (index_block_size < NTFS_BLOCK_SIZE || + index_block_size & (index_block_size - 1)) { + ntfs_log_error("Index block size %u is invalid.\n", + (unsigned)index_block_size); + goto dir_err_out; + } + index_block_size_bits = ffs(index_block_size) - 1; + if (vol->cluster_size <= index_block_size) { + index_vcn_size = vol->cluster_size; + index_vcn_size_bits = vol->cluster_size_bits; + } else { + index_vcn_size = vol->sector_size; + index_vcn_size_bits = vol->sector_size_bits; + } - /* Determine the size of a vcn in the directory index. */ - index_block_size = le32_to_cpu( ir->index_block_size ); - if ( index_block_size < NTFS_BLOCK_SIZE || - index_block_size & ( index_block_size - 1 ) ) - { - ntfs_log_error( "Index block size %u is invalid.\n", - ( unsigned )index_block_size ); - goto dir_err_out; - } - index_block_size_bits = ffs( index_block_size ) - 1; - if ( vol->cluster_size <= index_block_size ) - { - index_vcn_size = vol->cluster_size; - index_vcn_size_bits = vol->cluster_size_bits; - } - else - { - index_vcn_size = vol->sector_size; - index_vcn_size_bits = vol->sector_size_bits; - } + /* Are we jumping straight into the index allocation attribute? */ + if (*pos >= vol->mft_record_size) { + ntfs_attr_put_search_ctx(ctx); + ctx = NULL; + goto skip_index_root; + } - /* Are we jumping straight into the index allocation attribute? */ - if ( *pos >= vol->mft_record_size ) - { - ntfs_attr_put_search_ctx( ctx ); - ctx = NULL; - goto skip_index_root; - } + index_end = (u8*)&ir->index + le32_to_cpu(ir->index.index_length); + /* The first index entry. */ + ie = (INDEX_ENTRY*)((u8*)&ir->index + + le32_to_cpu(ir->index.entries_offset)); + /* + * Loop until we exceed valid memory (corruption case) or until we + * reach the last entry or until filldir tells us it has had enough + * or signals an error (both covered by the rc test). + */ + for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) { + ntfs_log_debug("In index root, offset %d.\n", (int)((u8*)ie - (u8*)ir)); + /* Bounds checks. */ + if ((u8*)ie < (u8*)ctx->mrec || (u8*)ie + + sizeof(INDEX_ENTRY_HEADER) > index_end || + (u8*)ie + le16_to_cpu(ie->key_length) > + index_end) + goto dir_err_out; + /* The last entry cannot contain a name. */ + if (ie->ie_flags & INDEX_ENTRY_END) + break; + + if (!le16_to_cpu(ie->length)) + goto dir_err_out; + + /* Skip index root entry if continuing previous readdir. */ + if (ir_pos > (u8*)ie - (u8*)ir) + continue; + /* + * Submit the directory entry to ntfs_filldir(), which will + * invoke the filldir() callback as appropriate. + */ + rc = ntfs_filldir(dir_ni, pos, index_vcn_size_bits, + INDEX_TYPE_ROOT, ir, ie, dirent, filldir); + if (rc) { + ntfs_attr_put_search_ctx(ctx); + ctx = NULL; + goto err_out; + } + } + ntfs_attr_put_search_ctx(ctx); + ctx = NULL; - index_end = ( u8* ) & ir->index + le32_to_cpu( ir->index.index_length ); - /* The first index entry. */ - ie = ( INDEX_ENTRY* )( ( u8* ) & ir->index + - le32_to_cpu( ir->index.entries_offset ) ); - /* - * Loop until we exceed valid memory (corruption case) or until we - * reach the last entry or until filldir tells us it has had enough - * or signals an error (both covered by the rc test). - */ - for ( ;; ie = ( INDEX_ENTRY* )( ( u8* )ie + le16_to_cpu( ie->length ) ) ) - { - ntfs_log_debug( "In index root, offset %d.\n", ( int )( ( u8* )ie - ( u8* )ir ) ); - /* Bounds checks. */ - if ( ( u8* )ie < ( u8* )ctx->mrec || ( u8* )ie + - sizeof( INDEX_ENTRY_HEADER ) > index_end || - ( u8* )ie + le16_to_cpu( ie->key_length ) > - index_end ) - goto dir_err_out; - /* The last entry cannot contain a name. */ - if ( ie->ie_flags & INDEX_ENTRY_END ) - break; + /* If there is no index allocation attribute we are finished. */ + if (!ia_na) + goto EOD; - if ( !le16_to_cpu( ie->length ) ) - goto dir_err_out; - - /* Skip index root entry if continuing previous readdir. */ - if ( ir_pos > ( u8* )ie - ( u8* )ir ) - continue; - /* - * Submit the directory entry to ntfs_filldir(), which will - * invoke the filldir() callback as appropriate. - */ - rc = ntfs_filldir( dir_ni, pos, index_vcn_size_bits, - INDEX_TYPE_ROOT, ir, ie, dirent, filldir ); - if ( rc ) - { - ntfs_attr_put_search_ctx( ctx ); - ctx = NULL; - goto err_out; - } - } - ntfs_attr_put_search_ctx( ctx ); - ctx = NULL; - - /* If there is no index allocation attribute we are finished. */ - if ( !ia_na ) - goto EOD; - - /* Advance *pos to the beginning of the index allocation. */ - *pos = vol->mft_record_size; + /* Advance *pos to the beginning of the index allocation. */ + *pos = vol->mft_record_size; skip_index_root: - if ( !ia_na ) - goto done; + if (!ia_na) + goto done; - /* Allocate a buffer for the current index block. */ - ia = ntfs_malloc( index_block_size ); - if ( !ia ) - goto err_out; + /* Allocate a buffer for the current index block. */ + ia = ntfs_malloc(index_block_size); + if (!ia) + goto err_out; - bmp_na = ntfs_attr_open( dir_ni, AT_BITMAP, NTFS_INDEX_I30, 4 ); - if ( !bmp_na ) - { - ntfs_log_perror( "Failed to open index bitmap attribute" ); - goto dir_err_out; - } + bmp_na = ntfs_attr_open(dir_ni, AT_BITMAP, NTFS_INDEX_I30, 4); + if (!bmp_na) { + ntfs_log_perror("Failed to open index bitmap attribute"); + goto dir_err_out; + } - /* Get the offset into the index allocation attribute. */ - ia_pos = *pos - vol->mft_record_size; + /* Get the offset into the index allocation attribute. */ + ia_pos = *pos - vol->mft_record_size; - bmp_pos = ia_pos >> index_block_size_bits; - if ( bmp_pos >> 3 >= bmp_na->data_size ) - { - ntfs_log_error( "Current index position exceeds index bitmap " - "size.\n" ); - goto dir_err_out; - } + bmp_pos = ia_pos >> index_block_size_bits; + if (bmp_pos >> 3 >= bmp_na->data_size) { + ntfs_log_error("Current index position exceeds index bitmap " + "size.\n"); + goto dir_err_out; + } - bmp_buf_size = min( bmp_na->data_size - ( bmp_pos >> 3 ), 4096 ); - bmp = ntfs_malloc( bmp_buf_size ); - if ( !bmp ) - goto err_out; + bmp_buf_size = min(bmp_na->data_size - (bmp_pos >> 3), 4096); + bmp = ntfs_malloc(bmp_buf_size); + if (!bmp) + goto err_out; - br = ntfs_attr_pread( bmp_na, bmp_pos >> 3, bmp_buf_size, bmp ); - if ( br != bmp_buf_size ) - { - if ( br != -1 ) - errno = EIO; - ntfs_log_perror( "Failed to read from index bitmap attribute" ); - goto err_out; - } + br = ntfs_attr_pread(bmp_na, bmp_pos >> 3, bmp_buf_size, bmp); + if (br != bmp_buf_size) { + if (br != -1) + errno = EIO; + ntfs_log_perror("Failed to read from index bitmap attribute"); + goto err_out; + } - bmp_buf_pos = 0; - /* If the index block is not in use find the next one that is. */ - while ( !( bmp[bmp_buf_pos >> 3] & ( 1 << ( bmp_buf_pos & 7 ) ) ) ) - { + bmp_buf_pos = 0; + /* If the index block is not in use find the next one that is. */ + while (!(bmp[bmp_buf_pos >> 3] & (1 << (bmp_buf_pos & 7)))) { find_next_index_buffer: - bmp_pos++; - bmp_buf_pos++; - /* If we have reached the end of the bitmap, we are done. */ - if ( bmp_pos >> 3 >= bmp_na->data_size ) - goto EOD; - ia_pos = bmp_pos << index_block_size_bits; - if ( bmp_buf_pos >> 3 < bmp_buf_size ) - continue; - /* Read next chunk from the index bitmap. */ - bmp_buf_pos = 0; - if ( ( bmp_pos >> 3 ) + bmp_buf_size > bmp_na->data_size ) - bmp_buf_size = bmp_na->data_size - ( bmp_pos >> 3 ); - br = ntfs_attr_pread( bmp_na, bmp_pos >> 3, bmp_buf_size, bmp ); - if ( br != bmp_buf_size ) - { - if ( br != -1 ) - errno = EIO; - ntfs_log_perror( "Failed to read from index bitmap attribute" ); - goto err_out; - } - } + bmp_pos++; + bmp_buf_pos++; + /* If we have reached the end of the bitmap, we are done. */ + if (bmp_pos >> 3 >= bmp_na->data_size) + goto EOD; + ia_pos = bmp_pos << index_block_size_bits; + if (bmp_buf_pos >> 3 < bmp_buf_size) + continue; + /* Read next chunk from the index bitmap. */ + bmp_buf_pos = 0; + if ((bmp_pos >> 3) + bmp_buf_size > bmp_na->data_size) + bmp_buf_size = bmp_na->data_size - (bmp_pos >> 3); + br = ntfs_attr_pread(bmp_na, bmp_pos >> 3, bmp_buf_size, bmp); + if (br != bmp_buf_size) { + if (br != -1) + errno = EIO; + ntfs_log_perror("Failed to read from index bitmap attribute"); + goto err_out; + } + } - ntfs_log_debug( "Handling index block 0x%llx.\n", ( long long )bmp_pos ); + ntfs_log_debug("Handling index block 0x%llx.\n", (long long)bmp_pos); - /* Read the index block starting at bmp_pos. */ - br = ntfs_attr_mst_pread( ia_na, bmp_pos << index_block_size_bits, 1, - index_block_size, ia ); - if ( br != 1 ) - { - if ( br != -1 ) - errno = EIO; - ntfs_log_perror( "Failed to read index block" ); - goto err_out; - } + /* Read the index block starting at bmp_pos. */ + br = ntfs_attr_mst_pread(ia_na, bmp_pos << index_block_size_bits, 1, + index_block_size, ia); + if (br != 1) { + if (br != -1) + errno = EIO; + ntfs_log_perror("Failed to read index block"); + goto err_out; + } - ia_start = ia_pos & ~( s64 )( index_block_size - 1 ); - if ( sle64_to_cpu( ia->index_block_vcn ) != ia_start >> - index_vcn_size_bits ) - { - ntfs_log_error( "Actual VCN (0x%llx) of index buffer is different " - "from expected VCN (0x%llx) in inode 0x%llx.\n", - ( long long )sle64_to_cpu( ia->index_block_vcn ), - ( long long )ia_start >> index_vcn_size_bits, - ( unsigned long long )dir_ni->mft_no ); - goto dir_err_out; - } - if ( le32_to_cpu( ia->index.allocated_size ) + 0x18 != index_block_size ) - { - ntfs_log_error( "Index buffer (VCN 0x%llx) of directory inode %lld " - "has a size (%u) differing from the directory " - "specified size (%u).\n", ( long long )ia_start >> - index_vcn_size_bits, - ( unsigned long long )dir_ni->mft_no, - ( unsigned ) le32_to_cpu( ia->index.allocated_size ) - + 0x18, ( unsigned )index_block_size ); - goto dir_err_out; - } - index_end = ( u8* ) & ia->index + le32_to_cpu( ia->index.index_length ); - if ( index_end > ( u8* )ia + index_block_size ) - { - ntfs_log_error( "Size of index buffer (VCN 0x%llx) of directory inode " - "%lld exceeds maximum size.\n", - ( long long )ia_start >> index_vcn_size_bits, - ( unsigned long long )dir_ni->mft_no ); - goto dir_err_out; - } - /* The first index entry. */ - ie = ( INDEX_ENTRY* )( ( u8* ) & ia->index + - le32_to_cpu( ia->index.entries_offset ) ); - /* - * Loop until we exceed valid memory (corruption case) or until we - * reach the last entry or until ntfs_filldir tells us it has had - * enough or signals an error (both covered by the rc test). - */ - for ( ;; ie = ( INDEX_ENTRY* )( ( u8* )ie + le16_to_cpu( ie->length ) ) ) - { - ntfs_log_debug( "In index allocation, offset 0x%llx.\n", - ( long long )ia_start + ( ( u8* )ie - ( u8* )ia ) ); - /* Bounds checks. */ - if ( ( u8* )ie < ( u8* )ia || ( u8* )ie + - sizeof( INDEX_ENTRY_HEADER ) > index_end || - ( u8* )ie + le16_to_cpu( ie->key_length ) > - index_end ) - { - ntfs_log_error( "Index entry out of bounds in directory inode " - "%lld.\n", ( unsigned long long )dir_ni->mft_no ); - goto dir_err_out; - } - /* The last entry cannot contain a name. */ - if ( ie->ie_flags & INDEX_ENTRY_END ) - break; - - if ( !le16_to_cpu( ie->length ) ) - goto dir_err_out; - - /* Skip index entry if continuing previous readdir. */ - if ( ia_pos - ia_start > ( u8* )ie - ( u8* )ia ) - continue; - /* - * Submit the directory entry to ntfs_filldir(), which will - * invoke the filldir() callback as appropriate. - */ - rc = ntfs_filldir( dir_ni, pos, index_vcn_size_bits, - INDEX_TYPE_ALLOCATION, ia, ie, dirent, filldir ); - if ( rc ) - goto err_out; - } - goto find_next_index_buffer; + ia_start = ia_pos & ~(s64)(index_block_size - 1); + if (sle64_to_cpu(ia->index_block_vcn) != ia_start >> + index_vcn_size_bits) { + ntfs_log_error("Actual VCN (0x%llx) of index buffer is different " + "from expected VCN (0x%llx) in inode 0x%llx.\n", + (long long)sle64_to_cpu(ia->index_block_vcn), + (long long)ia_start >> index_vcn_size_bits, + (unsigned long long)dir_ni->mft_no); + goto dir_err_out; + } + if (le32_to_cpu(ia->index.allocated_size) + 0x18 != index_block_size) { + ntfs_log_error("Index buffer (VCN 0x%llx) of directory inode %lld " + "has a size (%u) differing from the directory " + "specified size (%u).\n", (long long)ia_start >> + index_vcn_size_bits, + (unsigned long long)dir_ni->mft_no, + (unsigned) le32_to_cpu(ia->index.allocated_size) + + 0x18, (unsigned)index_block_size); + goto dir_err_out; + } + index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length); + if (index_end > (u8*)ia + index_block_size) { + ntfs_log_error("Size of index buffer (VCN 0x%llx) of directory inode " + "%lld exceeds maximum size.\n", + (long long)ia_start >> index_vcn_size_bits, + (unsigned long long)dir_ni->mft_no); + goto dir_err_out; + } + /* The first index entry. */ + ie = (INDEX_ENTRY*)((u8*)&ia->index + + le32_to_cpu(ia->index.entries_offset)); + /* + * Loop until we exceed valid memory (corruption case) or until we + * reach the last entry or until ntfs_filldir tells us it has had + * enough or signals an error (both covered by the rc test). + */ + for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) { + ntfs_log_debug("In index allocation, offset 0x%llx.\n", + (long long)ia_start + ((u8*)ie - (u8*)ia)); + /* Bounds checks. */ + if ((u8*)ie < (u8*)ia || (u8*)ie + + sizeof(INDEX_ENTRY_HEADER) > index_end || + (u8*)ie + le16_to_cpu(ie->key_length) > + index_end) { + ntfs_log_error("Index entry out of bounds in directory inode " + "%lld.\n", (unsigned long long)dir_ni->mft_no); + goto dir_err_out; + } + /* The last entry cannot contain a name. */ + if (ie->ie_flags & INDEX_ENTRY_END) + break; + + if (!le16_to_cpu(ie->length)) + goto dir_err_out; + + /* Skip index entry if continuing previous readdir. */ + if (ia_pos - ia_start > (u8*)ie - (u8*)ia) + continue; + /* + * Submit the directory entry to ntfs_filldir(), which will + * invoke the filldir() callback as appropriate. + */ + rc = ntfs_filldir(dir_ni, pos, index_vcn_size_bits, + INDEX_TYPE_ALLOCATION, ia, ie, dirent, filldir); + if (rc) + goto err_out; + } + goto find_next_index_buffer; EOD: - /* We are finished, set *pos to EOD. */ - *pos = i_size + vol->mft_record_size; + /* We are finished, set *pos to EOD. */ + *pos = i_size + vol->mft_record_size; done: - free( ia ); - free( bmp ); - if ( bmp_na ) - ntfs_attr_close( bmp_na ); - if ( ia_na ) - ntfs_attr_close( ia_na ); - ntfs_log_debug( "EOD, *pos 0x%llx, returning 0.\n", ( long long )*pos ); - return 0; + free(ia); + free(bmp); + if (bmp_na) + ntfs_attr_close(bmp_na); + if (ia_na) + ntfs_attr_close(ia_na); + ntfs_log_debug("EOD, *pos 0x%llx, returning 0.\n", (long long)*pos); + return 0; dir_err_out: - errno = EIO; + errno = EIO; err_out: - eo = errno; - ntfs_log_trace( "failed.\n" ); - if ( ctx ) - ntfs_attr_put_search_ctx( ctx ); - free( ia ); - free( bmp ); - if ( bmp_na ) - ntfs_attr_close( bmp_na ); - if ( ia_na ) - ntfs_attr_close( ia_na ); - errno = eo; - return -1; + eo = errno; + ntfs_log_trace("failed.\n"); + if (ctx) + ntfs_attr_put_search_ctx(ctx); + free(ia); + free(bmp); + if (bmp_na) + ntfs_attr_close(bmp_na); + if (ia_na) + ntfs_attr_close(ia_na); + errno = eo; + return -1; } /** * __ntfs_create - create object on ntfs volume - * @dir_ni: ntfs inode for directory in which create new object - * @securid: id of inheritable security descriptor, 0 if none - * @name: unicode name of new object - * @name_len: length of the name in unicode characters - * @type: type of the object to create - * @dev: major and minor device numbers (obtained from makedev()) - * @target: target in unicode (only for symlinks) - * @target_len: length of target in unicode characters + * @dir_ni: ntfs inode for directory in which create new object + * @securid: id of inheritable security descriptor, 0 if none + * @name: unicode name of new object + * @name_len: length of the name in unicode characters + * @type: type of the object to create + * @dev: major and minor device numbers (obtained from makedev()) + * @target: target in unicode (only for symlinks) + * @target_len: length of target in unicode characters * * Internal, use ntfs_create{,_device,_symlink} wrappers instead. * * @type can be: - * S_IFREG to create regular file - * S_IFDIR to create directory - * S_IFBLK to create block device - * S_IFCHR to create character device - * S_IFLNK to create symbolic link - * S_IFIFO to create FIFO - * S_IFSOCK to create socket + * S_IFREG to create regular file + * S_IFDIR to create directory + * S_IFBLK to create block device + * S_IFCHR to create character device + * S_IFLNK to create symbolic link + * S_IFIFO to create FIFO + * S_IFSOCK to create socket * other values are invalid. * * @dev is used only if @type is S_IFBLK or S_IFCHR, in other cases its value @@ -1514,699 +1400,644 @@ err_out: * Return opened ntfs inode that describes created object on success or NULL * on error with errno set to the error code. */ -static ntfs_inode *__ntfs_create( ntfs_inode *dir_ni, le32 securid, - ntfschar *name, u8 name_len, mode_t type, dev_t dev, - ntfschar *target, int target_len ) +static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, + ntfschar *name, u8 name_len, mode_t type, dev_t dev, + ntfschar *target, int target_len) { - ntfs_inode *ni; - int rollback_data = 0, rollback_sd = 0; - FILE_NAME_ATTR *fn = NULL; - STANDARD_INFORMATION *si = NULL; - int err, fn_len, si_len; + ntfs_inode *ni; + int rollback_data = 0, rollback_sd = 0; + FILE_NAME_ATTR *fn = NULL; + STANDARD_INFORMATION *si = NULL; + int err, fn_len, si_len; - ntfs_log_trace( "Entering.\n" ); - - /* Sanity checks. */ - if ( !dir_ni || !name || !name_len ) - { - ntfs_log_error( "Invalid arguments.\n" ); - errno = EINVAL; - return NULL; - } - - if ( dir_ni->flags & FILE_ATTR_REPARSE_POINT ) - { - errno = EOPNOTSUPP; - return NULL; - } - - ni = ntfs_mft_record_alloc( dir_ni->vol, NULL ); - if ( !ni ) - return NULL; + ntfs_log_trace("Entering.\n"); + + /* Sanity checks. */ + if (!dir_ni || !name || !name_len) { + ntfs_log_error("Invalid arguments.\n"); + errno = EINVAL; + return NULL; + } + + if (dir_ni->flags & FILE_ATTR_REPARSE_POINT) { + errno = EOPNOTSUPP; + return NULL; + } + + ni = ntfs_mft_record_alloc(dir_ni->vol, NULL); + if (!ni) + return NULL; #if CACHE_NIDATA_SIZE - ntfs_inode_invalidate( dir_ni->vol, ni->mft_no ); + ntfs_inode_invalidate(dir_ni->vol, ni->mft_no); #endif - /* - * Create STANDARD_INFORMATION attribute. - * JPA Depending on available inherited security descriptor, - * Write STANDARD_INFORMATION v1.2 (no inheritance) or v3 - */ - if ( securid ) - si_len = sizeof( STANDARD_INFORMATION ); - else - si_len = offsetof( STANDARD_INFORMATION, v1_end ); - si = ntfs_calloc( si_len ); - if ( !si ) - { - err = errno; - goto err_out; - } - si->creation_time = ni->creation_time; - si->last_data_change_time = ni->last_data_change_time; - si->last_mft_change_time = ni->last_mft_change_time; - si->last_access_time = ni->last_access_time; - if ( securid ) - { - set_nino_flag( ni, v3_Extensions ); - ni->owner_id = si->owner_id = 0; - ni->security_id = si->security_id = securid; - ni->quota_charged = si->quota_charged = const_cpu_to_le64( 0 ); - ni->usn = si->usn = const_cpu_to_le64( 0 ); - } - else - clear_nino_flag( ni, v3_Extensions ); - if ( !S_ISREG( type ) && !S_ISDIR( type ) ) - { - si->file_attributes = FILE_ATTR_SYSTEM; - ni->flags = FILE_ATTR_SYSTEM; - } - ni->flags |= FILE_ATTR_ARCHIVE; - if ( NVolHideDotFiles( dir_ni->vol ) - && ( name_len > 1 ) - && ( name[0] == const_cpu_to_le16( '.' ) ) - && ( name[1] != const_cpu_to_le16( '.' ) ) ) - ni->flags |= FILE_ATTR_HIDDEN; - /* - * Set compression flag according to parent directory - * unless NTFS version < 3.0 or cluster size > 4K - * or compression has been disabled - */ - if ( ( dir_ni->flags & FILE_ATTR_COMPRESSED ) - && ( dir_ni->vol->major_ver >= 3 ) - && NVolCompression( dir_ni->vol ) - && ( dir_ni->vol->cluster_size <= MAX_COMPRESSION_CLUSTER_SIZE ) - && ( S_ISREG( type ) || S_ISDIR( type ) ) ) - ni->flags |= FILE_ATTR_COMPRESSED; - /* Add STANDARD_INFORMATION to inode. */ - if ( ntfs_attr_add( ni, AT_STANDARD_INFORMATION, AT_UNNAMED, 0, - ( u8* )si, si_len ) ) - { - err = errno; - ntfs_log_error( "Failed to add STANDARD_INFORMATION " - "attribute.\n" ); - goto err_out; - } + /* + * Create STANDARD_INFORMATION attribute. + * JPA Depending on available inherited security descriptor, + * Write STANDARD_INFORMATION v1.2 (no inheritance) or v3 + */ + if (securid) + si_len = sizeof(STANDARD_INFORMATION); + else + si_len = offsetof(STANDARD_INFORMATION, v1_end); + si = ntfs_calloc(si_len); + if (!si) { + err = errno; + goto err_out; + } + si->creation_time = ni->creation_time; + si->last_data_change_time = ni->last_data_change_time; + si->last_mft_change_time = ni->last_mft_change_time; + si->last_access_time = ni->last_access_time; + if (securid) { + set_nino_flag(ni, v3_Extensions); + ni->owner_id = si->owner_id = 0; + ni->security_id = si->security_id = securid; + ni->quota_charged = si->quota_charged = const_cpu_to_le64(0); + ni->usn = si->usn = const_cpu_to_le64(0); + } else + clear_nino_flag(ni, v3_Extensions); + if (!S_ISREG(type) && !S_ISDIR(type)) { + si->file_attributes = FILE_ATTR_SYSTEM; + ni->flags = FILE_ATTR_SYSTEM; + } + ni->flags |= FILE_ATTR_ARCHIVE; + if (NVolHideDotFiles(dir_ni->vol) + && (name_len > 1) + && (name[0] == const_cpu_to_le16('.')) + && (name[1] != const_cpu_to_le16('.'))) + ni->flags |= FILE_ATTR_HIDDEN; + /* + * Set compression flag according to parent directory + * unless NTFS version < 3.0 or cluster size > 4K + * or compression has been disabled + */ + if ((dir_ni->flags & FILE_ATTR_COMPRESSED) + && (dir_ni->vol->major_ver >= 3) + && NVolCompression(dir_ni->vol) + && (dir_ni->vol->cluster_size <= MAX_COMPRESSION_CLUSTER_SIZE) + && (S_ISREG(type) || S_ISDIR(type))) + ni->flags |= FILE_ATTR_COMPRESSED; + /* Add STANDARD_INFORMATION to inode. */ + if (ntfs_attr_add(ni, AT_STANDARD_INFORMATION, AT_UNNAMED, 0, + (u8*)si, si_len)) { + err = errno; + ntfs_log_error("Failed to add STANDARD_INFORMATION " + "attribute.\n"); + goto err_out; + } - if ( !securid ) - { - if ( ntfs_sd_add_everyone( ni ) ) - { - err = errno; - goto err_out; - } - } - rollback_sd = 1; + if (!securid) { + if (ntfs_sd_add_everyone(ni)) { + err = errno; + goto err_out; + } + } + rollback_sd = 1; - if ( S_ISDIR( type ) ) - { - INDEX_ROOT *ir = NULL; - INDEX_ENTRY *ie; - int ir_len, index_len; + if (S_ISDIR(type)) { + INDEX_ROOT *ir = NULL; + INDEX_ENTRY *ie; + int ir_len, index_len; - /* Create INDEX_ROOT attribute. */ - index_len = sizeof( INDEX_HEADER ) + sizeof( INDEX_ENTRY_HEADER ); - ir_len = offsetof( INDEX_ROOT, index ) + index_len; - ir = ntfs_calloc( ir_len ); - if ( !ir ) - { - err = errno; - goto err_out; - } - ir->type = AT_FILE_NAME; - ir->collation_rule = COLLATION_FILE_NAME; - ir->index_block_size = cpu_to_le32( ni->vol->indx_record_size ); - if ( ni->vol->cluster_size <= ni->vol->indx_record_size ) - ir->clusters_per_index_block = - ni->vol->indx_record_size >> - ni->vol->cluster_size_bits; - else - ir->clusters_per_index_block = - ni->vol->indx_record_size >> - ni->vol->sector_size_bits; - ir->index.entries_offset = cpu_to_le32( sizeof( INDEX_HEADER ) ); - ir->index.index_length = cpu_to_le32( index_len ); - ir->index.allocated_size = cpu_to_le32( index_len ); - ie = ( INDEX_ENTRY* )( ( u8* )ir + sizeof( INDEX_ROOT ) ); - ie->length = cpu_to_le16( sizeof( INDEX_ENTRY_HEADER ) ); - ie->key_length = 0; - ie->ie_flags = INDEX_ENTRY_END; - /* Add INDEX_ROOT attribute to inode. */ - if ( ntfs_attr_add( ni, AT_INDEX_ROOT, NTFS_INDEX_I30, 4, - ( u8* )ir, ir_len ) ) - { - err = errno; - free( ir ); - ntfs_log_error( "Failed to add INDEX_ROOT attribute.\n" ); - goto err_out; - } - free( ir ); - } - else - { - INTX_FILE *data; - int data_len; + /* Create INDEX_ROOT attribute. */ + index_len = sizeof(INDEX_HEADER) + sizeof(INDEX_ENTRY_HEADER); + ir_len = offsetof(INDEX_ROOT, index) + index_len; + ir = ntfs_calloc(ir_len); + if (!ir) { + err = errno; + goto err_out; + } + ir->type = AT_FILE_NAME; + ir->collation_rule = COLLATION_FILE_NAME; + ir->index_block_size = cpu_to_le32(ni->vol->indx_record_size); + if (ni->vol->cluster_size <= ni->vol->indx_record_size) + ir->clusters_per_index_block = + ni->vol->indx_record_size >> + ni->vol->cluster_size_bits; + else + ir->clusters_per_index_block = + ni->vol->indx_record_size >> + ni->vol->sector_size_bits; + ir->index.entries_offset = cpu_to_le32(sizeof(INDEX_HEADER)); + ir->index.index_length = cpu_to_le32(index_len); + ir->index.allocated_size = cpu_to_le32(index_len); + ie = (INDEX_ENTRY*)((u8*)ir + sizeof(INDEX_ROOT)); + ie->length = cpu_to_le16(sizeof(INDEX_ENTRY_HEADER)); + ie->key_length = 0; + ie->ie_flags = INDEX_ENTRY_END; + /* Add INDEX_ROOT attribute to inode. */ + if (ntfs_attr_add(ni, AT_INDEX_ROOT, NTFS_INDEX_I30, 4, + (u8*)ir, ir_len)) { + err = errno; + free(ir); + ntfs_log_error("Failed to add INDEX_ROOT attribute.\n"); + goto err_out; + } + free(ir); + } else { + INTX_FILE *data; + int data_len; - switch ( type ) - { - case S_IFBLK: - case S_IFCHR: - data_len = offsetof( INTX_FILE, device_end ); - data = ntfs_malloc( data_len ); - if ( !data ) - { - err = errno; - goto err_out; - } - data->major = cpu_to_le64( major( dev ) ); - data->minor = cpu_to_le64( minor( dev ) ); - if ( type == S_IFBLK ) - data->magic = INTX_BLOCK_DEVICE; - if ( type == S_IFCHR ) - data->magic = INTX_CHARACTER_DEVICE; - break; - case S_IFLNK: - data_len = sizeof( INTX_FILE_TYPES ) + - target_len * sizeof( ntfschar ); - data = ntfs_malloc( data_len ); - if ( !data ) - { - err = errno; - goto err_out; - } - data->magic = INTX_SYMBOLIC_LINK; - memcpy( data->target, target, - target_len * sizeof( ntfschar ) ); - break; - case S_IFSOCK: - data = NULL; - data_len = 1; - break; - default: /* FIFO or regular file. */ - data = NULL; - data_len = 0; - break; - } - /* Add DATA attribute to inode. */ - if ( ntfs_attr_add( ni, AT_DATA, AT_UNNAMED, 0, ( u8* )data, - data_len ) ) - { - err = errno; - ntfs_log_error( "Failed to add DATA attribute.\n" ); - free( data ); - goto err_out; - } - rollback_data = 1; - free( data ); - } - /* Create FILE_NAME attribute. */ - fn_len = sizeof( FILE_NAME_ATTR ) + name_len * sizeof( ntfschar ); - fn = ntfs_calloc( fn_len ); - if ( !fn ) - { - err = errno; - goto err_out; - } - fn->parent_directory = MK_LE_MREF( dir_ni->mft_no, - le16_to_cpu( dir_ni->mrec->sequence_number ) ); - fn->file_name_length = name_len; - fn->file_name_type = FILE_NAME_POSIX; - if ( S_ISDIR( type ) ) - fn->file_attributes = FILE_ATTR_I30_INDEX_PRESENT; - if ( !S_ISREG( type ) && !S_ISDIR( type ) ) - fn->file_attributes = FILE_ATTR_SYSTEM; - else - fn->file_attributes |= ni->flags & FILE_ATTR_COMPRESSED; - fn->file_attributes |= FILE_ATTR_ARCHIVE; - fn->file_attributes |= ni->flags & FILE_ATTR_HIDDEN; - fn->creation_time = ni->creation_time; - fn->last_data_change_time = ni->last_data_change_time; - fn->last_mft_change_time = ni->last_mft_change_time; - fn->last_access_time = ni->last_access_time; - if ( ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ) - fn->data_size = fn->allocated_size = const_cpu_to_le64( 0 ); - else - { - fn->data_size = cpu_to_sle64( ni->data_size ); - fn->allocated_size = cpu_to_sle64( ni->allocated_size ); - } - memcpy( fn->file_name, name, name_len * sizeof( ntfschar ) ); - /* Add FILE_NAME attribute to inode. */ - if ( ntfs_attr_add( ni, AT_FILE_NAME, AT_UNNAMED, 0, ( u8* )fn, fn_len ) ) - { - err = errno; - ntfs_log_error( "Failed to add FILE_NAME attribute.\n" ); - goto err_out; - } - /* Add FILE_NAME attribute to index. */ - if ( ntfs_index_add_filename( dir_ni, fn, MK_MREF( ni->mft_no, - le16_to_cpu( ni->mrec->sequence_number ) ) ) ) - { - err = errno; - ntfs_log_perror( "Failed to add entry to the index" ); - goto err_out; - } - /* Set hard links count and directory flag. */ - ni->mrec->link_count = cpu_to_le16( 1 ); - if ( S_ISDIR( type ) ) - ni->mrec->flags |= MFT_RECORD_IS_DIRECTORY; - ntfs_inode_mark_dirty( ni ); - /* Done! */ - free( fn ); - free( si ); - ntfs_log_trace( "Done.\n" ); - return ni; + switch (type) { + case S_IFBLK: + case S_IFCHR: + data_len = offsetof(INTX_FILE, device_end); + data = ntfs_malloc(data_len); + if (!data) { + err = errno; + goto err_out; + } + data->major = cpu_to_le64(major(dev)); + data->minor = cpu_to_le64(minor(dev)); + if (type == S_IFBLK) + data->magic = INTX_BLOCK_DEVICE; + if (type == S_IFCHR) + data->magic = INTX_CHARACTER_DEVICE; + break; + case S_IFLNK: + data_len = sizeof(INTX_FILE_TYPES) + + target_len * sizeof(ntfschar); + data = ntfs_malloc(data_len); + if (!data) { + err = errno; + goto err_out; + } + data->magic = INTX_SYMBOLIC_LINK; + memcpy(data->target, target, + target_len * sizeof(ntfschar)); + break; + case S_IFSOCK: + data = NULL; + data_len = 1; + break; + default: /* FIFO or regular file. */ + data = NULL; + data_len = 0; + break; + } + /* Add DATA attribute to inode. */ + if (ntfs_attr_add(ni, AT_DATA, AT_UNNAMED, 0, (u8*)data, + data_len)) { + err = errno; + ntfs_log_error("Failed to add DATA attribute.\n"); + free(data); + goto err_out; + } + rollback_data = 1; + free(data); + } + /* Create FILE_NAME attribute. */ + fn_len = sizeof(FILE_NAME_ATTR) + name_len * sizeof(ntfschar); + fn = ntfs_calloc(fn_len); + if (!fn) { + err = errno; + goto err_out; + } + fn->parent_directory = MK_LE_MREF(dir_ni->mft_no, + le16_to_cpu(dir_ni->mrec->sequence_number)); + fn->file_name_length = name_len; + fn->file_name_type = FILE_NAME_POSIX; + if (S_ISDIR(type)) + fn->file_attributes = FILE_ATTR_I30_INDEX_PRESENT; + if (!S_ISREG(type) && !S_ISDIR(type)) + fn->file_attributes = FILE_ATTR_SYSTEM; + else + fn->file_attributes |= ni->flags & FILE_ATTR_COMPRESSED; + fn->file_attributes |= FILE_ATTR_ARCHIVE; + fn->file_attributes |= ni->flags & FILE_ATTR_HIDDEN; + fn->creation_time = ni->creation_time; + fn->last_data_change_time = ni->last_data_change_time; + fn->last_mft_change_time = ni->last_mft_change_time; + fn->last_access_time = ni->last_access_time; + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + fn->data_size = fn->allocated_size = const_cpu_to_le64(0); + else { + fn->data_size = cpu_to_sle64(ni->data_size); + fn->allocated_size = cpu_to_sle64(ni->allocated_size); + } + memcpy(fn->file_name, name, name_len * sizeof(ntfschar)); + /* Add FILE_NAME attribute to inode. */ + if (ntfs_attr_add(ni, AT_FILE_NAME, AT_UNNAMED, 0, (u8*)fn, fn_len)) { + err = errno; + ntfs_log_error("Failed to add FILE_NAME attribute.\n"); + goto err_out; + } + /* Add FILE_NAME attribute to index. */ + if (ntfs_index_add_filename(dir_ni, fn, MK_MREF(ni->mft_no, + le16_to_cpu(ni->mrec->sequence_number)))) { + err = errno; + ntfs_log_perror("Failed to add entry to the index"); + goto err_out; + } + /* Set hard links count and directory flag. */ + ni->mrec->link_count = cpu_to_le16(1); + if (S_ISDIR(type)) + ni->mrec->flags |= MFT_RECORD_IS_DIRECTORY; + ntfs_inode_mark_dirty(ni); + /* Done! */ + free(fn); + free(si); + ntfs_log_trace("Done.\n"); + return ni; err_out: - ntfs_log_trace( "Failed.\n" ); + ntfs_log_trace("Failed.\n"); - if ( rollback_sd ) - ntfs_attr_remove( ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0 ); - - if ( rollback_data ) - ntfs_attr_remove( ni, AT_DATA, AT_UNNAMED, 0 ); - /* - * Free extent MFT records (should not exist any with current - * ntfs_create implementation, but for any case if something will be - * changed in the future). - */ - while ( ni->nr_extents ) - if ( ntfs_mft_record_free( ni->vol, *( ni->extent_nis ) ) ) - { - err = errno; - ntfs_log_error( "Failed to free extent MFT record. " - "Leaving inconsistent metadata.\n" ); - } - if ( ntfs_mft_record_free( ni->vol, ni ) ) - ntfs_log_error( "Failed to free MFT record. " - "Leaving inconsistent metadata. Run chkdsk.\n" ); - free( fn ); - free( si ); - errno = err; - return NULL; + if (rollback_sd) + ntfs_attr_remove(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0); + + if (rollback_data) + ntfs_attr_remove(ni, AT_DATA, AT_UNNAMED, 0); + /* + * Free extent MFT records (should not exist any with current + * ntfs_create implementation, but for any case if something will be + * changed in the future). + */ + while (ni->nr_extents) + if (ntfs_mft_record_free(ni->vol, *(ni->extent_nis))) { + err = errno; + ntfs_log_error("Failed to free extent MFT record. " + "Leaving inconsistent metadata.\n"); + } + if (ntfs_mft_record_free(ni->vol, ni)) + ntfs_log_error("Failed to free MFT record. " + "Leaving inconsistent metadata. Run chkdsk.\n"); + free(fn); + free(si); + errno = err; + return NULL; } /** * Some wrappers around __ntfs_create() ... */ -ntfs_inode *ntfs_create( ntfs_inode *dir_ni, le32 securid, ntfschar *name, - u8 name_len, mode_t type ) +ntfs_inode *ntfs_create(ntfs_inode *dir_ni, le32 securid, ntfschar *name, + u8 name_len, mode_t type) { - if ( type != S_IFREG && type != S_IFDIR && type != S_IFIFO && - type != S_IFSOCK ) - { - ntfs_log_error( "Invalid arguments.\n" ); - return NULL; - } - return __ntfs_create( dir_ni, securid, name, name_len, type, 0, NULL, 0 ); + if (type != S_IFREG && type != S_IFDIR && type != S_IFIFO && + type != S_IFSOCK) { + ntfs_log_error("Invalid arguments.\n"); + return NULL; + } + return __ntfs_create(dir_ni, securid, name, name_len, type, 0, NULL, 0); } -ntfs_inode *ntfs_create_device( ntfs_inode *dir_ni, le32 securid, - ntfschar *name, u8 name_len, mode_t type, dev_t dev ) +ntfs_inode *ntfs_create_device(ntfs_inode *dir_ni, le32 securid, + ntfschar *name, u8 name_len, mode_t type, dev_t dev) { - if ( type != S_IFCHR && type != S_IFBLK ) - { - ntfs_log_error( "Invalid arguments.\n" ); - return NULL; - } - return __ntfs_create( dir_ni, securid, name, name_len, type, dev, NULL, 0 ); + if (type != S_IFCHR && type != S_IFBLK) { + ntfs_log_error("Invalid arguments.\n"); + return NULL; + } + return __ntfs_create(dir_ni, securid, name, name_len, type, dev, NULL, 0); } -ntfs_inode *ntfs_create_symlink( ntfs_inode *dir_ni, le32 securid, - ntfschar *name, u8 name_len, ntfschar *target, int target_len ) +ntfs_inode *ntfs_create_symlink(ntfs_inode *dir_ni, le32 securid, + ntfschar *name, u8 name_len, ntfschar *target, int target_len) { - if ( !target || !target_len ) - { - ntfs_log_error( "%s: Invalid argument (%p, %d)\n", __FUNCTION__, - target, target_len ); - return NULL; - } - return __ntfs_create( dir_ni, securid, name, name_len, S_IFLNK, 0, - target, target_len ); + if (!target || !target_len) { + ntfs_log_error("%s: Invalid argument (%p, %d)\n", __FUNCTION__, + target, target_len); + return NULL; + } + return __ntfs_create(dir_ni, securid, name, name_len, S_IFLNK, 0, + target, target_len); } -int ntfs_check_empty_dir( ntfs_inode *ni ) +int ntfs_check_empty_dir(ntfs_inode *ni) { - ntfs_attr *na; - int ret = 0; + ntfs_attr *na; + int ret = 0; + + if (!(ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) + return 0; - if ( !( ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ) ) - return 0; - - na = ntfs_attr_open( ni, AT_INDEX_ROOT, NTFS_INDEX_I30, 4 ); - if ( !na ) - { - errno = EIO; - ntfs_log_perror( "Failed to open directory" ); - return -1; - } - - /* Non-empty directory? */ - if ( ( na->data_size != sizeof( INDEX_ROOT ) + sizeof( INDEX_ENTRY_HEADER ) ) ) - { - /* Both ENOTEMPTY and EEXIST are ok. We use the more common. */ - errno = ENOTEMPTY; - ntfs_log_debug( "Directory is not empty\n" ); - ret = -1; - } - - ntfs_attr_close( na ); - return ret; + na = ntfs_attr_open(ni, AT_INDEX_ROOT, NTFS_INDEX_I30, 4); + if (!na) { + errno = EIO; + ntfs_log_perror("Failed to open directory"); + return -1; + } + + /* Non-empty directory? */ + if ((na->data_size != sizeof(INDEX_ROOT) + sizeof(INDEX_ENTRY_HEADER))){ + /* Both ENOTEMPTY and EEXIST are ok. We use the more common. */ + errno = ENOTEMPTY; + ntfs_log_debug("Directory is not empty\n"); + ret = -1; + } + + ntfs_attr_close(na); + return ret; } -static int ntfs_check_unlinkable_dir( ntfs_inode *ni, FILE_NAME_ATTR *fn ) +static int ntfs_check_unlinkable_dir(ntfs_inode *ni, FILE_NAME_ATTR *fn) { - int link_count = le16_to_cpu( ni->mrec->link_count ); - int ret; - - ret = ntfs_check_empty_dir( ni ); - if ( !ret || errno != ENOTEMPTY ) - return ret; - /* - * Directory is non-empty, so we can unlink only if there is more than - * one "real" hard link, i.e. links aren't different DOS and WIN32 names - */ - if ( ( link_count == 1 ) || - ( link_count == 2 && fn->file_name_type == FILE_NAME_DOS ) ) - { - errno = ENOTEMPTY; - ntfs_log_debug( "Non-empty directory without hard links\n" ); - goto no_hardlink; - } - - ret = 0; -no_hardlink: - return ret; + int link_count = le16_to_cpu(ni->mrec->link_count); + int ret; + + ret = ntfs_check_empty_dir(ni); + if (!ret || errno != ENOTEMPTY) + return ret; + /* + * Directory is non-empty, so we can unlink only if there is more than + * one "real" hard link, i.e. links aren't different DOS and WIN32 names + */ + if ((link_count == 1) || + (link_count == 2 && fn->file_name_type == FILE_NAME_DOS)) { + errno = ENOTEMPTY; + ntfs_log_debug("Non-empty directory without hard links\n"); + goto no_hardlink; + } + + ret = 0; +no_hardlink: + return ret; } /** * ntfs_delete - delete file or directory from ntfs volume - * @ni: ntfs inode for object to delte - * @dir_ni: ntfs inode for directory in which delete object - * @name: unicode name of the object to delete - * @name_len: length of the name in unicode characters + * @ni: ntfs inode for object to delte + * @dir_ni: ntfs inode for directory in which delete object + * @name: unicode name of the object to delete + * @name_len: length of the name in unicode characters * * @ni is always closed after the call to this function (even if it failed), * user does not need to call ntfs_inode_close himself. * * Return 0 on success or -1 on error with errno set to the error code. */ -int ntfs_delete( ntfs_volume *vol, const char *pathname, - ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, u8 name_len ) +int ntfs_delete(ntfs_volume *vol, const char *pathname, + ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, u8 name_len) { - ntfs_attr_search_ctx *actx = NULL; - FILE_NAME_ATTR *fn = NULL; - BOOL looking_for_dos_name = FALSE, looking_for_win32_name = FALSE; - BOOL case_sensitive_match = TRUE; - int err = 0; + ntfs_attr_search_ctx *actx = NULL; + FILE_NAME_ATTR *fn = NULL; + BOOL looking_for_dos_name = FALSE, looking_for_win32_name = FALSE; + BOOL case_sensitive_match = TRUE; + int err = 0; #if CACHE_NIDATA_SIZE - int i; + int i; #endif #if CACHE_INODE_SIZE - struct CACHED_INODE item; - const char *p; - u64 inum = ( u64 ) - 1; - int count; + struct CACHED_INODE item; + const char *p; + u64 inum = (u64)-1; + int count; #endif #if CACHE_LOOKUP_SIZE - struct CACHED_LOOKUP lkitem; + struct CACHED_LOOKUP lkitem; #endif - ntfs_log_trace( "Entering.\n" ); - - if ( !ni || !dir_ni || !name || !name_len ) - { - ntfs_log_error( "Invalid arguments.\n" ); - errno = EINVAL; - goto err_out; - } - if ( ni->nr_extents == -1 ) - ni = ni->base_ni; - if ( dir_ni->nr_extents == -1 ) - dir_ni = dir_ni->base_ni; - /* - * Search for FILE_NAME attribute with such name. If it's in POSIX or - * WIN32_AND_DOS namespace, then simply remove it from index and inode. - * If filename in DOS or in WIN32 namespace, then remove DOS name first, - * only then remove WIN32 name. - */ - actx = ntfs_attr_get_search_ctx( ni, NULL ); - if ( !actx ) - goto err_out; + ntfs_log_trace("Entering.\n"); + + if (!ni || !dir_ni || !name || !name_len) { + ntfs_log_error("Invalid arguments.\n"); + errno = EINVAL; + goto err_out; + } + if (ni->nr_extents == -1) + ni = ni->base_ni; + if (dir_ni->nr_extents == -1) + dir_ni = dir_ni->base_ni; + /* + * Search for FILE_NAME attribute with such name. If it's in POSIX or + * WIN32_AND_DOS namespace, then simply remove it from index and inode. + * If filename in DOS or in WIN32 namespace, then remove DOS name first, + * only then remove WIN32 name. + */ + actx = ntfs_attr_get_search_ctx(ni, NULL); + if (!actx) + goto err_out; search: - while ( !ntfs_attr_lookup( AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE, - 0, NULL, 0, actx ) ) - { - char *s; - BOOL case_sensitive = IGNORE_CASE; + while (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE, + 0, NULL, 0, actx)) { + char *s; + BOOL case_sensitive = IGNORE_CASE; - errno = 0; - fn = ( FILE_NAME_ATTR* )( ( u8* )actx->attr + - le16_to_cpu( actx->attr->value_offset ) ); - s = ntfs_attr_name_get( fn->file_name, fn->file_name_length ); - ntfs_log_trace( "name: '%s' type: %d dos: %d win32: %d " - "case: %d\n", s, fn->file_name_type, - looking_for_dos_name, looking_for_win32_name, - case_sensitive_match ); - ntfs_attr_name_free( &s ); - if ( looking_for_dos_name ) - { - if ( fn->file_name_type == FILE_NAME_DOS ) - break; - else - continue; - } - if ( looking_for_win32_name ) - { - if ( fn->file_name_type == FILE_NAME_WIN32 ) - break; - else - continue; - } - - /* Ignore hard links from other directories */ - if ( dir_ni->mft_no != MREF_LE( fn->parent_directory ) ) - { - ntfs_log_debug( "MFT record numbers don't match " - "(%llu != %llu)\n", - ( long long unsigned )dir_ni->mft_no, - ( long long unsigned )MREF_LE( fn->parent_directory ) ); - continue; - } - - if ( fn->file_name_type == FILE_NAME_POSIX || case_sensitive_match ) - case_sensitive = CASE_SENSITIVE; - - if ( ntfs_names_are_equal( fn->file_name, fn->file_name_length, - name, name_len, case_sensitive, - ni->vol->upcase, ni->vol->upcase_len ) ) - { - - if ( fn->file_name_type == FILE_NAME_WIN32 ) - { - looking_for_dos_name = TRUE; - ntfs_attr_reinit_search_ctx( actx ); - continue; - } - if ( fn->file_name_type == FILE_NAME_DOS ) - looking_for_dos_name = TRUE; - break; - } - } - if ( errno ) - { - /* - * If case sensitive search failed, then try once again - * ignoring case. - */ - if ( errno == ENOENT && case_sensitive_match ) - { - case_sensitive_match = FALSE; - ntfs_attr_reinit_search_ctx( actx ); - goto search; - } - goto err_out; - } - - if ( ntfs_check_unlinkable_dir( ni, fn ) < 0 ) - goto err_out; - - if ( ntfs_index_remove( dir_ni, ni, fn, le32_to_cpu( actx->attr->value_length ) ) ) - goto err_out; - - if ( ntfs_attr_record_rm( actx ) ) - goto err_out; - - ni->mrec->link_count = cpu_to_le16( le16_to_cpu( - ni->mrec->link_count ) - 1 ); - - ntfs_inode_mark_dirty( ni ); - if ( looking_for_dos_name ) - { - looking_for_dos_name = FALSE; - looking_for_win32_name = TRUE; - ntfs_attr_reinit_search_ctx( actx ); - goto search; - } - /* TODO: Update object id, quota and securiry indexes if required. */ - /* - * If hard link count is not equal to zero then we are done. In other - * case there are no reference to this inode left, so we should free all - * non-resident attributes and mark all MFT record as not in use. - */ + errno = 0; + fn = (FILE_NAME_ATTR*)((u8*)actx->attr + + le16_to_cpu(actx->attr->value_offset)); + s = ntfs_attr_name_get(fn->file_name, fn->file_name_length); + ntfs_log_trace("name: '%s' type: %d dos: %d win32: %d " + "case: %d\n", s, fn->file_name_type, + looking_for_dos_name, looking_for_win32_name, + case_sensitive_match); + ntfs_attr_name_free(&s); + if (looking_for_dos_name) { + if (fn->file_name_type == FILE_NAME_DOS) + break; + else + continue; + } + if (looking_for_win32_name) { + if (fn->file_name_type == FILE_NAME_WIN32) + break; + else + continue; + } + + /* Ignore hard links from other directories */ + if (dir_ni->mft_no != MREF_LE(fn->parent_directory)) { + ntfs_log_debug("MFT record numbers don't match " + "(%llu != %llu)\n", + (long long unsigned)dir_ni->mft_no, + (long long unsigned)MREF_LE(fn->parent_directory)); + continue; + } + + if (fn->file_name_type == FILE_NAME_POSIX || case_sensitive_match) + case_sensitive = CASE_SENSITIVE; + + if (ntfs_names_are_equal(fn->file_name, fn->file_name_length, + name, name_len, case_sensitive, + ni->vol->upcase, ni->vol->upcase_len)){ + + if (fn->file_name_type == FILE_NAME_WIN32) { + looking_for_dos_name = TRUE; + ntfs_attr_reinit_search_ctx(actx); + continue; + } + if (fn->file_name_type == FILE_NAME_DOS) + looking_for_dos_name = TRUE; + break; + } + } + if (errno) { + /* + * If case sensitive search failed, then try once again + * ignoring case. + */ + if (errno == ENOENT && case_sensitive_match) { + case_sensitive_match = FALSE; + ntfs_attr_reinit_search_ctx(actx); + goto search; + } + goto err_out; + } + + if (ntfs_check_unlinkable_dir(ni, fn) < 0) + goto err_out; + + if (ntfs_index_remove(dir_ni, ni, fn, le32_to_cpu(actx->attr->value_length))) + goto err_out; + + if (ntfs_attr_record_rm(actx)) + goto err_out; + + ni->mrec->link_count = cpu_to_le16(le16_to_cpu( + ni->mrec->link_count) - 1); + + ntfs_inode_mark_dirty(ni); + if (looking_for_dos_name) { + looking_for_dos_name = FALSE; + looking_for_win32_name = TRUE; + ntfs_attr_reinit_search_ctx(actx); + goto search; + } + /* TODO: Update object id, quota and securiry indexes if required. */ + /* + * If hard link count is not equal to zero then we are done. In other + * case there are no reference to this inode left, so we should free all + * non-resident attributes and mark all MFT record as not in use. + */ #if CACHE_LOOKUP_SIZE - /* invalidate entry in lookup cache */ - lkitem.name = ( const char* )NULL; - lkitem.namesize = 0; - lkitem.inum = ni->mft_no; - lkitem.parent = dir_ni->mft_no; - ntfs_invalidate_cache( vol->lookup_cache, GENERIC( &lkitem ), - lookup_cache_inv_compare, CACHE_NOHASH ); + /* invalidate entry in lookup cache */ + lkitem.name = (const char*)NULL; + lkitem.namesize = 0; + lkitem.inum = ni->mft_no; + lkitem.parent = dir_ni->mft_no; + ntfs_invalidate_cache(vol->lookup_cache, GENERIC(&lkitem), + lookup_cache_inv_compare, CACHE_NOHASH); #endif #if CACHE_INODE_SIZE - inum = ni->mft_no; - if ( pathname ) - { - /* invalide cache entry, even if there was an error */ - /* Remove leading /'s. */ - p = pathname; - while ( *p == PATH_SEP ) - p++; - if ( p[0] && ( p[strlen( p )-1] == PATH_SEP ) ) - ntfs_log_error( "Unnormalized path %s\n", pathname ); - item.pathname = p; - item.varsize = strlen( p ); - } - else - { - item.pathname = ( const char* )NULL; - item.varsize = 0; - } - item.inum = inum; - count = ntfs_invalidate_cache( vol->xinode_cache, GENERIC( &item ), - inode_cache_inv_compare, CACHE_NOHASH ); - if ( pathname && !count ) - ntfs_log_error( "Could not delete inode cache entry for %s\n", - pathname ); + inum = ni->mft_no; + if (pathname) { + /* invalide cache entry, even if there was an error */ + /* Remove leading /'s. */ + p = pathname; + while (*p == PATH_SEP) + p++; + if (p[0] && (p[strlen(p)-1] == PATH_SEP)) + ntfs_log_error("Unnormalized path %s\n",pathname); + item.pathname = p; + item.varsize = strlen(p); + } else { + item.pathname = (const char*)NULL; + item.varsize = 0; + } + item.inum = inum; + count = ntfs_invalidate_cache(vol->xinode_cache, GENERIC(&item), + inode_cache_inv_compare, CACHE_NOHASH); + if (pathname && !count) + ntfs_log_error("Could not delete inode cache entry for %s\n", + pathname); #endif - if ( ni->mrec->link_count ) - { - ntfs_inode_update_times( ni, NTFS_UPDATE_CTIME ); - goto ok; - } - if ( ntfs_delete_reparse_index( ni ) ) - { - /* - * Failed to remove the reparse index : proceed anyway - * This is not a critical error, the entry is useless - * because of sequence_number, and stopping file deletion - * would be much worse as the file is not referenced now. - */ - err = errno; - } - if ( ntfs_delete_object_id_index( ni ) ) - { - /* - * Failed to remove the object id index : proceed anyway - * This is not a critical error. - */ - err = errno; - } - ntfs_attr_reinit_search_ctx( actx ); - while ( !ntfs_attrs_walk( actx ) ) - { - if ( actx->attr->non_resident ) - { - runlist *rl; + if (ni->mrec->link_count) { + ntfs_inode_update_times(ni, NTFS_UPDATE_CTIME); + goto ok; + } + if (ntfs_delete_reparse_index(ni)) { + /* + * Failed to remove the reparse index : proceed anyway + * This is not a critical error, the entry is useless + * because of sequence_number, and stopping file deletion + * would be much worse as the file is not referenced now. + */ + err = errno; + } + if (ntfs_delete_object_id_index(ni)) { + /* + * Failed to remove the object id index : proceed anyway + * This is not a critical error. + */ + err = errno; + } + ntfs_attr_reinit_search_ctx(actx); + while (!ntfs_attrs_walk(actx)) { + if (actx->attr->non_resident) { + runlist *rl; - rl = ntfs_mapping_pairs_decompress( ni->vol, actx->attr, - NULL ); - if ( !rl ) - { - err = errno; - ntfs_log_error( "Failed to decompress runlist. " - "Leaving inconsistent metadata.\n" ); - continue; - } - if ( ntfs_cluster_free_from_rl( ni->vol, rl ) ) - { - err = errno; - ntfs_log_error( "Failed to free clusters. " - "Leaving inconsistent metadata.\n" ); - continue; - } - free( rl ); - } - } - if ( errno != ENOENT ) - { - err = errno; - ntfs_log_error( "Attribute enumeration failed. " - "Probably leaving inconsistent metadata.\n" ); - } - /* All extents should be attached after attribute walk. */ + rl = ntfs_mapping_pairs_decompress(ni->vol, actx->attr, + NULL); + if (!rl) { + err = errno; + ntfs_log_error("Failed to decompress runlist. " + "Leaving inconsistent metadata.\n"); + continue; + } + if (ntfs_cluster_free_from_rl(ni->vol, rl)) { + err = errno; + ntfs_log_error("Failed to free clusters. " + "Leaving inconsistent metadata.\n"); + continue; + } + free(rl); + } + } + if (errno != ENOENT) { + err = errno; + ntfs_log_error("Attribute enumeration failed. " + "Probably leaving inconsistent metadata.\n"); + } + /* All extents should be attached after attribute walk. */ #if CACHE_NIDATA_SIZE - /* - * Disconnect extents before deleting them, so they are - * not wrongly moved to cache through the chainings - */ - for ( i = ni->nr_extents - 1; i >= 0; i-- ) - { - ni->extent_nis[i]->base_ni = ( ntfs_inode* )NULL; - ni->extent_nis[i]->nr_extents = 0; - if ( ntfs_mft_record_free( ni->vol, ni->extent_nis[i] ) ) - { - err = errno; - ntfs_log_error( "Failed to free extent MFT record. " - "Leaving inconsistent metadata.\n" ); - } - } - free( ni->extent_nis ); - ni->nr_extents = 0; - ni->extent_nis = ( ntfs_inode** )NULL; + /* + * Disconnect extents before deleting them, so they are + * not wrongly moved to cache through the chainings + */ + for (i=ni->nr_extents-1; i>=0; i--) { + ni->extent_nis[i]->base_ni = (ntfs_inode*)NULL; + ni->extent_nis[i]->nr_extents = 0; + if (ntfs_mft_record_free(ni->vol, ni->extent_nis[i])) { + err = errno; + ntfs_log_error("Failed to free extent MFT record. " + "Leaving inconsistent metadata.\n"); + } + } + free(ni->extent_nis); + ni->nr_extents = 0; + ni->extent_nis = (ntfs_inode**)NULL; #else - while ( ni->nr_extents ) - if ( ntfs_mft_record_free( ni->vol, *( ni->extent_nis ) ) ) - { - err = errno; - ntfs_log_error( "Failed to free extent MFT record. " - "Leaving inconsistent metadata.\n" ); - } + while (ni->nr_extents) + if (ntfs_mft_record_free(ni->vol, *(ni->extent_nis))) { + err = errno; + ntfs_log_error("Failed to free extent MFT record. " + "Leaving inconsistent metadata.\n"); + } #endif - if ( ntfs_mft_record_free( ni->vol, ni ) ) - { - err = errno; - ntfs_log_error( "Failed to free base MFT record. " - "Leaving inconsistent metadata.\n" ); - } - ni = NULL; -ok: - ntfs_inode_update_times( dir_ni, NTFS_UPDATE_MCTIME ); + if (ntfs_mft_record_free(ni->vol, ni)) { + err = errno; + ntfs_log_error("Failed to free base MFT record. " + "Leaving inconsistent metadata.\n"); + } + ni = NULL; +ok: + ntfs_inode_update_times(dir_ni, NTFS_UPDATE_MCTIME); out: - if ( actx ) - ntfs_attr_put_search_ctx( actx ); - if ( ntfs_inode_close( dir_ni ) && !err ) - err = errno; - if ( ntfs_inode_close( ni ) && !err ) - err = errno; - if ( err ) - { - errno = err; - ntfs_log_debug( "Could not delete file: %s\n", strerror( errno ) ); - return -1; - } - ntfs_log_trace( "Done.\n" ); - return 0; + if (actx) + ntfs_attr_put_search_ctx(actx); + if (ntfs_inode_close(dir_ni) && !err) + err = errno; + if (ntfs_inode_close(ni) && !err) + err = errno; + if (err) { + errno = err; + ntfs_log_debug("Could not delete file: %s\n", strerror(errno)); + return -1; + } + ntfs_log_trace("Done.\n"); + return 0; err_out: - err = errno; - goto out; + err = errno; + goto out; } /** * ntfs_link - create hard link for file or directory - * @ni: ntfs inode for object to create hard link - * @dir_ni: ntfs inode for directory in which new link should be placed - * @name: unicode name of the new link - * @name_len: length of the name in unicode characters + * @ni: ntfs inode for object to create hard link + * @dir_ni: ntfs inode for directory in which new link should be placed + * @name: unicode name of the new link + * @name_len: length of the name in unicode characters * * NOTE: At present we allow creating hardlinks to directories, we use them * in a temporary state during rename. But it's defenitely bad idea to have @@ -2216,374 +2047,344 @@ err_out: * * Return 0 on success or -1 on error with errno set to the error code. */ -static int ntfs_link_i( ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, - u8 name_len, FILE_NAME_TYPE_FLAGS nametype ) +static int ntfs_link_i(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, + u8 name_len, FILE_NAME_TYPE_FLAGS nametype) { - FILE_NAME_ATTR *fn = NULL; - int fn_len, err; + FILE_NAME_ATTR *fn = NULL; + int fn_len, err; - ntfs_log_trace( "Entering.\n" ); - - if ( !ni || !dir_ni || !name || !name_len || - ni->mft_no == dir_ni->mft_no ) - { - err = EINVAL; - ntfs_log_perror( "ntfs_link wrong arguments" ); - goto err_out; - } - - if ( ( ni->flags & FILE_ATTR_REPARSE_POINT ) - && !ntfs_possible_symlink( ni ) ) - { - err = EOPNOTSUPP; - goto err_out; - } - - /* Create FILE_NAME attribute. */ - fn_len = sizeof( FILE_NAME_ATTR ) + name_len * sizeof( ntfschar ); - fn = ntfs_calloc( fn_len ); - if ( !fn ) - { - err = errno; - goto err_out; - } - fn->parent_directory = MK_LE_MREF( dir_ni->mft_no, - le16_to_cpu( dir_ni->mrec->sequence_number ) ); - fn->file_name_length = name_len; - fn->file_name_type = nametype; - fn->file_attributes = ni->flags; - if ( ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ) - { - fn->file_attributes |= FILE_ATTR_I30_INDEX_PRESENT; - fn->data_size = fn->allocated_size = const_cpu_to_le64( 0 ); - } - else - { - fn->allocated_size = cpu_to_sle64( ni->allocated_size ); - fn->data_size = cpu_to_sle64( ni->data_size ); - } - fn->creation_time = ni->creation_time; - fn->last_data_change_time = ni->last_data_change_time; - fn->last_mft_change_time = ni->last_mft_change_time; - fn->last_access_time = ni->last_access_time; - memcpy( fn->file_name, name, name_len * sizeof( ntfschar ) ); - /* Add FILE_NAME attribute to index. */ - if ( ntfs_index_add_filename( dir_ni, fn, MK_MREF( ni->mft_no, - le16_to_cpu( ni->mrec->sequence_number ) ) ) ) - { - err = errno; - ntfs_log_perror( "Failed to add filename to the index" ); - goto err_out; - } - /* Add FILE_NAME attribute to inode. */ - if ( ntfs_attr_add( ni, AT_FILE_NAME, AT_UNNAMED, 0, ( u8* )fn, fn_len ) ) - { - ntfs_log_error( "Failed to add FILE_NAME attribute.\n" ); - err = errno; - /* Try to remove just added attribute from index. */ - if ( ntfs_index_remove( dir_ni, ni, fn, fn_len ) ) - goto rollback_failed; - goto err_out; - } - /* Increment hard links count. */ - ni->mrec->link_count = cpu_to_le16( le16_to_cpu( - ni->mrec->link_count ) + 1 ); - /* Done! */ - ntfs_inode_mark_dirty( ni ); - free( fn ); - ntfs_log_trace( "Done.\n" ); - return 0; + ntfs_log_trace("Entering.\n"); + + if (!ni || !dir_ni || !name || !name_len || + ni->mft_no == dir_ni->mft_no) { + err = EINVAL; + ntfs_log_perror("ntfs_link wrong arguments"); + goto err_out; + } + + if ((ni->flags & FILE_ATTR_REPARSE_POINT) + && !ntfs_possible_symlink(ni)) { + err = EOPNOTSUPP; + goto err_out; + } + + /* Create FILE_NAME attribute. */ + fn_len = sizeof(FILE_NAME_ATTR) + name_len * sizeof(ntfschar); + fn = ntfs_calloc(fn_len); + if (!fn) { + err = errno; + goto err_out; + } + fn->parent_directory = MK_LE_MREF(dir_ni->mft_no, + le16_to_cpu(dir_ni->mrec->sequence_number)); + fn->file_name_length = name_len; + fn->file_name_type = nametype; + fn->file_attributes = ni->flags; + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + fn->file_attributes |= FILE_ATTR_I30_INDEX_PRESENT; + fn->data_size = fn->allocated_size = const_cpu_to_le64(0); + } else { + fn->allocated_size = cpu_to_sle64(ni->allocated_size); + fn->data_size = cpu_to_sle64(ni->data_size); + } + fn->creation_time = ni->creation_time; + fn->last_data_change_time = ni->last_data_change_time; + fn->last_mft_change_time = ni->last_mft_change_time; + fn->last_access_time = ni->last_access_time; + memcpy(fn->file_name, name, name_len * sizeof(ntfschar)); + /* Add FILE_NAME attribute to index. */ + if (ntfs_index_add_filename(dir_ni, fn, MK_MREF(ni->mft_no, + le16_to_cpu(ni->mrec->sequence_number)))) { + err = errno; + ntfs_log_perror("Failed to add filename to the index"); + goto err_out; + } + /* Add FILE_NAME attribute to inode. */ + if (ntfs_attr_add(ni, AT_FILE_NAME, AT_UNNAMED, 0, (u8*)fn, fn_len)) { + ntfs_log_error("Failed to add FILE_NAME attribute.\n"); + err = errno; + /* Try to remove just added attribute from index. */ + if (ntfs_index_remove(dir_ni, ni, fn, fn_len)) + goto rollback_failed; + goto err_out; + } + /* Increment hard links count. */ + ni->mrec->link_count = cpu_to_le16(le16_to_cpu( + ni->mrec->link_count) + 1); + /* Done! */ + ntfs_inode_mark_dirty(ni); + free(fn); + ntfs_log_trace("Done.\n"); + return 0; rollback_failed: - ntfs_log_error( "Rollback failed. Leaving inconsistent metadata.\n" ); + ntfs_log_error("Rollback failed. Leaving inconsistent metadata.\n"); err_out: - free( fn ); - errno = err; - return -1; + free(fn); + errno = err; + return -1; } -int ntfs_link( ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, u8 name_len ) +int ntfs_link(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, u8 name_len) { - return ( ntfs_link_i( ni, dir_ni, name, name_len, FILE_NAME_POSIX ) ); + return (ntfs_link_i(ni, dir_ni, name, name_len, FILE_NAME_POSIX)); } /* - * Get a parent directory from an inode entry + * Get a parent directory from an inode entry * - * This is only used in situations where the path used to access - * the current file is not known for sure. The result may be different - * from the path when the file is linked in several parent directories. + * This is only used in situations where the path used to access + * the current file is not known for sure. The result may be different + * from the path when the file is linked in several parent directories. * - * Currently this is only used for translating ".." in the target - * of a Vista relative symbolic link + * Currently this is only used for translating ".." in the target + * of a Vista relative symbolic link */ -ntfs_inode *ntfs_dir_parent_inode( ntfs_inode *ni ) +ntfs_inode *ntfs_dir_parent_inode(ntfs_inode *ni) { - ntfs_inode *dir_ni = ( ntfs_inode* )NULL; - u64 inum; - FILE_NAME_ATTR *fn; - ntfs_attr_search_ctx *ctx; + ntfs_inode *dir_ni = (ntfs_inode*)NULL; + u64 inum; + FILE_NAME_ATTR *fn; + ntfs_attr_search_ctx *ctx; - if ( ni->mft_no != FILE_root ) - { - /* find the name in the attributes */ - ctx = ntfs_attr_get_search_ctx( ni, NULL ); - if ( !ctx ) - return ( ( ntfs_inode* )NULL ); + if (ni->mft_no != FILE_root) { + /* find the name in the attributes */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return ((ntfs_inode*)NULL); - if ( !ntfs_attr_lookup( AT_FILE_NAME, AT_UNNAMED, 0, - CASE_SENSITIVE, 0, NULL, 0, ctx ) ) - { - /* We know this will always be resident. */ - fn = ( FILE_NAME_ATTR* )( ( u8* )ctx->attr + - le16_to_cpu( ctx->attr->value_offset ) ); - inum = le64_to_cpu( fn->parent_directory ); - if ( inum != ( u64 ) - 1 ) - { - dir_ni = ntfs_inode_open( ni->vol, MREF( inum ) ); - } - } - ntfs_attr_put_search_ctx( ctx ); - } - return ( dir_ni ); + if (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + /* We know this will always be resident. */ + fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + inum = le64_to_cpu(fn->parent_directory); + if (inum != (u64)-1) { + dir_ni = ntfs_inode_open(ni->vol, MREF(inum)); + } + } + ntfs_attr_put_search_ctx(ctx); + } + return (dir_ni); } #ifdef HAVE_SETXATTR -#define MAX_DOS_NAME_LENGTH 12 +#define MAX_DOS_NAME_LENGTH 12 /* - * Get a DOS name for a file in designated directory + * Get a DOS name for a file in designated directory * - * Returns size if found - * 0 if not found - * -1 if there was an error (described by errno) + * Returns size if found + * 0 if not found + * -1 if there was an error (described by errno) */ -static int get_dos_name( ntfs_inode *ni, u64 dnum, ntfschar *dosname ) +static int get_dos_name(ntfs_inode *ni, u64 dnum, ntfschar *dosname) { - size_t outsize = 0; - FILE_NAME_ATTR *fn; - ntfs_attr_search_ctx *ctx; + size_t outsize = 0; + FILE_NAME_ATTR *fn; + ntfs_attr_search_ctx *ctx; - /* find the name in the attributes */ - ctx = ntfs_attr_get_search_ctx( ni, NULL ); - if ( !ctx ) - return -1; + /* find the name in the attributes */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return -1; - while ( !ntfs_attr_lookup( AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE, - 0, NULL, 0, ctx ) ) - { - /* We know this will always be resident. */ - fn = ( FILE_NAME_ATTR* )( ( u8* )ctx->attr + - le16_to_cpu( ctx->attr->value_offset ) ); + while (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + /* We know this will always be resident. */ + fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); - if ( ( fn->file_name_type & FILE_NAME_DOS ) - && ( MREF_LE( fn->parent_directory ) == dnum ) ) - { - /* - * Found a DOS or WIN32+DOS name for the entry - * copy name, after truncation for safety - */ - outsize = fn->file_name_length; - /* TODO : reject if name is too long ? */ - if ( outsize > MAX_DOS_NAME_LENGTH ) - outsize = MAX_DOS_NAME_LENGTH; - memcpy( dosname, fn->file_name, outsize*sizeof( ntfschar ) ); - } - } - ntfs_attr_put_search_ctx( ctx ); - return ( outsize ); + if ((fn->file_name_type & FILE_NAME_DOS) + && (MREF_LE(fn->parent_directory) == dnum)) { + /* + * Found a DOS or WIN32+DOS name for the entry + * copy name, after truncation for safety + */ + outsize = fn->file_name_length; +/* TODO : reject if name is too long ? */ + if (outsize > MAX_DOS_NAME_LENGTH) + outsize = MAX_DOS_NAME_LENGTH; + memcpy(dosname,fn->file_name,outsize*sizeof(ntfschar)); + } + } + ntfs_attr_put_search_ctx(ctx); + return (outsize); } /* - * Get a long name for a file in designated directory + * Get a long name for a file in designated directory * - * Returns size if found - * 0 if not found - * -1 if there was an error (described by errno) + * Returns size if found + * 0 if not found + * -1 if there was an error (described by errno) */ -static int get_long_name( ntfs_inode *ni, u64 dnum, ntfschar *longname ) +static int get_long_name(ntfs_inode *ni, u64 dnum, ntfschar *longname) { - size_t outsize = 0; - FILE_NAME_ATTR *fn; - ntfs_attr_search_ctx *ctx; + size_t outsize = 0; + FILE_NAME_ATTR *fn; + ntfs_attr_search_ctx *ctx; - /* find the name in the attributes */ - ctx = ntfs_attr_get_search_ctx( ni, NULL ); - if ( !ctx ) - return -1; + /* find the name in the attributes */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return -1; - /* first search for WIN32 or DOS+WIN32 names */ - while ( !ntfs_attr_lookup( AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE, - 0, NULL, 0, ctx ) ) - { - /* We know this will always be resident. */ - fn = ( FILE_NAME_ATTR* )( ( u8* )ctx->attr + - le16_to_cpu( ctx->attr->value_offset ) ); + /* first search for WIN32 or DOS+WIN32 names */ + while (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + /* We know this will always be resident. */ + fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); - if ( ( fn->file_name_type & FILE_NAME_WIN32 ) - && ( MREF_LE( fn->parent_directory ) == dnum ) ) - { - /* - * Found a WIN32 or WIN32+DOS name for the entry - * copy name - */ - outsize = fn->file_name_length; - memcpy( longname, fn->file_name, outsize*sizeof( ntfschar ) ); - } - } - /* if not found search for POSIX names */ - if ( !outsize ) - { - ntfs_attr_reinit_search_ctx( ctx ); - while ( !ntfs_attr_lookup( AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE, - 0, NULL, 0, ctx ) ) - { - /* We know this will always be resident. */ - fn = ( FILE_NAME_ATTR* )( ( u8* )ctx->attr + - le16_to_cpu( ctx->attr->value_offset ) ); + if ((fn->file_name_type & FILE_NAME_WIN32) + && (MREF_LE(fn->parent_directory) == dnum)) { + /* + * Found a WIN32 or WIN32+DOS name for the entry + * copy name + */ + outsize = fn->file_name_length; + memcpy(longname,fn->file_name,outsize*sizeof(ntfschar)); + } + } + /* if not found search for POSIX names */ + if (!outsize) { + ntfs_attr_reinit_search_ctx(ctx); + while (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + /* We know this will always be resident. */ + fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); - if ( ( fn->file_name_type == FILE_NAME_POSIX ) - && ( MREF_LE( fn->parent_directory ) == dnum ) ) - { - /* - * Found a POSIX name for the entry - * copy name - */ - outsize = fn->file_name_length; - memcpy( longname, fn->file_name, outsize*sizeof( ntfschar ) ); - } - } - } - ntfs_attr_put_search_ctx( ctx ); - return ( outsize ); + if ((fn->file_name_type == FILE_NAME_POSIX) + && (MREF_LE(fn->parent_directory) == dnum)) { + /* + * Found a POSIX name for the entry + * copy name + */ + outsize = fn->file_name_length; + memcpy(longname,fn->file_name,outsize*sizeof(ntfschar)); + } + } + } + ntfs_attr_put_search_ctx(ctx); + return (outsize); } /* - * Get the ntfs DOS name into an extended attribute + * Get the ntfs DOS name into an extended attribute */ -int ntfs_get_ntfs_dos_name( ntfs_inode *ni, ntfs_inode *dir_ni, - char *value, size_t size ) +int ntfs_get_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, + char *value, size_t size) { - int outsize = 0; - char *outname = ( char* )NULL; - u64 dnum; - int doslen; - ntfschar dosname[MAX_DOS_NAME_LENGTH]; + int outsize = 0; + char *outname = (char*)NULL; + u64 dnum; + int doslen; + ntfschar dosname[MAX_DOS_NAME_LENGTH]; - dnum = dir_ni->mft_no; - doslen = get_dos_name( ni, dnum, dosname ); - if ( doslen > 0 ) - { - /* - * Found a DOS name for the entry, make - * uppercase and encode into the buffer - * if there is enough space - */ - ntfs_name_upcase( dosname, doslen, - ni->vol->upcase, ni->vol->upcase_len ); - if ( ntfs_ucstombs( dosname, doslen, &outname, size ) < 0 ) - { - ntfs_log_error( "Cannot represent dosname in current locale.\n" ); - outsize = -errno; - } - else - { - outsize = strlen( outname ); - if ( value && ( outsize <= ( int )size ) ) - memcpy( value, outname, outsize ); - else if ( size && ( outsize > ( int )size ) ) - outsize = -ERANGE; - free( outname ); - } - } - else - { - if ( doslen == 0 ) - errno = ENODATA; - outsize = -errno; - } - return ( outsize ); + dnum = dir_ni->mft_no; + doslen = get_dos_name(ni, dnum, dosname); + if (doslen > 0) { + /* + * Found a DOS name for the entry, make + * uppercase and encode into the buffer + * if there is enough space + */ + ntfs_name_upcase(dosname, doslen, + ni->vol->upcase, ni->vol->upcase_len); + if (ntfs_ucstombs(dosname, doslen, &outname, size) < 0) { + ntfs_log_error("Cannot represent dosname in current locale.\n"); + outsize = -errno; + } else { + outsize = strlen(outname); + if (value && (outsize <= (int)size)) + memcpy(value, outname, outsize); + else + if (size && (outsize > (int)size)) + outsize = -ERANGE; + free(outname); + } + } else { + if (doslen == 0) + errno = ENODATA; + outsize = -errno; + } + return (outsize); } /* - * Change the name space of an existing file or directory + * Change the name space of an existing file or directory * - * Returns the old namespace if successful - * -1 if an error occurred (described by errno) + * Returns the old namespace if successful + * -1 if an error occurred (described by errno) */ -static int set_namespace( ntfs_inode *ni, ntfs_inode *dir_ni, - ntfschar *name, int len, - FILE_NAME_TYPE_FLAGS nametype ) +static int set_namespace(ntfs_inode *ni, ntfs_inode *dir_ni, + ntfschar *name, int len, + FILE_NAME_TYPE_FLAGS nametype) { - ntfs_attr_search_ctx *actx; - ntfs_index_context *icx; - FILE_NAME_ATTR *fnx; - FILE_NAME_ATTR *fn = NULL; - BOOL found; - int lkup; - int ret; + ntfs_attr_search_ctx *actx; + ntfs_index_context *icx; + FILE_NAME_ATTR *fnx; + FILE_NAME_ATTR *fn = NULL; + BOOL found; + int lkup; + int ret; - ret = -1; - actx = ntfs_attr_get_search_ctx( ni, NULL ); - if ( actx ) - { - found = FALSE; - do - { - lkup = ntfs_attr_lookup( AT_FILE_NAME, AT_UNNAMED, 0, - CASE_SENSITIVE, 0, NULL, 0, actx ); - if ( !lkup ) - { - fn = ( FILE_NAME_ATTR* )( ( u8* )actx->attr + - le16_to_cpu( actx->attr->value_offset ) ); - found = ( MREF_LE( fn->parent_directory ) - == dir_ni->mft_no ) - && !memcmp( fn->file_name, name, - len * sizeof( ntfschar ) ); - } - } - while ( !lkup && !found ); - if ( found ) - { - icx = ntfs_index_ctx_get( dir_ni, NTFS_INDEX_I30, 4 ); - if ( icx ) - { - lkup = ntfs_index_lookup( ( char* )fn, len, icx ); - if ( !lkup && icx->data && icx->data_len ) - { - fnx = ( FILE_NAME_ATTR* )icx->data; - ret = fn->file_name_type; - fn->file_name_type = nametype; - fnx->file_name_type = nametype; - ntfs_inode_mark_dirty( ni ); - ntfs_index_entry_mark_dirty( icx ); - } - ntfs_index_ctx_put( icx ); - } - } - ntfs_attr_put_search_ctx( actx ); - } - return ( ret ); + ret = -1; + actx = ntfs_attr_get_search_ctx(ni, NULL); + if (actx) { + found = FALSE; + do { + lkup = ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, + CASE_SENSITIVE, 0, NULL, 0, actx); + if (!lkup) { + fn = (FILE_NAME_ATTR*)((u8*)actx->attr + + le16_to_cpu(actx->attr->value_offset)); + found = (MREF_LE(fn->parent_directory) + == dir_ni->mft_no) + && !memcmp(fn->file_name, name, + len*sizeof(ntfschar)); + } + } while (!lkup && !found); + if (found) { + icx = ntfs_index_ctx_get(dir_ni, NTFS_INDEX_I30, 4); + if (icx) { + lkup = ntfs_index_lookup((char*)fn, len, icx); + if (!lkup && icx->data && icx->data_len) { + fnx = (FILE_NAME_ATTR*)icx->data; + ret = fn->file_name_type; + fn->file_name_type = nametype; + fnx->file_name_type = nametype; + ntfs_inode_mark_dirty(ni); + ntfs_index_entry_mark_dirty(icx); + } + ntfs_index_ctx_put(icx); + } + } + ntfs_attr_put_search_ctx(actx); + } + return (ret); } /* - * Set a DOS name to a file and adjust name spaces + * Set a DOS name to a file and adjust name spaces * - * If the new names are collapsible (same uppercased chars) : + * If the new names are collapsible (same uppercased chars) : * * - the existing DOS name or DOS+Win32 name is made Posix * - if it was a real DOS name, the existing long name is made DOS+Win32 * and the existing DOS name is deleted * - finally the existing long name is made DOS+Win32 unless already done * - * If the new names are not collapsible : + * If the new names are not collapsible : * * - insert the short name as a DOS name * - delete the old long name or existing short name @@ -2596,302 +2397,264 @@ static int set_namespace( ntfs_inode *ni, ntfs_inode *dir_ni, * The inodes of file and parent directory are always closed * * Returns 0 if successful - * -1 if failed + * -1 if failed */ -static int set_dos_name( ntfs_inode *ni, ntfs_inode *dir_ni, - ntfschar *shortname, int shortlen, - ntfschar *longname, int longlen, - ntfschar *deletename, int deletelen, BOOL existed ) +static int set_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, + ntfschar *shortname, int shortlen, + ntfschar *longname, int longlen, + ntfschar *deletename, int deletelen, BOOL existed) { - unsigned int linkcount; - ntfs_volume *vol; - BOOL collapsible; - BOOL deleted; - BOOL done; - FILE_NAME_TYPE_FLAGS oldnametype; - u64 dnum; - u64 fnum; - int res; + unsigned int linkcount; + ntfs_volume *vol; + BOOL collapsible; + BOOL deleted; + BOOL done; + FILE_NAME_TYPE_FLAGS oldnametype; + u64 dnum; + u64 fnum; + int res; - res = -1; - vol = ni->vol; - dnum = dir_ni->mft_no; - fnum = ni->mft_no; - /* save initial link count */ - linkcount = le16_to_cpu( ni->mrec->link_count ); + res = -1; + vol = ni->vol; + dnum = dir_ni->mft_no; + fnum = ni->mft_no; + /* save initial link count */ + linkcount = le16_to_cpu(ni->mrec->link_count); - /* check whether the same name may be used as DOS and WIN32 */ - collapsible = ntfs_collapsible_chars( ni->vol, shortname, shortlen, - longname, longlen ); - if ( collapsible ) - { - deleted = FALSE; - done = FALSE; - if ( existed ) - { - oldnametype = set_namespace( ni, dir_ni, deletename, - deletelen, FILE_NAME_POSIX ); - if ( oldnametype == FILE_NAME_DOS ) - { - if ( set_namespace( ni, dir_ni, longname, longlen, - FILE_NAME_WIN32_AND_DOS ) >= 0 ) - { - if ( !ntfs_delete( vol, - ( const char* )NULL, ni, dir_ni, - deletename, deletelen ) ) - res = 0; - deleted = TRUE; - } - else - done = TRUE; - } - } - if ( !deleted ) - { - if ( !done && ( set_namespace( ni, dir_ni, - longname, longlen, - FILE_NAME_WIN32_AND_DOS ) >= 0 ) ) - res = 0; - ntfs_inode_update_times( ni, NTFS_UPDATE_CTIME ); - ntfs_inode_update_times( dir_ni, NTFS_UPDATE_MCTIME ); - if ( ntfs_inode_close_in_dir( ni, dir_ni ) && !res ) - res = -1; - if ( ntfs_inode_close( dir_ni ) && !res ) - res = -1; - } - } - else - { - if ( !ntfs_link_i( ni, dir_ni, shortname, shortlen, - FILE_NAME_DOS ) - /* make sure a new link was recorded */ - && ( le16_to_cpu( ni->mrec->link_count ) > linkcount ) ) - { - /* delete the existing long name or short name */ + /* check whether the same name may be used as DOS and WIN32 */ + collapsible = ntfs_collapsible_chars(ni->vol, shortname, shortlen, + longname, longlen); + if (collapsible) { + deleted = FALSE; + done = FALSE; + if (existed) { + oldnametype = set_namespace(ni, dir_ni, deletename, + deletelen, FILE_NAME_POSIX); + if (oldnametype == FILE_NAME_DOS) { + if (set_namespace(ni, dir_ni, longname, longlen, + FILE_NAME_WIN32_AND_DOS) >= 0) { + if (!ntfs_delete(vol, + (const char*)NULL, ni, dir_ni, + deletename, deletelen)) + res = 0; + deleted = TRUE; + } else + done = TRUE; + } + } + if (!deleted) { + if (!done && (set_namespace(ni, dir_ni, + longname, longlen, + FILE_NAME_WIN32_AND_DOS) >= 0)) + res = 0; + ntfs_inode_update_times(ni, NTFS_UPDATE_CTIME); + ntfs_inode_update_times(dir_ni, NTFS_UPDATE_MCTIME); + if (ntfs_inode_close_in_dir(ni,dir_ni) && !res) + res = -1; + if (ntfs_inode_close(dir_ni) && !res) + res = -1; + } + } else { + if (!ntfs_link_i(ni, dir_ni, shortname, shortlen, + FILE_NAME_DOS) + /* make sure a new link was recorded */ + && (le16_to_cpu(ni->mrec->link_count) > linkcount)) { + /* delete the existing long name or short name */ // is it ok to not provide the path ? - if ( !ntfs_delete( vol, ( char* )NULL, ni, dir_ni, - deletename, deletelen ) ) - { - /* delete closes the inodes, so have to open again */ - dir_ni = ntfs_inode_open( vol, dnum ); - if ( dir_ni ) - { - ni = ntfs_inode_open( vol, fnum ); - if ( ni ) - { - if ( !ntfs_link_i( ni, dir_ni, - longname, longlen, - FILE_NAME_WIN32 ) ) - res = 0; - if ( ntfs_inode_close_in_dir( ni, - dir_ni ) - && !res ) - res = -1; - } - if ( ntfs_inode_close( dir_ni ) && !res ) - res = -1; - } - } - } - else - { - ntfs_inode_close_in_dir( ni, dir_ni ); - ntfs_inode_close( dir_ni ); - } - } - return ( res ); + if (!ntfs_delete(vol, (char*)NULL, ni, dir_ni, + deletename, deletelen)) { + /* delete closes the inodes, so have to open again */ + dir_ni = ntfs_inode_open(vol, dnum); + if (dir_ni) { + ni = ntfs_inode_open(vol, fnum); + if (ni) { + if (!ntfs_link_i(ni, dir_ni, + longname, longlen, + FILE_NAME_WIN32)) + res = 0; + if (ntfs_inode_close_in_dir(ni, + dir_ni) + && !res) + res = -1; + } + if (ntfs_inode_close(dir_ni) && !res) + res = -1; + } + } + } else { + ntfs_inode_close_in_dir(ni,dir_ni); + ntfs_inode_close(dir_ni); + } + } + return (res); } /* - * Set the ntfs DOS name into an extended attribute + * Set the ntfs DOS name into an extended attribute * * The DOS name will be added as another file name attribute * using the existing file name information from the original * name or overwriting the DOS Name if one exists. * - * The inode of the file is always closed + * The inode of the file is always closed */ -int ntfs_set_ntfs_dos_name( ntfs_inode *ni, ntfs_inode *dir_ni, - const char *value, size_t size, int flags ) +int ntfs_set_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, + const char *value, size_t size, int flags) { - int res = 0; - int longlen = 0; - int shortlen = 0; - char newname[MAX_DOS_NAME_LENGTH + 1]; - ntfschar oldname[MAX_DOS_NAME_LENGTH]; - int oldlen; - ntfs_volume *vol; - u64 fnum; - u64 dnum; - BOOL closed = FALSE; - ntfschar *shortname = NULL; - ntfschar longname[NTFS_MAX_NAME_LEN]; + int res = 0; + int longlen = 0; + int shortlen = 0; + char newname[MAX_DOS_NAME_LENGTH + 1]; + ntfschar oldname[MAX_DOS_NAME_LENGTH]; + int oldlen; + ntfs_volume *vol; + u64 fnum; + u64 dnum; + BOOL closed = FALSE; + ntfschar *shortname = NULL; + ntfschar longname[NTFS_MAX_NAME_LEN]; - vol = ni->vol; - fnum = ni->mft_no; - /* convert the string to the NTFS wide chars */ - if ( size > MAX_DOS_NAME_LENGTH ) - size = MAX_DOS_NAME_LENGTH; - strncpy( newname, value, size ); - newname[size] = 0; - shortlen = ntfs_mbstoucs( newname, &shortname ); - /* make sure the short name has valid chars */ - if ( ( shortlen < 0 ) || ntfs_forbidden_chars( shortname, shortlen ) ) - { - ntfs_inode_close_in_dir( ni, dir_ni ); - ntfs_inode_close( dir_ni ); - res = -errno; - return res; - } - dnum = dir_ni->mft_no; - longlen = get_long_name( ni, dnum, longname ); - if ( longlen > 0 ) - { - oldlen = get_dos_name( ni, dnum, oldname ); - if ( ( oldlen >= 0 ) - && !ntfs_forbidden_chars( longname, longlen ) ) - { - if ( oldlen > 0 ) - { - if ( flags & XATTR_CREATE ) - { - res = -1; - errno = EEXIST; - } - else if ( ( shortlen == oldlen ) - && !memcmp( shortname, oldname, - oldlen*sizeof( ntfschar ) ) ) - /* already set, done */ - res = 0; - else - { - res = set_dos_name( ni, dir_ni, - shortname, shortlen, - longname, longlen, - oldname, oldlen, TRUE ); - closed = TRUE; - } - } - else - { - if ( flags & XATTR_REPLACE ) - { - res = -1; - errno = ENODATA; - } - else - { - res = set_dos_name( ni, dir_ni, - shortname, shortlen, - longname, longlen, - longname, longlen, FALSE ); - closed = TRUE; - } - } - } - else - res = -1; - } - else - { - res = -1; - errno = ENOENT; - } - free( shortname ); - if ( !closed ) - { - ntfs_inode_close_in_dir( ni, dir_ni ); - ntfs_inode_close( dir_ni ); - } - return ( res ? -1 : 0 ); + vol = ni->vol; + fnum = ni->mft_no; + /* convert the string to the NTFS wide chars */ + if (size > MAX_DOS_NAME_LENGTH) + size = MAX_DOS_NAME_LENGTH; + strncpy(newname, value, size); + newname[size] = 0; + shortlen = ntfs_mbstoucs(newname, &shortname); + /* make sure the short name has valid chars */ + if ((shortlen < 0) || ntfs_forbidden_chars(shortname,shortlen)) { + ntfs_inode_close_in_dir(ni,dir_ni); + ntfs_inode_close(dir_ni); + res = -errno; + return res; + } + dnum = dir_ni->mft_no; + longlen = get_long_name(ni, dnum, longname); + if (longlen > 0) { + oldlen = get_dos_name(ni, dnum, oldname); + if ((oldlen >= 0) + && !ntfs_forbidden_chars(longname, longlen)) { + if (oldlen > 0) { + if (flags & XATTR_CREATE) { + res = -1; + errno = EEXIST; + } else + if ((shortlen == oldlen) + && !memcmp(shortname,oldname, + oldlen*sizeof(ntfschar))) + /* already set, done */ + res = 0; + else { + res = set_dos_name(ni, dir_ni, + shortname, shortlen, + longname, longlen, + oldname, oldlen, TRUE); + closed = TRUE; + } + } else { + if (flags & XATTR_REPLACE) { + res = -1; + errno = ENODATA; + } else { + res = set_dos_name(ni, dir_ni, + shortname, shortlen, + longname, longlen, + longname, longlen, FALSE); + closed = TRUE; + } + } + } else + res = -1; + } else { + res = -1; + errno = ENOENT; + } + free(shortname); + if (!closed) { + ntfs_inode_close_in_dir(ni,dir_ni); + ntfs_inode_close(dir_ni); + } + return (res ? -1 : 0); } /* - * Delete the ntfs DOS name + * Delete the ntfs DOS name */ -int ntfs_remove_ntfs_dos_name( ntfs_inode *ni, ntfs_inode *dir_ni ) +int ntfs_remove_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni) { - int res; - int oldnametype; - int longlen = 0; - int shortlen; - u64 dnum; - ntfs_volume *vol; - BOOL deleted = FALSE; - ntfschar shortname[MAX_DOS_NAME_LENGTH]; - ntfschar longname[NTFS_MAX_NAME_LEN]; + int res; + int oldnametype; + int longlen = 0; + int shortlen; + u64 dnum; + ntfs_volume *vol; + BOOL deleted = FALSE; + ntfschar shortname[MAX_DOS_NAME_LENGTH]; + ntfschar longname[NTFS_MAX_NAME_LEN]; - res = -1; - vol = ni->vol; - dnum = dir_ni->mft_no; - longlen = get_long_name( ni, dnum, longname ); - if ( longlen > 0 ) - { - shortlen = get_dos_name( ni, dnum, shortname ); - if ( shortlen >= 0 ) - { - /* migrate the long name as Posix */ - oldnametype = set_namespace( ni, dir_ni, longname, longlen, - FILE_NAME_POSIX ); - switch ( oldnametype ) - { - case FILE_NAME_WIN32_AND_DOS : - /* name was Win32+DOS : done */ - res = 0; - break; - case FILE_NAME_DOS : - /* name was DOS, make it back to DOS */ - set_namespace( ni, dir_ni, longname, longlen, - FILE_NAME_DOS ); - errno = ENOENT; - break; - case FILE_NAME_WIN32 : - /* name was Win32, make it Posix and delete */ - if ( set_namespace( ni, dir_ni, shortname, shortlen, - FILE_NAME_POSIX ) >= 0 ) - { - if ( !ntfs_delete( vol, - ( const char* )NULL, ni, - dir_ni, shortname, - shortlen ) ) - res = 0; - deleted = TRUE; - } - else - { - /* - * DOS name has been found, but cannot - * migrate to Posix : something bad - * has happened - */ - errno = EIO; - ntfs_log_error( "Could not change" - " DOS name of inode %lld to Posix\n", - ( long long )ni->mft_no ); - } - break; - default : - /* name was Posix or not found : error */ - errno = ENOENT; - break; - } - } - } - else - { - errno = ENOENT; - res = -1; - } - if ( !deleted ) - { - ntfs_inode_close_in_dir( ni, dir_ni ); - ntfs_inode_close( dir_ni ); - } - return ( res ); + res = -1; + vol = ni->vol; + dnum = dir_ni->mft_no; + longlen = get_long_name(ni, dnum, longname); + if (longlen > 0) { + shortlen = get_dos_name(ni, dnum, shortname); + if (shortlen >= 0) { + /* migrate the long name as Posix */ + oldnametype = set_namespace(ni,dir_ni,longname,longlen, + FILE_NAME_POSIX); + switch (oldnametype) { + case FILE_NAME_WIN32_AND_DOS : + /* name was Win32+DOS : done */ + res = 0; + break; + case FILE_NAME_DOS : + /* name was DOS, make it back to DOS */ + set_namespace(ni,dir_ni,longname,longlen, + FILE_NAME_DOS); + errno = ENOENT; + break; + case FILE_NAME_WIN32 : + /* name was Win32, make it Posix and delete */ + if (set_namespace(ni,dir_ni,shortname,shortlen, + FILE_NAME_POSIX) >= 0) { + if (!ntfs_delete(vol, + (const char*)NULL, ni, + dir_ni, shortname, + shortlen)) + res = 0; + deleted = TRUE; + } else { + /* + * DOS name has been found, but cannot + * migrate to Posix : something bad + * has happened + */ + errno = EIO; + ntfs_log_error("Could not change" + " DOS name of inode %lld to Posix\n", + (long long)ni->mft_no); + } + break; + default : + /* name was Posix or not found : error */ + errno = ENOENT; + break; + } + } + } else { + errno = ENOENT; + res = -1; + } + if (!deleted) { + ntfs_inode_close_in_dir(ni,dir_ni); + ntfs_inode_close(dir_ni); + } + return (res); } #endif diff --git a/source/libntfs/dir.h b/source/libntfs/dir.h index d0c8670f..56e76fe7 100644 --- a/source/libntfs/dir.h +++ b/source/libntfs/dir.h @@ -59,40 +59,40 @@ extern ntfschar NTFS_INDEX_O[3]; extern ntfschar NTFS_INDEX_Q[3]; extern ntfschar NTFS_INDEX_R[3]; -extern u64 ntfs_inode_lookup_by_name( ntfs_inode *dir_ni, - const ntfschar *uname, const int uname_len ); -extern u64 ntfs_inode_lookup_by_mbsname( ntfs_inode *dir_ni, const char *name ); -extern void ntfs_inode_update_mbsname( ntfs_inode *dir_ni, const char *name, - u64 inum ); +extern u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, + const ntfschar *uname, const int uname_len); +extern u64 ntfs_inode_lookup_by_mbsname(ntfs_inode *dir_ni, const char *name); +extern void ntfs_inode_update_mbsname(ntfs_inode *dir_ni, const char *name, + u64 inum); -extern ntfs_inode *ntfs_pathname_to_inode( ntfs_volume *vol, ntfs_inode *parent, - const char *pathname ); -extern ntfs_inode *ntfs_create( ntfs_inode *dir_ni, le32 securid, - ntfschar *name, u8 name_len, mode_t type ); -extern ntfs_inode *ntfs_create_device( ntfs_inode *dir_ni, le32 securid, - ntfschar *name, u8 name_len, mode_t type, dev_t dev ); -extern ntfs_inode *ntfs_create_symlink( ntfs_inode *dir_ni, le32 securid, - ntfschar *name, u8 name_len, ntfschar *target, int target_len ); -extern int ntfs_check_empty_dir( ntfs_inode *ni ); -extern int ntfs_delete( ntfs_volume *vol, const char *path, - ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, - u8 name_len ); +extern ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent, + const char *pathname); +extern ntfs_inode *ntfs_create(ntfs_inode *dir_ni, le32 securid, + ntfschar *name, u8 name_len, mode_t type); +extern ntfs_inode *ntfs_create_device(ntfs_inode *dir_ni, le32 securid, + ntfschar *name, u8 name_len, mode_t type, dev_t dev); +extern ntfs_inode *ntfs_create_symlink(ntfs_inode *dir_ni, le32 securid, + ntfschar *name, u8 name_len, ntfschar *target, int target_len); +extern int ntfs_check_empty_dir(ntfs_inode *ni); +extern int ntfs_delete(ntfs_volume *vol, const char *path, + ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, + u8 name_len); -extern int ntfs_link( ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, - u8 name_len ); +extern int ntfs_link(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, + u8 name_len); /* * File types (adapted from include ) */ -#define NTFS_DT_UNKNOWN 0 -#define NTFS_DT_FIFO 1 -#define NTFS_DT_CHR 2 -#define NTFS_DT_DIR 4 -#define NTFS_DT_BLK 6 -#define NTFS_DT_REG 8 -#define NTFS_DT_LNK 10 -#define NTFS_DT_SOCK 12 -#define NTFS_DT_WHT 14 +#define NTFS_DT_UNKNOWN 0 +#define NTFS_DT_FIFO 1 +#define NTFS_DT_CHR 2 +#define NTFS_DT_DIR 4 +#define NTFS_DT_BLK 6 +#define NTFS_DT_REG 8 +#define NTFS_DT_LNK 10 +#define NTFS_DT_SOCK 12 +#define NTFS_DT_WHT 14 /* * This is the "ntfs_filldir" function type, used by ntfs_readdir() to let @@ -100,27 +100,27 @@ extern int ntfs_link( ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, * This allows the caller to read directories into their application or * to have different dirent layouts depending on the binary type. */ -typedef int ( *ntfs_filldir_t )( void *dirent, const ntfschar *name, - const int name_len, const int name_type, const s64 pos, - const MFT_REF mref, const unsigned dt_type ); +typedef int (*ntfs_filldir_t)(void *dirent, const ntfschar *name, + const int name_len, const int name_type, const s64 pos, + const MFT_REF mref, const unsigned dt_type); -extern int ntfs_readdir( ntfs_inode *dir_ni, s64 *pos, - void *dirent, ntfs_filldir_t filldir ); +extern int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, + void *dirent, ntfs_filldir_t filldir); -ntfs_inode *ntfs_dir_parent_inode( ntfs_inode *ni ); +ntfs_inode *ntfs_dir_parent_inode(ntfs_inode *ni); -int ntfs_get_ntfs_dos_name( ntfs_inode *ni, ntfs_inode *dir_ni, - char *value, size_t size ); -int ntfs_set_ntfs_dos_name( ntfs_inode *ni, ntfs_inode *dir_ni, - const char *value, size_t size, int flags ); -int ntfs_remove_ntfs_dos_name( ntfs_inode *ni, ntfs_inode *dir_ni ); +int ntfs_get_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, + char *value, size_t size); +int ntfs_set_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, + const char *value, size_t size, int flags); +int ntfs_remove_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni); #if CACHE_INODE_SIZE struct CACHED_GENERIC; -extern int ntfs_dir_inode_hash( const struct CACHED_GENERIC *cached ); -extern int ntfs_dir_lookup_hash( const struct CACHED_GENERIC *cached ); +extern int ntfs_dir_inode_hash(const struct CACHED_GENERIC *cached); +extern int ntfs_dir_lookup_hash(const struct CACHED_GENERIC *cached); #endif diff --git a/source/libntfs/efs.c b/source/libntfs/efs.c index 25f2444b..6ccec20a 100644 --- a/source/libntfs/efs.c +++ b/source/libntfs/efs.c @@ -1,7 +1,7 @@ /** * efs.c - Limited processing of encrypted files * - * This module is part of ntfs-3g library + * This module is part of ntfs-3g library * * Copyright (c) 2009 Martin Bene * Copyright (c) 2009-2010 Jean-Pierre Andre @@ -58,305 +58,256 @@ #include "misc.h" #include "efs.h" -#ifdef HAVE_SETXATTR /* extended attributes interface required */ +#ifdef HAVE_SETXATTR /* extended attributes interface required */ -static ntfschar logged_utility_stream_name[] = -{ - const_cpu_to_le16( '$' ), - const_cpu_to_le16( 'E' ), - const_cpu_to_le16( 'F' ), - const_cpu_to_le16( 'S' ), - const_cpu_to_le16( 0 ) +static ntfschar logged_utility_stream_name[] = { + const_cpu_to_le16('$'), + const_cpu_to_le16('E'), + const_cpu_to_le16('F'), + const_cpu_to_le16('S'), + const_cpu_to_le16(0) } ; /* - * Get the ntfs EFS info into an extended attribute + * Get the ntfs EFS info into an extended attribute */ -int ntfs_get_efs_info( ntfs_inode *ni, char *value, size_t size ) +int ntfs_get_efs_info(ntfs_inode *ni, char *value, size_t size) { - EFS_ATTR_HEADER *efs_info; - s64 attr_size = 0; + EFS_ATTR_HEADER *efs_info; + s64 attr_size = 0; - if ( ni ) - { - if ( ni->flags & FILE_ATTR_ENCRYPTED ) - { - efs_info = ( EFS_ATTR_HEADER* )ntfs_attr_readall( ni, - AT_LOGGED_UTILITY_STREAM, ( ntfschar* )NULL, 0, - &attr_size ); - if ( efs_info - && ( le32_to_cpu( efs_info->length ) == attr_size ) ) - { - if ( attr_size <= ( s64 )size ) - { - if ( value ) - memcpy( value, efs_info, attr_size ); - else - { - errno = EFAULT; - attr_size = 0; - } - } - else if ( size ) - { - errno = ERANGE; - attr_size = 0; - } - free ( efs_info ); - } - else - { - if ( efs_info ) - { - free( efs_info ); - ntfs_log_error( "Bad efs_info for inode %lld\n", - ( long long )ni->mft_no ); - } - else - { - ntfs_log_error( "Could not get efsinfo" - " for inode %lld\n", - ( long long )ni->mft_no ); - } - errno = EIO; - attr_size = 0; - } - } - else - { - errno = ENODATA; - ntfs_log_trace( "Inode %lld is not encrypted\n", - ( long long )ni->mft_no ); - } - } - return ( attr_size ? ( int )attr_size : -errno ); + if (ni) { + if (ni->flags & FILE_ATTR_ENCRYPTED) { + efs_info = (EFS_ATTR_HEADER*)ntfs_attr_readall(ni, + AT_LOGGED_UTILITY_STREAM,(ntfschar*)NULL, 0, + &attr_size); + if (efs_info + && (le32_to_cpu(efs_info->length) == attr_size)) { + if (attr_size <= (s64)size) { + if (value) + memcpy(value,efs_info,attr_size); + else { + errno = EFAULT; + attr_size = 0; + } + } else + if (size) { + errno = ERANGE; + attr_size = 0; + } + free (efs_info); + } else { + if (efs_info) { + free(efs_info); + ntfs_log_error("Bad efs_info for inode %lld\n", + (long long)ni->mft_no); + } else { + ntfs_log_error("Could not get efsinfo" + " for inode %lld\n", + (long long)ni->mft_no); + } + errno = EIO; + attr_size = 0; + } + } else { + errno = ENODATA; + ntfs_log_trace("Inode %lld is not encrypted\n", + (long long)ni->mft_no); + } + } + return (attr_size ? (int)attr_size : -errno); } /* - * Fix all encrypted AT_DATA attributes of an inode + * Fix all encrypted AT_DATA attributes of an inode * - * The fix may require making an attribute non resident, which - * requires more space in the MFT record, and may cause some - * attribute to be expelled and the full record to be reorganized. - * When this happens, the search for data attributes has to be - * reinitialized. + * The fix may require making an attribute non resident, which + * requires more space in the MFT record, and may cause some + * attribute to be expelled and the full record to be reorganized. + * When this happens, the search for data attributes has to be + * reinitialized. * - * Returns zero if successful. - * -1 if there is a problem. + * Returns zero if successful. + * -1 if there is a problem. */ -static int fixup_loop( ntfs_inode *ni ) +static int fixup_loop(ntfs_inode *ni) { - ntfs_attr_search_ctx *ctx; - ntfs_attr *na; - ATTR_RECORD *a; - BOOL restart; - BOOL first; - int cnt; - int maxcnt; - int res = 0; + ntfs_attr_search_ctx *ctx; + ntfs_attr *na; + ATTR_RECORD *a; + BOOL restart; + BOOL first; + int cnt; + int maxcnt; + int res = 0; - maxcnt = 0; - do - { - restart = FALSE; - ctx = ntfs_attr_get_search_ctx( ni, NULL ); - if ( !ctx ) - { - ntfs_log_error( "Failed to get ctx for efs\n" ); - res = -1; - } - cnt = 0; - while ( !restart && !res - && !ntfs_attr_lookup( AT_DATA, NULL, 0, - CASE_SENSITIVE, 0, NULL, 0, ctx ) ) - { - cnt++; - a = ctx->attr; - na = ntfs_attr_open( ctx->ntfs_ino, AT_DATA, - ( ntfschar* )( ( u8* )a + le16_to_cpu( a->name_offset ) ), - a->name_length ); - if ( !na ) - { - ntfs_log_error( "can't open DATA Attribute\n" ); - res = -1; - } - if ( na && !( ctx->attr->flags & ATTR_IS_ENCRYPTED ) ) - { - if ( !NAttrNonResident( na ) - && ntfs_attr_make_non_resident( na, ctx ) ) - { - /* - * ntfs_attr_make_non_resident fails if there - * is not enough space in the MFT record. - * When this happens, force making non-resident - * so that some other attribute is expelled. - */ - if ( ntfs_attr_force_non_resident( na ) ) - { - res = -1; - } - else - { - /* make sure there is some progress */ - if ( cnt <= maxcnt ) - { - errno = EIO; - ntfs_log_error( "Multiple failure" - " making non resident\n" ); - res = -1; - } - else - { - ntfs_attr_put_search_ctx( ctx ); - ctx = ( ntfs_attr_search_ctx* )NULL; - restart = TRUE; - maxcnt = cnt; - } - } - } - if ( !restart && !res - && ntfs_efs_fixup_attribute( ctx, na ) ) - { - ntfs_log_error( "Error in efs fixup of AT_DATA Attribute\n" ); - res = -1; - } - } - if ( na ) - ntfs_attr_close( na ); - } - first = FALSE; - } - while ( restart && !res ); - if ( ctx ) - ntfs_attr_put_search_ctx( ctx ); - return ( res ); + maxcnt = 0; + do { + restart = FALSE; + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) { + ntfs_log_error("Failed to get ctx for efs\n"); + res = -1; + } + cnt = 0; + while (!restart && !res + && !ntfs_attr_lookup(AT_DATA, NULL, 0, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + cnt++; + a = ctx->attr; + na = ntfs_attr_open(ctx->ntfs_ino, AT_DATA, + (ntfschar*)((u8*)a + le16_to_cpu(a->name_offset)), + a->name_length); + if (!na) { + ntfs_log_error("can't open DATA Attribute\n"); + res = -1; + } + if (na && !(ctx->attr->flags & ATTR_IS_ENCRYPTED)) { + if (!NAttrNonResident(na) + && ntfs_attr_make_non_resident(na, ctx)) { + /* + * ntfs_attr_make_non_resident fails if there + * is not enough space in the MFT record. + * When this happens, force making non-resident + * so that some other attribute is expelled. + */ + if (ntfs_attr_force_non_resident(na)) { + res = -1; + } else { + /* make sure there is some progress */ + if (cnt <= maxcnt) { + errno = EIO; + ntfs_log_error("Multiple failure" + " making non resident\n"); + res = -1; + } else { + ntfs_attr_put_search_ctx(ctx); + ctx = (ntfs_attr_search_ctx*)NULL; + restart = TRUE; + maxcnt = cnt; + } + } + } + if (!restart && !res + && ntfs_efs_fixup_attribute(ctx, na)) { + ntfs_log_error("Error in efs fixup of AT_DATA Attribute\n"); + res = -1; + } + } + if (na) + ntfs_attr_close(na); + } + first = FALSE; + } while (restart && !res); + if (ctx) + ntfs_attr_put_search_ctx(ctx); + return (res); } /* - * Set the efs data from an extended attribute - * Warning : the new data is not checked - * Returns 0, or -1 if there is a problem + * Set the efs data from an extended attribute + * Warning : the new data is not checked + * Returns 0, or -1 if there is a problem */ -int ntfs_set_efs_info( ntfs_inode *ni, const char *value, size_t size, - int flags ) - +int ntfs_set_efs_info(ntfs_inode *ni, const char *value, size_t size, + int flags) + { - int res; - int written; - ntfs_attr *na; - const EFS_ATTR_HEADER *info_header; + int res; + int written; + ntfs_attr *na; + const EFS_ATTR_HEADER *info_header; - res = 0; - if ( ni && value && size ) - { - if ( ni->flags & ( FILE_ATTR_ENCRYPTED | FILE_ATTR_COMPRESSED ) ) - { - if ( ni->flags & FILE_ATTR_ENCRYPTED ) - { - ntfs_log_trace( "Inode %lld already encrypted\n", - ( long long )ni->mft_no ); - errno = EEXIST; - } - else - { - /* - * Possible problem : if encrypted file was - * restored in a compressed directory, it was - * restored as compressed. - * TODO : decompress first. - */ - ntfs_log_error( "Inode %lld cannot be encrypted and compressed\n", - ( long long )ni->mft_no ); - errno = EIO; - } - return -1; - } - info_header = ( const EFS_ATTR_HEADER* )value; - /* make sure we get a likely efsinfo */ - if ( le32_to_cpu( info_header->length ) != size ) - { - errno = EINVAL; - return ( -1 ); - } - if ( !ntfs_attr_exist( ni, AT_LOGGED_UTILITY_STREAM, - ( ntfschar* )NULL, 0 ) ) - { - if ( !( flags & XATTR_REPLACE ) ) - { - /* - * no logged_utility_stream attribute : add one, - * apparently, this does not feed the new value in - */ - res = ntfs_attr_add( ni, AT_LOGGED_UTILITY_STREAM, - logged_utility_stream_name, 4, - ( u8* )NULL, ( s64 )size ); - } - else - { - errno = ENODATA; - res = -1; - } - } - else - { - errno = EEXIST; - res = -1; - } - if ( !res ) - { - /* - * open and update the existing efs data - */ - na = ntfs_attr_open( ni, AT_LOGGED_UTILITY_STREAM, - logged_utility_stream_name, 4 ); - if ( na ) - { - /* resize attribute */ - res = ntfs_attr_truncate( na, ( s64 )size ); - /* overwrite value if any */ - if ( !res && value ) - { - written = ( int )ntfs_attr_pwrite( na, - ( s64 )0, ( s64 )size, value ); - if ( written != ( s64 )size ) - { - ntfs_log_error( "Failed to " - "update efs data\n" ); - errno = EIO; - res = -1; - } - } - ntfs_attr_close( na ); - } - else - res = -1; - } - if ( !res ) - { - /* Don't handle AT_DATA Attribute(s) if inode is a directory */ - if ( !( ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ) ) - { - /* iterate over AT_DATA attributes */ - /* set encrypted flag, truncate attribute to match padding bytes */ - - if ( fixup_loop( ni ) ) - return -1; - } - ni->flags |= FILE_ATTR_ENCRYPTED; - NInoSetDirty( ni ); - NInoFileNameSetDirty( ni ); - } - } - else - { - errno = EINVAL; - res = -1; - } - return ( res ? -1 : 0 ); + res = 0; + if (ni && value && size) { + if (ni->flags & (FILE_ATTR_ENCRYPTED | FILE_ATTR_COMPRESSED)) { + if (ni->flags & FILE_ATTR_ENCRYPTED) { + ntfs_log_trace("Inode %lld already encrypted\n", + (long long)ni->mft_no); + errno = EEXIST; + } else { + /* + * Possible problem : if encrypted file was + * restored in a compressed directory, it was + * restored as compressed. + * TODO : decompress first. + */ + ntfs_log_error("Inode %lld cannot be encrypted and compressed\n", + (long long)ni->mft_no); + errno = EIO; + } + return -1; + } + info_header = (const EFS_ATTR_HEADER*)value; + /* make sure we get a likely efsinfo */ + if (le32_to_cpu(info_header->length) != size) { + errno = EINVAL; + return (-1); + } + if (!ntfs_attr_exist(ni,AT_LOGGED_UTILITY_STREAM, + (ntfschar*)NULL,0)) { + if (!(flags & XATTR_REPLACE)) { + /* + * no logged_utility_stream attribute : add one, + * apparently, this does not feed the new value in + */ + res = ntfs_attr_add(ni,AT_LOGGED_UTILITY_STREAM, + logged_utility_stream_name,4, + (u8*)NULL,(s64)size); + } else { + errno = ENODATA; + res = -1; + } + } else { + errno = EEXIST; + res = -1; + } + if (!res) { + /* + * open and update the existing efs data + */ + na = ntfs_attr_open(ni, AT_LOGGED_UTILITY_STREAM, + logged_utility_stream_name, 4); + if (na) { + /* resize attribute */ + res = ntfs_attr_truncate(na, (s64)size); + /* overwrite value if any */ + if (!res && value) { + written = (int)ntfs_attr_pwrite(na, + (s64)0, (s64)size, value); + if (written != (s64)size) { + ntfs_log_error("Failed to " + "update efs data\n"); + errno = EIO; + res = -1; + } + } + ntfs_attr_close(na); + } else + res = -1; + } + if (!res) { + /* Don't handle AT_DATA Attribute(s) if inode is a directory */ + if (!(ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) { + /* iterate over AT_DATA attributes */ + /* set encrypted flag, truncate attribute to match padding bytes */ + + if (fixup_loop(ni)) + return -1; + } + ni->flags |= FILE_ATTR_ENCRYPTED; + NInoSetDirty(ni); + NInoFileNameSetDirty(ni); + } + } else { + errno = EINVAL; + res = -1; + } + return (res ? -1 : 0); } /* @@ -364,144 +315,125 @@ int ntfs_set_efs_info( ntfs_inode *ni, const char *value, size_t size, * read padding length from last two bytes * truncate attribute, make non-resident, * set data size to match padding length - * set ATTR_IS_ENCRYPTED flag on attribute + * set ATTR_IS_ENCRYPTED flag on attribute * - * Return 0 if successful - * -1 if failed (errno tells why) + * Return 0 if successful + * -1 if failed (errno tells why) */ -int ntfs_efs_fixup_attribute( ntfs_attr_search_ctx *ctx, ntfs_attr *na ) +int ntfs_efs_fixup_attribute(ntfs_attr_search_ctx *ctx, ntfs_attr *na) { - u64 newsize; - u64 oldsize; - le16 appended_bytes; - u16 padding_length; - ntfs_inode *ni; - BOOL close_ctx = FALSE; + u64 newsize; + u64 oldsize; + le16 appended_bytes; + u16 padding_length; + ntfs_inode *ni; + BOOL close_ctx = FALSE; - if ( !na ) - { - ntfs_log_error( "no na specified for efs_fixup_attribute\n" ); - goto err_out; - } - if ( !ctx ) - { - ctx = ntfs_attr_get_search_ctx( na->ni, NULL ); - if ( !ctx ) - { - ntfs_log_error( "Failed to get ctx for efs\n" ); - goto err_out; - } - close_ctx = TRUE; - if ( ntfs_attr_lookup( AT_DATA, na->name, na->name_len, - CASE_SENSITIVE, 0, NULL, 0, ctx ) ) - { - ntfs_log_error( "attr lookup for AT_DATA attribute failed in efs fixup\n" ); - goto err_out; - } - } - else - { - if ( !NAttrNonResident( na ) ) - { - ntfs_log_error( "Cannot make non resident" - " when a context has been allocated\n" ); - goto err_out; - } - } + if (!na) { + ntfs_log_error("no na specified for efs_fixup_attribute\n"); + goto err_out; + } + if (!ctx) { + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) { + ntfs_log_error("Failed to get ctx for efs\n"); + goto err_out; + } + close_ctx = TRUE; + if (ntfs_attr_lookup(AT_DATA, na->name, na->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + ntfs_log_error("attr lookup for AT_DATA attribute failed in efs fixup\n"); + goto err_out; + } + } else { + if (!NAttrNonResident(na)) { + ntfs_log_error("Cannot make non resident" + " when a context has been allocated\n"); + goto err_out; + } + } - /* no extra bytes are added to void attributes */ - oldsize = na->data_size; - if ( oldsize ) - { - /* make sure size is valid for a raw encrypted stream */ - if ( ( oldsize & 511 ) != 2 ) - { - ntfs_log_error( "Bad raw encrypted stream\n" ); - goto err_out; - } - /* read padding length from last two bytes of attribute */ - if ( ntfs_attr_pread( na, oldsize - 2, 2, &appended_bytes ) != 2 ) - { - ntfs_log_error( "Error reading padding length\n" ); - goto err_out; - } - padding_length = le16_to_cpu( appended_bytes ); - if ( padding_length > 511 || padding_length > na->data_size - 2 ) - { - errno = EINVAL; - ntfs_log_error( "invalid padding length %d for data_size %lld\n", - padding_length, ( long long )oldsize ); - goto err_out; - } - newsize = oldsize - padding_length - 2; - /* - * truncate attribute to possibly free clusters allocated - * for the last two bytes, but do not truncate to new size - * to avoid losing useful data - */ - if ( ntfs_attr_truncate( na, oldsize - 2 ) ) - { - ntfs_log_error( "Error truncating attribute\n" ); - goto err_out; - } - } - else - newsize = 0; + /* no extra bytes are added to void attributes */ + oldsize = na->data_size; + if (oldsize) { + /* make sure size is valid for a raw encrypted stream */ + if ((oldsize & 511) != 2) { + ntfs_log_error("Bad raw encrypted stream\n"); + goto err_out; + } + /* read padding length from last two bytes of attribute */ + if (ntfs_attr_pread(na, oldsize - 2, 2, &appended_bytes) != 2) { + ntfs_log_error("Error reading padding length\n"); + goto err_out; + } + padding_length = le16_to_cpu(appended_bytes); + if (padding_length > 511 || padding_length > na->data_size-2) { + errno = EINVAL; + ntfs_log_error("invalid padding length %d for data_size %lld\n", + padding_length, (long long)oldsize); + goto err_out; + } + newsize = oldsize - padding_length - 2; + /* + * truncate attribute to possibly free clusters allocated + * for the last two bytes, but do not truncate to new size + * to avoid losing useful data + */ + if (ntfs_attr_truncate(na, oldsize - 2)) { + ntfs_log_error("Error truncating attribute\n"); + goto err_out; + } + } else + newsize = 0; - /* - * Encrypted AT_DATA Attributes MUST be non-resident - * This has to be done after the attribute is resized, as - * resizing down to zero may cause the attribute to be made - * resident. - */ - if ( !NAttrNonResident( na ) - && ntfs_attr_make_non_resident( na, ctx ) ) - { - if ( !close_ctx - || ntfs_attr_force_non_resident( na ) ) - { - ntfs_log_error( "Error making DATA attribute non-resident\n" ); - goto err_out; - } - else - { - /* - * must reinitialize context after forcing - * non-resident. We need a context for updating - * the state, and at this point, we are sure - * the context is not used elsewhere. - */ - ntfs_attr_reinit_search_ctx( ctx ); - if ( ntfs_attr_lookup( AT_DATA, na->name, na->name_len, - CASE_SENSITIVE, 0, NULL, 0, ctx ) ) - { - ntfs_log_error( "attr lookup for AT_DATA attribute failed in efs fixup\n" ); - goto err_out; - } - } - } - ni = na->ni; - if ( !na->name_len ) - { - ni->data_size = newsize; - ni->allocated_size = na->allocated_size; - } - NInoSetDirty( ni ); - NInoFileNameSetDirty( ni ); + /* + * Encrypted AT_DATA Attributes MUST be non-resident + * This has to be done after the attribute is resized, as + * resizing down to zero may cause the attribute to be made + * resident. + */ + if (!NAttrNonResident(na) + && ntfs_attr_make_non_resident(na, ctx)) { + if (!close_ctx + || ntfs_attr_force_non_resident(na)) { + ntfs_log_error("Error making DATA attribute non-resident\n"); + goto err_out; + } else { + /* + * must reinitialize context after forcing + * non-resident. We need a context for updating + * the state, and at this point, we are sure + * the context is not used elsewhere. + */ + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_lookup(AT_DATA, na->name, na->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + ntfs_log_error("attr lookup for AT_DATA attribute failed in efs fixup\n"); + goto err_out; + } + } + } + ni = na->ni; + if (!na->name_len) { + ni->data_size = newsize; + ni->allocated_size = na->allocated_size; + } + NInoSetDirty(ni); + NInoFileNameSetDirty(ni); - ctx->attr->data_size = cpu_to_le64( newsize ); - if ( le64_to_cpu( ctx->attr->initialized_size ) > newsize ) - ctx->attr->initialized_size = ctx->attr->data_size; - ctx->attr->flags |= ATTR_IS_ENCRYPTED; - if ( close_ctx ) - ntfs_attr_put_search_ctx( ctx ); - - return ( 0 ); + ctx->attr->data_size = cpu_to_le64(newsize); + if (le64_to_cpu(ctx->attr->initialized_size) > newsize) + ctx->attr->initialized_size = ctx->attr->data_size; + ctx->attr->flags |= ATTR_IS_ENCRYPTED; + if (close_ctx) + ntfs_attr_put_search_ctx(ctx); + + return (0); err_out: - if ( close_ctx && ctx ) - ntfs_attr_put_search_ctx( ctx ); - return ( -1 ); + if (close_ctx && ctx) + ntfs_attr_put_search_ctx(ctx); + return (-1); } #endif /* HAVE_SETXATTR */ diff --git a/source/libntfs/efs.h b/source/libntfs/efs.h index 3b450907..6eada067 100644 --- a/source/libntfs/efs.h +++ b/source/libntfs/efs.h @@ -21,10 +21,10 @@ #ifndef EFS_H #define EFS_H -int ntfs_get_efs_info( ntfs_inode *ni, char *value, size_t size ); +int ntfs_get_efs_info(ntfs_inode *ni, char *value, size_t size); -int ntfs_set_efs_info( ntfs_inode *ni, - const char *value, size_t size, int flags ); -int ntfs_efs_fixup_attribute( ntfs_attr_search_ctx *ctx, ntfs_attr *na ); +int ntfs_set_efs_info(ntfs_inode *ni, + const char *value, size_t size, int flags); +int ntfs_efs_fixup_attribute(ntfs_attr_search_ctx *ctx, ntfs_attr *na); #endif /* EFS_H */ diff --git a/source/libntfs/endians.h b/source/libntfs/endians.h index 84a7d250..397f1c20 100644 --- a/source/libntfs/endians.h +++ b/source/libntfs/endians.h @@ -1,5 +1,5 @@ /* - * endians.h - Definitions related to handling of byte ordering. + * endians.h - Definitions related to handling of byte ordering. * Originated from the Linux-NTFS project. * * Copyright (c) 2000-2005 Anton Altaparmakov @@ -29,9 +29,9 @@ /* * Notes: - * We define the conversion functions including typecasts since the + * We define the conversion functions including typecasts since the * defaults don't necessarily perform appropriate typecasts. - * Also, using our own functions means that we can change them if it + * Also, using our own functions means that we can change them if it * turns out that we do need to use the unaligned access macros on * architectures requiring aligned memory accesses... */ @@ -53,59 +53,59 @@ #endif #ifndef __BYTE_ORDER -# if defined(_BYTE_ORDER) -# define __BYTE_ORDER _BYTE_ORDER -# define __LITTLE_ENDIAN _LITTLE_ENDIAN -# define __BIG_ENDIAN _BIG_ENDIAN -# elif defined(BYTE_ORDER) -# define __BYTE_ORDER BYTE_ORDER -# define __LITTLE_ENDIAN LITTLE_ENDIAN -# define __BIG_ENDIAN BIG_ENDIAN -# elif defined(__BYTE_ORDER__) -# define __BYTE_ORDER __BYTE_ORDER__ -# define __LITTLE_ENDIAN __LITTLE_ENDIAN__ -# define __BIG_ENDIAN __BIG_ENDIAN__ -# elif (defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)) || \ - defined(WORDS_LITTLEENDIAN) -# define __BYTE_ORDER 1 -# define __LITTLE_ENDIAN 1 -# define __BIG_ENDIAN 0 -# elif (!defined(_LITTLE_ENDIAN) && defined(_BIG_ENDIAN)) || \ - defined(WORDS_BIGENDIAN) -# define __BYTE_ORDER 0 -# define __LITTLE_ENDIAN 1 -# define __BIG_ENDIAN 0 -# else -# error "__BYTE_ORDER is not defined." -# endif +# if defined(_BYTE_ORDER) +# define __BYTE_ORDER _BYTE_ORDER +# define __LITTLE_ENDIAN _LITTLE_ENDIAN +# define __BIG_ENDIAN _BIG_ENDIAN +# elif defined(BYTE_ORDER) +# define __BYTE_ORDER BYTE_ORDER +# define __LITTLE_ENDIAN LITTLE_ENDIAN +# define __BIG_ENDIAN BIG_ENDIAN +# elif defined(__BYTE_ORDER__) +# define __BYTE_ORDER __BYTE_ORDER__ +# define __LITTLE_ENDIAN __LITTLE_ENDIAN__ +# define __BIG_ENDIAN __BIG_ENDIAN__ +# elif (defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)) || \ + defined(WORDS_LITTLEENDIAN) +# define __BYTE_ORDER 1 +# define __LITTLE_ENDIAN 1 +# define __BIG_ENDIAN 0 +# elif (!defined(_LITTLE_ENDIAN) && defined(_BIG_ENDIAN)) || \ + defined(WORDS_BIGENDIAN) +# define __BYTE_ORDER 0 +# define __LITTLE_ENDIAN 1 +# define __BIG_ENDIAN 0 +# else +# error "__BYTE_ORDER is not defined." +# endif #endif -#define __ntfs_bswap_constant_16(x) \ - (u16)((((u16)(x) & 0xff00) >> 8) | \ - (((u16)(x) & 0x00ff) << 8)) +#define __ntfs_bswap_constant_16(x) \ + (u16)((((u16)(x) & 0xff00) >> 8) | \ + (((u16)(x) & 0x00ff) << 8)) -#define __ntfs_bswap_constant_32(x) \ - (u32)((((u32)(x) & 0xff000000u) >> 24) | \ - (((u32)(x) & 0x00ff0000u) >> 8) | \ - (((u32)(x) & 0x0000ff00u) << 8) | \ - (((u32)(x) & 0x000000ffu) << 24)) +#define __ntfs_bswap_constant_32(x) \ + (u32)((((u32)(x) & 0xff000000u) >> 24) | \ + (((u32)(x) & 0x00ff0000u) >> 8) | \ + (((u32)(x) & 0x0000ff00u) << 8) | \ + (((u32)(x) & 0x000000ffu) << 24)) -#define __ntfs_bswap_constant_64(x) \ - (u64)((((u64)(x) & 0xff00000000000000ull) >> 56) | \ - (((u64)(x) & 0x00ff000000000000ull) >> 40) | \ - (((u64)(x) & 0x0000ff0000000000ull) >> 24) | \ - (((u64)(x) & 0x000000ff00000000ull) >> 8) | \ - (((u64)(x) & 0x00000000ff000000ull) << 8) | \ - (((u64)(x) & 0x0000000000ff0000ull) << 24) | \ - (((u64)(x) & 0x000000000000ff00ull) << 40) | \ - (((u64)(x) & 0x00000000000000ffull) << 56)) +#define __ntfs_bswap_constant_64(x) \ + (u64)((((u64)(x) & 0xff00000000000000ull) >> 56) | \ + (((u64)(x) & 0x00ff000000000000ull) >> 40) | \ + (((u64)(x) & 0x0000ff0000000000ull) >> 24) | \ + (((u64)(x) & 0x000000ff00000000ull) >> 8) | \ + (((u64)(x) & 0x00000000ff000000ull) << 8) | \ + (((u64)(x) & 0x0000000000ff0000ull) << 24) | \ + (((u64)(x) & 0x000000000000ff00ull) << 40) | \ + (((u64)(x) & 0x00000000000000ffull) << 56)) #ifdef HAVE_BYTESWAP_H -# include +# include #else -# define bswap_16(x) __ntfs_bswap_constant_16(x) -# define bswap_32(x) __ntfs_bswap_constant_32(x) -# define bswap_64(x) __ntfs_bswap_constant_64(x) +# define bswap_16(x) __ntfs_bswap_constant_16(x) +# define bswap_32(x) __ntfs_bswap_constant_32(x) +# define bswap_64(x) __ntfs_bswap_constant_64(x) #endif #if defined(__LITTLE_ENDIAN) && (__BYTE_ORDER == __LITTLE_ENDIAN) @@ -152,52 +152,52 @@ /* Unsigned from LE to CPU conversion. */ -#define le16_to_cpu(x) (u16)__le16_to_cpu((u16)(x)) -#define le32_to_cpu(x) (u32)__le32_to_cpu((u32)(x)) -#define le64_to_cpu(x) (u64)__le64_to_cpu((u64)(x)) +#define le16_to_cpu(x) (u16)__le16_to_cpu((u16)(x)) +#define le32_to_cpu(x) (u32)__le32_to_cpu((u32)(x)) +#define le64_to_cpu(x) (u64)__le64_to_cpu((u64)(x)) -#define le16_to_cpup(x) (u16)__le16_to_cpu(*(const u16*)(x)) -#define le32_to_cpup(x) (u32)__le32_to_cpu(*(const u32*)(x)) -#define le64_to_cpup(x) (u64)__le64_to_cpu(*(const u64*)(x)) +#define le16_to_cpup(x) (u16)__le16_to_cpu(*(const u16*)(x)) +#define le32_to_cpup(x) (u32)__le32_to_cpu(*(const u32*)(x)) +#define le64_to_cpup(x) (u64)__le64_to_cpu(*(const u64*)(x)) /* Signed from LE to CPU conversion. */ -#define sle16_to_cpu(x) (s16)__le16_to_cpu((s16)(x)) -#define sle32_to_cpu(x) (s32)__le32_to_cpu((s32)(x)) -#define sle64_to_cpu(x) (s64)__le64_to_cpu((s64)(x)) +#define sle16_to_cpu(x) (s16)__le16_to_cpu((s16)(x)) +#define sle32_to_cpu(x) (s32)__le32_to_cpu((s32)(x)) +#define sle64_to_cpu(x) (s64)__le64_to_cpu((s64)(x)) -#define sle16_to_cpup(x) (s16)__le16_to_cpu(*(s16*)(x)) -#define sle32_to_cpup(x) (s32)__le32_to_cpu(*(s32*)(x)) -#define sle64_to_cpup(x) (s64)__le64_to_cpu(*(s64*)(x)) +#define sle16_to_cpup(x) (s16)__le16_to_cpu(*(s16*)(x)) +#define sle32_to_cpup(x) (s32)__le32_to_cpu(*(s32*)(x)) +#define sle64_to_cpup(x) (s64)__le64_to_cpu(*(s64*)(x)) /* Unsigned from CPU to LE conversion. */ -#define cpu_to_le16(x) (u16)__cpu_to_le16((u16)(x)) -#define cpu_to_le32(x) (u32)__cpu_to_le32((u32)(x)) -#define cpu_to_le64(x) (u64)__cpu_to_le64((u64)(x)) +#define cpu_to_le16(x) (u16)__cpu_to_le16((u16)(x)) +#define cpu_to_le32(x) (u32)__cpu_to_le32((u32)(x)) +#define cpu_to_le64(x) (u64)__cpu_to_le64((u64)(x)) -#define cpu_to_le16p(x) (u16)__cpu_to_le16(*(u16*)(x)) -#define cpu_to_le32p(x) (u32)__cpu_to_le32(*(u32*)(x)) -#define cpu_to_le64p(x) (u64)__cpu_to_le64(*(u64*)(x)) +#define cpu_to_le16p(x) (u16)__cpu_to_le16(*(u16*)(x)) +#define cpu_to_le32p(x) (u32)__cpu_to_le32(*(u32*)(x)) +#define cpu_to_le64p(x) (u64)__cpu_to_le64(*(u64*)(x)) /* Signed from CPU to LE conversion. */ -#define cpu_to_sle16(x) (s16)__cpu_to_le16((s16)(x)) -#define cpu_to_sle32(x) (s32)__cpu_to_le32((s32)(x)) -#define cpu_to_sle64(x) (s64)__cpu_to_le64((s64)(x)) +#define cpu_to_sle16(x) (s16)__cpu_to_le16((s16)(x)) +#define cpu_to_sle32(x) (s32)__cpu_to_le32((s32)(x)) +#define cpu_to_sle64(x) (s64)__cpu_to_le64((s64)(x)) -#define cpu_to_sle16p(x) (s16)__cpu_to_le16(*(s16*)(x)) -#define cpu_to_sle32p(x) (s32)__cpu_to_le32(*(s32*)(x)) -#define cpu_to_sle64p(x) (s64)__cpu_to_le64(*(s64*)(x)) +#define cpu_to_sle16p(x) (s16)__cpu_to_le16(*(s16*)(x)) +#define cpu_to_sle32p(x) (s32)__cpu_to_le32(*(s32*)(x)) +#define cpu_to_sle64p(x) (s64)__cpu_to_le64(*(s64*)(x)) /* Constant endianness conversion defines. */ -#define const_le16_to_cpu(x) __constant_le16_to_cpu(x) -#define const_le32_to_cpu(x) __constant_le32_to_cpu(x) -#define const_le64_to_cpu(x) __constant_le64_to_cpu(x) +#define const_le16_to_cpu(x) __constant_le16_to_cpu(x) +#define const_le32_to_cpu(x) __constant_le32_to_cpu(x) +#define const_le64_to_cpu(x) __constant_le64_to_cpu(x) -#define const_cpu_to_le16(x) __constant_cpu_to_le16(x) -#define const_cpu_to_le32(x) __constant_cpu_to_le32(x) -#define const_cpu_to_le64(x) __constant_cpu_to_le64(x) +#define const_cpu_to_le16(x) __constant_cpu_to_le16(x) +#define const_cpu_to_le32(x) __constant_cpu_to_le32(x) +#define const_cpu_to_le64(x) __constant_cpu_to_le64(x) #endif /* defined _NTFS_ENDIANS_H */ diff --git a/source/libntfs/gekko_io.c b/source/libntfs/gekko_io.c index eef1a895..48ca90d4 100644 --- a/source/libntfs/gekko_io.c +++ b/source/libntfs/gekko_io.c @@ -66,93 +66,83 @@ #define DEV_FD(dev) ((gekko_fd *)dev->d_private) /* Prototypes */ -static s64 ntfs_device_gekko_io_readbytes( struct ntfs_device *dev, s64 offset, s64 count, void *buf ); -static bool ntfs_device_gekko_io_readsectors( struct ntfs_device *dev, sec_t sector, sec_t numSectors, void* buffer ); -static s64 ntfs_device_gekko_io_writebytes( struct ntfs_device *dev, s64 offset, s64 count, const void *buf ); -static bool ntfs_device_gekko_io_writesectors( struct ntfs_device *dev, sec_t sector, sec_t numSectors, const void* buffer ); +static s64 ntfs_device_gekko_io_readbytes(struct ntfs_device *dev, s64 offset, s64 count, void *buf); +static bool ntfs_device_gekko_io_readsectors(struct ntfs_device *dev, sec_t sector, sec_t numSectors, void* buffer); +static s64 ntfs_device_gekko_io_writebytes(struct ntfs_device *dev, s64 offset, s64 count, const void *buf); +static bool ntfs_device_gekko_io_writesectors(struct ntfs_device *dev, sec_t sector, sec_t numSectors, const void* buffer); /** * */ -static int ntfs_device_gekko_io_open( struct ntfs_device *dev, int flags ) +static int ntfs_device_gekko_io_open(struct ntfs_device *dev, int flags) { - ntfs_log_trace( "dev %p, flags %i\n", dev, flags ); + ntfs_log_trace("dev %p, flags %i\n", dev, flags); // Get the device driver descriptor - gekko_fd *fd = DEV_FD( dev ); - if ( !fd ) - { + gekko_fd *fd = DEV_FD(dev); + if (!fd) { errno = EBADF; return -1; } // Get the device interface const DISC_INTERFACE* interface = fd->interface; - if ( !interface ) - { + if (!interface) { errno = ENODEV; return -1; } // Start the device interface and ensure that it is inserted - if ( !interface->startup() ) - { - ntfs_log_perror( "device failed to start\n" ); + if (!interface->startup()) { + ntfs_log_perror("device failed to start\n"); errno = EIO; return -1; } - if ( !interface->isInserted() ) - { - ntfs_log_perror( "device media is not inserted\n" ); + if (!interface->isInserted()) { + ntfs_log_perror("device media is not inserted\n"); errno = EIO; return -1; } // Check that the device isn't already open (used by another volume?) - if ( NDevOpen( dev ) ) - { - ntfs_log_perror( "device is busy (already open)\n" ); + if (NDevOpen(dev)) { + ntfs_log_perror("device is busy (already open)\n"); errno = EBUSY; return -1; } // Check that there is a valid NTFS boot sector at the start of the device NTFS_BOOT_SECTOR boot; - if ( interface->readSectors( fd->startSector, 1, &boot ) ) - { - if ( !ntfs_boot_sector_is_ntfs( &boot ) ) - { + if (interface->readSectors(fd->startSector, 1, &boot)) { + if (!ntfs_boot_sector_is_ntfs(&boot)) { errno = EINVALPART; return -1; } - } - else - { - ntfs_log_perror( "read failure @ sector %d\n", fd->startSector ); + } else { + ntfs_log_perror("read failure @ sector %d\n", fd->startSector); errno = EIO; return -1; } // Parse the boot sector - fd->hiddenSectors = le32_to_cpu( boot.bpb.hidden_sectors ); - fd->sectorSize = le16_to_cpu( boot.bpb.bytes_per_sector ); - fd->sectorCount = sle64_to_cpu( boot.number_of_sectors ); + fd->hiddenSectors = le32_to_cpu(boot.bpb.hidden_sectors); + fd->sectorSize = le16_to_cpu(boot.bpb.bytes_per_sector); + fd->sectorCount = sle64_to_cpu(boot.number_of_sectors); fd->pos = 0; - fd->len = ( fd->sectorCount * fd->sectorSize ); - fd->ino = le64_to_cpu( boot.volume_serial_number ); + fd->len = (fd->sectorCount * fd->sectorSize); + fd->ino = le64_to_cpu(boot.volume_serial_number); // Mark the device as read-only (if required) - if ( flags & O_RDONLY ) - { - NDevSetReadOnly( dev ); + if (flags & O_RDONLY) { + NDevSetReadOnly(dev); } // Create the cache - fd->cache = _NTFS_cache_constructor( fd->cachePageCount, fd->cachePageSize, interface, fd->startSector + fd->sectorCount, fd->sectorSize ); + fd->cache = _NTFS_cache_constructor(fd->cachePageCount, fd->cachePageSize, interface, fd->startSector + fd->sectorCount, fd->sectorSize); // Mark the device as open - NDevSetBlock( dev ); - NDevSetOpen( dev ); + NDevSetBlock(dev); + NDevSetOpen(dev); return 0; } @@ -160,47 +150,43 @@ static int ntfs_device_gekko_io_open( struct ntfs_device *dev, int flags ) /** * */ -static int ntfs_device_gekko_io_close( struct ntfs_device *dev ) +static int ntfs_device_gekko_io_close(struct ntfs_device *dev) { - ntfs_log_trace( "dev %p\n", dev ); + ntfs_log_trace("dev %p\n", dev); // Get the device driver descriptor - gekko_fd *fd = DEV_FD( dev ); - if ( !fd ) - { + gekko_fd *fd = DEV_FD(dev); + if (!fd) { errno = EBADF; return -1; } // Check that the device is actually open - if ( !NDevOpen( dev ) ) - { - ntfs_log_perror( "device is not open\n" ); + if (!NDevOpen(dev)) { + ntfs_log_perror("device is not open\n"); errno = EIO; return -1; } // Mark the device as closed - NDevClearOpen( dev ); - NDevClearBlock( dev ); + NDevClearOpen(dev); + NDevClearBlock(dev); // Flush the device (if dirty and not read-only) - if ( NDevDirty( dev ) && !NDevReadOnly( dev ) ) - { - ntfs_log_debug( "device is dirty, will now sync\n" ); + if (NDevDirty(dev) && !NDevReadOnly(dev)) { + ntfs_log_debug("device is dirty, will now sync\n"); // ...? // Mark the device as clean - NDevClearDirty( dev ); + NDevClearDirty(dev); } // Flush and destroy the cache (if required) - if ( fd->cache ) - { - _NTFS_cache_flush( fd->cache ); - _NTFS_cache_destructor( fd->cache ); + if (fd->cache) { + _NTFS_cache_flush(fd->cache); + _NTFS_cache_destructor(fd->cache); } // Shutdown the device interface @@ -210,7 +196,7 @@ static int ntfs_device_gekko_io_close( struct ntfs_device *dev ) }*/ // Free the device driver private data - ntfs_free( dev->d_private ); + ntfs_free(dev->d_private); dev->d_private = NULL; return 0; @@ -219,24 +205,22 @@ static int ntfs_device_gekko_io_close( struct ntfs_device *dev ) /** * */ -static s64 ntfs_device_gekko_io_seek( struct ntfs_device *dev, s64 offset, int whence ) +static s64 ntfs_device_gekko_io_seek(struct ntfs_device *dev, s64 offset, int whence) { - ntfs_log_trace( "dev %p, offset %Li, whence %i\n", dev, offset, whence ); + ntfs_log_trace("dev %p, offset %Li, whence %i\n", dev, offset, whence); // Get the device driver descriptor - gekko_fd *fd = DEV_FD( dev ); - if ( !fd ) - { + gekko_fd *fd = DEV_FD(dev); + if (!fd) { errno = EBADF; return -1; } // Set the current position on the device (in bytes) - switch ( whence ) - { - case SEEK_SET: fd->pos = MIN( MAX( offset, 0 ), fd->len ); break; - case SEEK_CUR: fd->pos = MIN( MAX( fd->pos + offset, 0 ), fd->len ); break; - case SEEK_END: fd->pos = MIN( MAX( fd->len + offset, 0 ), fd->len ); break; + switch(whence) { + case SEEK_SET: fd->pos = MIN(MAX(offset, 0), fd->len); break; + case SEEK_CUR: fd->pos = MIN(MAX(fd->pos + offset, 0), fd->len); break; + case SEEK_END: fd->pos = MIN(MAX(fd->len + offset, 0), fd->len); break; } return 0; @@ -245,123 +229,115 @@ static s64 ntfs_device_gekko_io_seek( struct ntfs_device *dev, s64 offset, int w /** * */ -static s64 ntfs_device_gekko_io_read( struct ntfs_device *dev, void *buf, s64 count ) +static s64 ntfs_device_gekko_io_read(struct ntfs_device *dev, void *buf, s64 count) { - return ntfs_device_gekko_io_readbytes( dev, DEV_FD( dev )->pos, count, buf ); + return ntfs_device_gekko_io_readbytes(dev, DEV_FD(dev)->pos, count, buf); } /** * */ -static s64 ntfs_device_gekko_io_write( struct ntfs_device *dev, const void *buf, s64 count ) +static s64 ntfs_device_gekko_io_write(struct ntfs_device *dev, const void *buf, s64 count) { - return ntfs_device_gekko_io_writebytes( dev, DEV_FD( dev )->pos, count, buf ); + return ntfs_device_gekko_io_writebytes(dev, DEV_FD(dev)->pos, count, buf); } /** * */ -static s64 ntfs_device_gekko_io_pread( struct ntfs_device *dev, void *buf, s64 count, s64 offset ) +static s64 ntfs_device_gekko_io_pread(struct ntfs_device *dev, void *buf, s64 count, s64 offset) { - return ntfs_device_gekko_io_readbytes( dev, offset, count, buf ); + return ntfs_device_gekko_io_readbytes(dev, offset, count, buf); } /** * */ -static s64 ntfs_device_gekko_io_pwrite( struct ntfs_device *dev, const void *buf, s64 count, s64 offset ) +static s64 ntfs_device_gekko_io_pwrite(struct ntfs_device *dev, const void *buf, s64 count, s64 offset) { - return ntfs_device_gekko_io_writebytes( dev, offset, count, buf ); + return ntfs_device_gekko_io_writebytes(dev, offset, count, buf); } /** * */ -static s64 ntfs_device_gekko_io_readbytes( struct ntfs_device *dev, s64 offset, s64 count, void *buf ) +static s64 ntfs_device_gekko_io_readbytes(struct ntfs_device *dev, s64 offset, s64 count, void *buf) { - ntfs_log_trace( "dev %p, offset %Li, count %Li\n", dev, offset, count ); + ntfs_log_trace("dev %p, offset %Li, count %Li\n", dev, offset, count); // Get the device driver descriptor - gekko_fd *fd = DEV_FD( dev ); - if ( !fd ) - { + gekko_fd *fd = DEV_FD(dev); + if (!fd) { errno = EBADF; return -1; } // Get the device interface const DISC_INTERFACE* interface = fd->interface; - if ( !interface ) - { + if (!interface) { errno = ENODEV; return -1; } - if ( offset < 0 ) + if(offset < 0) { errno = EROFS; return -1; } - if ( !count ) + if(!count) return 0; - sec_t sec_start = ( sec_t ) fd->startSector; + sec_t sec_start = (sec_t) fd->startSector; sec_t sec_count = 1; - u32 buffer_offset = ( u32 ) ( offset % fd->sectorSize ); + u32 buffer_offset = (u32) (offset % fd->sectorSize); u8 *buffer = NULL; // Determine the range of sectors required for this read - if ( offset > 0 ) - { - sec_start += ( sec_t ) floor( ( f64 ) offset / ( f64 ) fd->sectorSize ); + if (offset > 0) { + sec_start += (sec_t) floor((f64) offset / (f64) fd->sectorSize); } - if ( buffer_offset + count > fd->sectorSize ) - { - sec_count = ( sec_t ) ceil( ( f64 ) ( buffer_offset + count ) / ( f64 ) fd->sectorSize ); + if (buffer_offset+count > fd->sectorSize) { + sec_count = (sec_t) ceil((f64) (buffer_offset+count) / (f64) fd->sectorSize); } // If this read happens to be on the sector boundaries then do the read straight into the destination buffer - if ( ( buffer_offset == 0 ) && ( count % fd->sectorSize == 0 ) ) - { + if((buffer_offset == 0) && (count % fd->sectorSize == 0)) { // Read from the device - ntfs_log_trace( "direct read from sector %d (%d sector(s) long)\n", sec_start, sec_count ); - if ( !ntfs_device_gekko_io_readsectors( dev, sec_start, sec_count, buf ) ) - { - ntfs_log_perror( "direct read failure @ sector %d (%d sector(s) long)\n", sec_start, sec_count ); + ntfs_log_trace("direct read from sector %d (%d sector(s) long)\n", sec_start, sec_count); + if (!ntfs_device_gekko_io_readsectors(dev, sec_start, sec_count, buf)) { + ntfs_log_perror("direct read failure @ sector %d (%d sector(s) long)\n", sec_start, sec_count); errno = EIO; return -1; } - // Else read into a buffer and copy over only what was requested + // Else read into a buffer and copy over only what was requested } else - { + { // Allocate a buffer to hold the read data - buffer = ( u8* )ntfs_alloc( sec_count * fd->sectorSize ); - if ( !buffer ) - { + buffer = (u8*)ntfs_alloc(sec_count * fd->sectorSize); + if (!buffer) { errno = ENOMEM; return -1; } // Read from the device - ntfs_log_trace( "buffered read from sector %d (%d sector(s) long)\n", sec_start, sec_count ); - ntfs_log_trace( "count: %d sec_count:%d fd->sectorSize: %d )\n", ( u32 )count, ( u32 )sec_count, ( u32 )fd->sectorSize ); - if ( !ntfs_device_gekko_io_readsectors( dev, sec_start, sec_count, buffer ) ) - { - ntfs_log_perror( "buffered read failure @ sector %d (%d sector(s) long)\n", sec_start, sec_count ); - ntfs_free( buffer ); + ntfs_log_trace("buffered read from sector %d (%d sector(s) long)\n", sec_start, sec_count); + ntfs_log_trace("count: %d sec_count:%d fd->sectorSize: %d )\n", (u32)count, (u32)sec_count,(u32)fd->sectorSize); + if (!ntfs_device_gekko_io_readsectors(dev, sec_start, sec_count, buffer)) { + ntfs_log_perror("buffered read failure @ sector %d (%d sector(s) long)\n", sec_start, sec_count); + ntfs_free(buffer); errno = EIO; return -1; } // Copy what was requested to the destination buffer - memcpy( buf, buffer + buffer_offset, count ); - ntfs_free( buffer ); + memcpy(buf, buffer + buffer_offset, count); + ntfs_free(buffer); } @@ -371,159 +347,146 @@ static s64 ntfs_device_gekko_io_readbytes( struct ntfs_device *dev, s64 offset, /** * */ -static s64 ntfs_device_gekko_io_writebytes( struct ntfs_device *dev, s64 offset, s64 count, const void *buf ) +static s64 ntfs_device_gekko_io_writebytes(struct ntfs_device *dev, s64 offset, s64 count, const void *buf) { - ntfs_log_trace( "dev %p, offset %lli, count %lli\n", dev, offset, count ); + ntfs_log_trace("dev %p, offset %lli, count %lli\n", dev, offset, count); // Get the device driver descriptor - gekko_fd *fd = DEV_FD( dev ); - if ( !fd ) - { + gekko_fd *fd = DEV_FD(dev); + if (!fd) { errno = EBADF; return -1; } // Get the device interface const DISC_INTERFACE* interface = fd->interface; - if ( !interface ) - { + if (!interface) { errno = ENODEV; return -1; } // Check that the device can be written to - if ( NDevReadOnly( dev ) ) - { + if (NDevReadOnly(dev)) { errno = EROFS; return -1; } - if ( count < 0 || offset < 0 ) - { + if(count < 0 || offset < 0) { errno = EROFS; return -1; } - if ( count == 0 ) + if(count == 0) return 0; - sec_t sec_start = ( sec_t ) fd->startSector; + sec_t sec_start = (sec_t) fd->startSector; sec_t sec_count = 1; - u32 buffer_offset = ( u32 ) ( offset % fd->sectorSize ); + u32 buffer_offset = (u32) (offset % fd->sectorSize); u8 *buffer = NULL; // Determine the range of sectors required for this write - if ( offset > 0 ) - { - sec_start += ( sec_t ) floor( ( f64 ) offset / ( f64 ) fd->sectorSize ); + if (offset > 0) { + sec_start += (sec_t) floor((f64) offset / (f64) fd->sectorSize); } - if ( ( buffer_offset + count ) > fd->sectorSize ) - { - sec_count = ( sec_t ) ceil( ( f64 ) ( buffer_offset + count ) / ( f64 ) fd->sectorSize ); + if ((buffer_offset+count) > fd->sectorSize) { + sec_count = (sec_t) ceil((f64) (buffer_offset+count) / (f64) fd->sectorSize); } // If this write happens to be on the sector boundaries then do the write straight to disc - if ( ( buffer_offset == 0 ) && ( count % fd->sectorSize == 0 ) ) + if((buffer_offset == 0) && (count % fd->sectorSize == 0)) { // Write to the device - ntfs_log_trace( "direct write to sector %d (%d sector(s) long)\n", sec_start, sec_count ); - if ( !ntfs_device_gekko_io_writesectors( dev, sec_start, sec_count, buf ) ) - { - ntfs_log_perror( "direct write failure @ sector %d (%d sector(s) long)\n", sec_start, sec_count ); + ntfs_log_trace("direct write to sector %d (%d sector(s) long)\n", sec_start, sec_count); + if (!ntfs_device_gekko_io_writesectors(dev, sec_start, sec_count, buf)) { + ntfs_log_perror("direct write failure @ sector %d (%d sector(s) long)\n", sec_start, sec_count); errno = EIO; return -1; } - // Else write from a buffer aligned to the sector boundaries + // Else write from a buffer aligned to the sector boundaries } else { // Allocate a buffer to hold the write data - buffer = ( u8 * ) ntfs_alloc( sec_count * fd->sectorSize ); - if ( !buffer ) - { + buffer = (u8 *) ntfs_alloc(sec_count * fd->sectorSize); + if (!buffer) { errno = ENOMEM; return -1; } // Read the first and last sectors of the buffer from disc (if required) // NOTE: This is done because the data does not line up with the sector boundaries, // we just read in the buffer edges where the data overlaps with the rest of the disc - if ( buffer_offset != 0 ) + if(buffer_offset != 0) { - if ( !ntfs_device_gekko_io_readsectors( dev, sec_start, 1, buffer ) ) - { - ntfs_log_perror( "read failure @ sector %d\n", sec_start ); - ntfs_free( buffer ); + if (!ntfs_device_gekko_io_readsectors(dev, sec_start, 1, buffer)) { + ntfs_log_perror("read failure @ sector %d\n", sec_start); + ntfs_free(buffer); errno = EIO; return -1; } } - if ( ( buffer_offset + count ) % fd->sectorSize != 0 ) + if((buffer_offset+count) % fd->sectorSize != 0) { - if ( !ntfs_device_gekko_io_readsectors( dev, sec_start + sec_count - 1, 1, buffer + ( ( sec_count - 1 ) * fd->sectorSize ) ) ) - { - ntfs_log_perror( "read failure @ sector %d\n", sec_start + sec_count - 1 ); - ntfs_free( buffer ); + if (!ntfs_device_gekko_io_readsectors(dev, sec_start + sec_count - 1, 1, buffer + ((sec_count-1) * fd->sectorSize))) { + ntfs_log_perror("read failure @ sector %d\n", sec_start + sec_count - 1); + ntfs_free(buffer); errno = EIO; return -1; } } // Copy the data into the write buffer - memcpy( buffer + buffer_offset, buf, count ); + memcpy(buffer + buffer_offset, buf, count); // Write to the device - ntfs_log_trace( "buffered write to sector %d (%d sector(s) long)\n", sec_start, sec_count ); - if ( !ntfs_device_gekko_io_writesectors( dev, sec_start, sec_count, buffer ) ) - { - ntfs_log_perror( "buffered write failure @ sector %d\n", sec_start ); - ntfs_free( buffer ); + ntfs_log_trace("buffered write to sector %d (%d sector(s) long)\n", sec_start, sec_count); + if (!ntfs_device_gekko_io_writesectors(dev, sec_start, sec_count, buffer)) { + ntfs_log_perror("buffered write failure @ sector %d\n", sec_start); + ntfs_free(buffer); errno = EIO; return -1; } // Free the buffer - ntfs_free( buffer ); + ntfs_free(buffer); } // Mark the device as dirty (if we actually wrote anything) - NDevSetDirty( dev ); + NDevSetDirty(dev); return count; } -static bool ntfs_device_gekko_io_readsectors( struct ntfs_device *dev, sec_t sector, sec_t numSectors, void* buffer ) +static bool ntfs_device_gekko_io_readsectors(struct ntfs_device *dev, sec_t sector, sec_t numSectors, void* buffer) { // Get the device driver descriptor - gekko_fd *fd = DEV_FD( dev ); - if ( !fd ) - { + gekko_fd *fd = DEV_FD(dev); + if (!fd) { errno = EBADF; return false; } // Read the sectors from disc (or cache, if enabled) - if ( fd->cache ) - return _NTFS_cache_readSectors( fd->cache, sector, numSectors, buffer ); + if (fd->cache) + return _NTFS_cache_readSectors(fd->cache, sector, numSectors, buffer); else - return fd->interface->readSectors( sector, numSectors, buffer ); + return fd->interface->readSectors(sector, numSectors, buffer); return false; } -static bool ntfs_device_gekko_io_writesectors( struct ntfs_device *dev, sec_t sector, sec_t numSectors, const void* buffer ) +static bool ntfs_device_gekko_io_writesectors(struct ntfs_device *dev, sec_t sector, sec_t numSectors, const void* buffer) { // Get the device driver descriptor - gekko_fd *fd = DEV_FD( dev ); - if ( !fd ) - { + gekko_fd *fd = DEV_FD(dev); + if (!fd) { errno = EBADF; return false; } // Write the sectors to disc (or cache, if enabled) - if ( fd->cache ) - return _NTFS_cache_writeSectors( fd->cache, sector, numSectors, buffer ); + if (fd->cache) + return _NTFS_cache_writeSectors(fd->cache, sector, numSectors, buffer); else - return fd->interface->writeSectors( sector, numSectors, buffer ); + return fd->interface->writeSectors(sector, numSectors, buffer); return false; } @@ -531,26 +494,23 @@ static bool ntfs_device_gekko_io_writesectors( struct ntfs_device *dev, sec_t se /** * */ -static int ntfs_device_gekko_io_sync( struct ntfs_device *dev ) +static int ntfs_device_gekko_io_sync(struct ntfs_device *dev) { - gekko_fd *fd = DEV_FD( dev ); - ntfs_log_trace( "dev %p\n", dev ); + gekko_fd *fd = DEV_FD(dev); + ntfs_log_trace("dev %p\n", dev); // Check that the device can be written to - if ( NDevReadOnly( dev ) ) - { + if (NDevReadOnly(dev)) { errno = EROFS; return -1; } // Mark the device as clean - NDevClearDirty( dev ); + NDevClearDirty(dev); // Flush any sectors in the disc cache (if required) - if ( fd->cache ) - { - if ( !_NTFS_cache_flush( fd->cache ) ) - { + if (fd->cache) { + if (!_NTFS_cache_flush(fd->cache)) { errno = EIO; return -1; } @@ -562,29 +522,28 @@ static int ntfs_device_gekko_io_sync( struct ntfs_device *dev ) /** * */ -static int ntfs_device_gekko_io_stat( struct ntfs_device *dev, struct stat *buf ) +static int ntfs_device_gekko_io_stat(struct ntfs_device *dev, struct stat *buf) { - ntfs_log_trace( "dev %p, buf %p\n", dev, buf ); + ntfs_log_trace("dev %p, buf %p\n", dev, buf); // Get the device driver descriptor - gekko_fd *fd = DEV_FD( dev ); - if ( !fd ) - { + gekko_fd *fd = DEV_FD(dev); + if (!fd) { errno = EBADF; return -1; } // Short circuit cases were we don't actually have to do anything - if ( !buf ) + if (!buf) return 0; // Build the device mode - mode_t mode = ( S_IFBLK ) | - ( S_IRUSR | S_IRGRP | S_IROTH ) | - ( ( !NDevReadOnly( dev ) ) ? ( S_IWUSR | S_IWGRP | S_IWOTH ) : 0 ); + mode_t mode = (S_IFBLK) | + (S_IRUSR | S_IRGRP | S_IROTH) | + ((!NDevReadOnly(dev)) ? (S_IWUSR | S_IWGRP | S_IWOTH) : 0); // Zero out the stat buffer - memset( buf, 0, sizeof( struct stat ) ); + memset(buf, 0, sizeof(struct stat)); // Build the device stats buf->st_dev = fd->interface->ioType; @@ -600,79 +559,71 @@ static int ntfs_device_gekko_io_stat( struct ntfs_device *dev, struct stat *buf /** * */ -static int ntfs_device_gekko_io_ioctl( struct ntfs_device *dev, int request, void *argp ) +static int ntfs_device_gekko_io_ioctl(struct ntfs_device *dev, int request, void *argp) { - ntfs_log_trace( "dev %p, request %i, argp %p\n", dev, request, argp ); + ntfs_log_trace("dev %p, request %i, argp %p\n", dev, request, argp); // Get the device driver descriptor - gekko_fd *fd = DEV_FD( dev ); - if ( !fd ) - { + gekko_fd *fd = DEV_FD(dev); + if (!fd) { errno = EBADF; return -1; } // Figure out which i/o control was requested - switch ( request ) - { + switch (request) { - // Get block device size (sectors) -#if defined(BLKGETSIZE) - case BLKGETSIZE: - { - *( u32* )argp = fd->sectorCount; - return 0; - } -#endif + // Get block device size (sectors) + #if defined(BLKGETSIZE) + case BLKGETSIZE: { + *(u32*)argp = fd->sectorCount; + return 0; + } + #endif - // Get block device size (bytes) -#if defined(BLKGETSIZE64) - case BLKGETSIZE64: - { - *( u64* )argp = ( fd->sectorCount * fd->sectorSize ); - return 0; - } -#endif + // Get block device size (bytes) + #if defined(BLKGETSIZE64) + case BLKGETSIZE64: { + *(u64*)argp = (fd->sectorCount * fd->sectorSize); + return 0; + } + #endif - // Get hard drive geometry -#if defined(HDIO_GETGEO) - case HDIO_GETGEO: - { - struct hd_geometry *geo = ( struct hd_geometry* )argp; - geo->sectors = 0; - geo->heads = 0; - geo->cylinders = 0; - geo->start = fd->hiddenSectors; - return -1; - } -#endif + // Get hard drive geometry + #if defined(HDIO_GETGEO) + case HDIO_GETGEO: { + struct hd_geometry *geo = (struct hd_geometry*)argp; + geo->sectors = 0; + geo->heads = 0; + geo->cylinders = 0; + geo->start = fd->hiddenSectors; + return -1; + } + #endif - // Get block device sector size (bytes) -#if defined(BLKSSZGET) - case BLKSSZGET: - { - *( int* )argp = fd->sectorSize; - return 0; - } -#endif + // Get block device sector size (bytes) + #if defined(BLKSSZGET) + case BLKSSZGET: { + *(int*)argp = fd->sectorSize; + return 0; + } + #endif - // Set block device block size (bytes) -#if defined(BLKBSZSET) - case BLKBSZSET: - { - int sectorSize = *( int* )argp; - fd->sectorSize = sectorSize; - return 0; - } -#endif + // Set block device block size (bytes) + #if defined(BLKBSZSET) + case BLKBSZSET: { + int sectorSize = *(int*)argp; + fd->sectorSize = sectorSize; + return 0; + } + #endif - // Unimplemented ioctrl - default: - { - ntfs_log_perror( "Unimplemented ioctrl %i\n", request ); - errno = EOPNOTSUPP; - return -1; - } + // Unimplemented ioctrl + default: { + ntfs_log_perror("Unimplemented ioctrl %i\n", request); + errno = EOPNOTSUPP; + return -1; + } } @@ -682,8 +633,7 @@ static int ntfs_device_gekko_io_ioctl( struct ntfs_device *dev, int request, voi /** * Device operations for working with gekko style devices and files. */ -struct ntfs_device_operations ntfs_device_gekko_io_ops = -{ +struct ntfs_device_operations ntfs_device_gekko_io_ops = { .open = ntfs_device_gekko_io_open, .close = ntfs_device_gekko_io_close, .seek = ntfs_device_gekko_io_seek, diff --git a/source/libntfs/gekko_io.h b/source/libntfs/gekko_io.h index 9a288794..d5018e89 100644 --- a/source/libntfs/gekko_io.h +++ b/source/libntfs/gekko_io.h @@ -33,8 +33,7 @@ /** * gekko_fd - Gekko device driver descriptor */ -typedef struct _gekko_fd -{ +typedef struct _gekko_fd { const DISC_INTERFACE* interface; /* Device disc interface */ sec_t startSector; /* LBA of partition start */ sec_t hiddenSectors; /* LBA offset to true partition start (as described by boot sector) */ diff --git a/source/libntfs/index.c b/source/libntfs/index.c index 693c51a7..7df0deec 100644 --- a/source/libntfs/index.c +++ b/source/libntfs/index.c @@ -50,7 +50,7 @@ /** * ntfs_index_entry_mark_dirty - mark an index entry dirty - * @ictx: ntfs index context describing the index entry + * @ictx: ntfs index context describing the index entry * * Mark the index entry described by the index entry context @ictx dirty. * @@ -62,433 +62,418 @@ * attribute, set ib_dirty to TRUE, thus index block will be updated during * ntfs_index_ctx_put. */ -void ntfs_index_entry_mark_dirty( ntfs_index_context *ictx ) +void ntfs_index_entry_mark_dirty(ntfs_index_context *ictx) { - if ( ictx->is_in_root ) - ntfs_inode_mark_dirty( ictx->actx->ntfs_ino ); - else - ictx->ib_dirty = TRUE; + if (ictx->is_in_root) + ntfs_inode_mark_dirty(ictx->actx->ntfs_ino); + else + ictx->ib_dirty = TRUE; } -static s64 ntfs_ib_vcn_to_pos( ntfs_index_context *icx, VCN vcn ) +static s64 ntfs_ib_vcn_to_pos(ntfs_index_context *icx, VCN vcn) { - return vcn << icx->vcn_size_bits; + return vcn << icx->vcn_size_bits; } -static VCN ntfs_ib_pos_to_vcn( ntfs_index_context *icx, s64 pos ) +static VCN ntfs_ib_pos_to_vcn(ntfs_index_context *icx, s64 pos) { - return pos >> icx->vcn_size_bits; + return pos >> icx->vcn_size_bits; } -static int ntfs_ib_write( ntfs_index_context *icx, INDEX_BLOCK *ib ) +static int ntfs_ib_write(ntfs_index_context *icx, INDEX_BLOCK *ib) { - s64 ret, vcn = sle64_to_cpu( ib->index_block_vcn ); - - ntfs_log_trace( "vcn: %lld\n", ( long long )vcn ); - - ret = ntfs_attr_mst_pwrite( icx->ia_na, ntfs_ib_vcn_to_pos( icx, vcn ), - 1, icx->block_size, ib ); - if ( ret != 1 ) - { - ntfs_log_perror( "Failed to write index block %lld, inode %llu", - ( long long )vcn, ( unsigned long long )icx->ni->mft_no ); - return STATUS_ERROR; - } - - return STATUS_OK; + s64 ret, vcn = sle64_to_cpu(ib->index_block_vcn); + + ntfs_log_trace("vcn: %lld\n", (long long)vcn); + + ret = ntfs_attr_mst_pwrite(icx->ia_na, ntfs_ib_vcn_to_pos(icx, vcn), + 1, icx->block_size, ib); + if (ret != 1) { + ntfs_log_perror("Failed to write index block %lld, inode %llu", + (long long)vcn, (unsigned long long)icx->ni->mft_no); + return STATUS_ERROR; + } + + return STATUS_OK; } -static int ntfs_icx_ib_write( ntfs_index_context *icx ) +static int ntfs_icx_ib_write(ntfs_index_context *icx) { - if ( ntfs_ib_write( icx, icx->ib ) ) - return STATUS_ERROR; - - icx->ib_dirty = FALSE; - - return STATUS_OK; + if (ntfs_ib_write(icx, icx->ib)) + return STATUS_ERROR; + + icx->ib_dirty = FALSE; + + return STATUS_OK; } /** * ntfs_index_ctx_get - allocate and initialize a new index context - * @ni: ntfs inode with which to initialize the context - * @name: name of the which context describes - * @name_len: length of the index name + * @ni: ntfs inode with which to initialize the context + * @name: name of the which context describes + * @name_len: length of the index name * * Allocate a new index context, initialize it with @ni and return it. * Return NULL if allocation failed. */ -ntfs_index_context *ntfs_index_ctx_get( ntfs_inode *ni, - ntfschar *name, u32 name_len ) +ntfs_index_context *ntfs_index_ctx_get(ntfs_inode *ni, + ntfschar *name, u32 name_len) { - ntfs_index_context *icx; + ntfs_index_context *icx; - ntfs_log_trace( "Entering\n" ); - - if ( !ni ) - { - errno = EINVAL; - return NULL; - } - if ( ni->nr_extents == -1 ) - ni = ni->base_ni; - icx = ntfs_calloc( sizeof( ntfs_index_context ) ); - if ( icx ) - *icx = ( ntfs_index_context ) - { - .ni = ni, - .name = name, - .name_len = name_len, - }; - return icx; + ntfs_log_trace("Entering\n"); + + if (!ni) { + errno = EINVAL; + return NULL; + } + if (ni->nr_extents == -1) + ni = ni->base_ni; + icx = ntfs_calloc(sizeof(ntfs_index_context)); + if (icx) + *icx = (ntfs_index_context) { + .ni = ni, + .name = name, + .name_len = name_len, + }; + return icx; } -static void ntfs_index_ctx_free( ntfs_index_context *icx ) +static void ntfs_index_ctx_free(ntfs_index_context *icx) { - ntfs_log_trace( "Entering\n" ); + ntfs_log_trace("Entering\n"); + + if (!icx->entry) + return; - if ( !icx->entry ) - return; + if (icx->actx) + ntfs_attr_put_search_ctx(icx->actx); - if ( icx->actx ) - ntfs_attr_put_search_ctx( icx->actx ); - - if ( !icx->is_in_root ) - { - if ( icx->ib_dirty ) - { - /* FIXME: Error handling!!! */ - ntfs_ib_write( icx, icx->ib ); - } - free( icx->ib ); - } - - ntfs_attr_close( icx->ia_na ); + if (!icx->is_in_root) { + if (icx->ib_dirty) { + /* FIXME: Error handling!!! */ + ntfs_ib_write(icx, icx->ib); + } + free(icx->ib); + } + + ntfs_attr_close(icx->ia_na); } /** * ntfs_index_ctx_put - release an index context - * @icx: index context to free + * @icx: index context to free * * Release the index context @icx, releasing all associated resources. */ -void ntfs_index_ctx_put( ntfs_index_context *icx ) +void ntfs_index_ctx_put(ntfs_index_context *icx) { - ntfs_index_ctx_free( icx ); - free( icx ); + ntfs_index_ctx_free(icx); + free(icx); } /** * ntfs_index_ctx_reinit - reinitialize an index context - * @icx: index context to reinitialize + * @icx: index context to reinitialize * * Reinitialize the index context @icx so it can be used for ntfs_index_lookup. */ -void ntfs_index_ctx_reinit( ntfs_index_context *icx ) +void ntfs_index_ctx_reinit(ntfs_index_context *icx) { - ntfs_log_trace( "Entering\n" ); - - ntfs_index_ctx_free( icx ); - - *icx = ( ntfs_index_context ) - { - .ni = icx->ni, - .name = icx->name, - .name_len = icx->name_len, - }; + ntfs_log_trace("Entering\n"); + + ntfs_index_ctx_free(icx); + + *icx = (ntfs_index_context) { + .ni = icx->ni, + .name = icx->name, + .name_len = icx->name_len, + }; } -static VCN *ntfs_ie_get_vcn_addr( INDEX_ENTRY *ie ) +static VCN *ntfs_ie_get_vcn_addr(INDEX_ENTRY *ie) { - return ( VCN * )( ( u8 * )ie + le16_to_cpu( ie->length ) - sizeof( VCN ) ); + return (VCN *)((u8 *)ie + le16_to_cpu(ie->length) - sizeof(VCN)); } /** * Get the subnode vcn to which the index entry refers. */ -VCN ntfs_ie_get_vcn( INDEX_ENTRY *ie ) +VCN ntfs_ie_get_vcn(INDEX_ENTRY *ie) { - return sle64_to_cpup( ntfs_ie_get_vcn_addr( ie ) ); + return sle64_to_cpup(ntfs_ie_get_vcn_addr(ie)); } -static INDEX_ENTRY *ntfs_ie_get_first( INDEX_HEADER *ih ) +static INDEX_ENTRY *ntfs_ie_get_first(INDEX_HEADER *ih) { - return ( INDEX_ENTRY * )( ( u8 * )ih + le32_to_cpu( ih->entries_offset ) ); + return (INDEX_ENTRY *)((u8 *)ih + le32_to_cpu(ih->entries_offset)); } -static INDEX_ENTRY *ntfs_ie_get_next( INDEX_ENTRY *ie ) +static INDEX_ENTRY *ntfs_ie_get_next(INDEX_ENTRY *ie) { - return ( INDEX_ENTRY * )( ( char * )ie + le16_to_cpu( ie->length ) ); + return (INDEX_ENTRY *)((char *)ie + le16_to_cpu(ie->length)); } -static u8 *ntfs_ie_get_end( INDEX_HEADER *ih ) +static u8 *ntfs_ie_get_end(INDEX_HEADER *ih) { - /* FIXME: check if it isn't overflowing the index block size */ - return ( u8 * )ih + le32_to_cpu( ih->index_length ); + /* FIXME: check if it isn't overflowing the index block size */ + return (u8 *)ih + le32_to_cpu(ih->index_length); } -static int ntfs_ie_end( INDEX_ENTRY *ie ) +static int ntfs_ie_end(INDEX_ENTRY *ie) { - return ie->ie_flags & INDEX_ENTRY_END || !ie->length; + return ie->ie_flags & INDEX_ENTRY_END || !ie->length; } -/** +/** * Find the last entry in the index block */ -static INDEX_ENTRY *ntfs_ie_get_last( INDEX_ENTRY *ie, char *ies_end ) +static INDEX_ENTRY *ntfs_ie_get_last(INDEX_ENTRY *ie, char *ies_end) { - ntfs_log_trace( "Entering\n" ); - - while ( ( char * )ie < ies_end && !ntfs_ie_end( ie ) ) - ie = ntfs_ie_get_next( ie ); - - return ie; + ntfs_log_trace("Entering\n"); + + while ((char *)ie < ies_end && !ntfs_ie_end(ie)) + ie = ntfs_ie_get_next(ie); + + return ie; } -static INDEX_ENTRY *ntfs_ie_get_by_pos( INDEX_HEADER *ih, int pos ) +static INDEX_ENTRY *ntfs_ie_get_by_pos(INDEX_HEADER *ih, int pos) { - INDEX_ENTRY *ie; - - ntfs_log_trace( "pos: %d\n", pos ); - - ie = ntfs_ie_get_first( ih ); - - while ( pos-- > 0 ) - ie = ntfs_ie_get_next( ie ); - - return ie; + INDEX_ENTRY *ie; + + ntfs_log_trace("pos: %d\n", pos); + + ie = ntfs_ie_get_first(ih); + + while (pos-- > 0) + ie = ntfs_ie_get_next(ie); + + return ie; } -static INDEX_ENTRY *ntfs_ie_prev( INDEX_HEADER *ih, INDEX_ENTRY *ie ) +static INDEX_ENTRY *ntfs_ie_prev(INDEX_HEADER *ih, INDEX_ENTRY *ie) { - INDEX_ENTRY *ie_prev = NULL; - INDEX_ENTRY *tmp; - - ntfs_log_trace( "Entering\n" ); - - tmp = ntfs_ie_get_first( ih ); - - while ( tmp != ie ) - { - ie_prev = tmp; - tmp = ntfs_ie_get_next( tmp ); - } - - return ie_prev; + INDEX_ENTRY *ie_prev = NULL; + INDEX_ENTRY *tmp; + + ntfs_log_trace("Entering\n"); + + tmp = ntfs_ie_get_first(ih); + + while (tmp != ie) { + ie_prev = tmp; + tmp = ntfs_ie_get_next(tmp); + } + + return ie_prev; } -char *ntfs_ie_filename_get( INDEX_ENTRY *ie ) +char *ntfs_ie_filename_get(INDEX_ENTRY *ie) { - FILE_NAME_ATTR *fn; + FILE_NAME_ATTR *fn; - fn = ( FILE_NAME_ATTR * ) & ie->key; - return ntfs_attr_name_get( fn->file_name, fn->file_name_length ); + fn = (FILE_NAME_ATTR *)&ie->key; + return ntfs_attr_name_get(fn->file_name, fn->file_name_length); } -void ntfs_ie_filename_dump( INDEX_ENTRY *ie ) +void ntfs_ie_filename_dump(INDEX_ENTRY *ie) { - char *s; + char *s; - s = ntfs_ie_filename_get( ie ); - ntfs_log_debug( "'%s' ", s ); - ntfs_attr_name_free( &s ); + s = ntfs_ie_filename_get(ie); + ntfs_log_debug("'%s' ", s); + ntfs_attr_name_free(&s); } -void ntfs_ih_filename_dump( INDEX_HEADER *ih ) +void ntfs_ih_filename_dump(INDEX_HEADER *ih) { - INDEX_ENTRY *ie; - - ntfs_log_trace( "Entering\n" ); - - ie = ntfs_ie_get_first( ih ); - while ( !ntfs_ie_end( ie ) ) - { - ntfs_ie_filename_dump( ie ); - ie = ntfs_ie_get_next( ie ); - } + INDEX_ENTRY *ie; + + ntfs_log_trace("Entering\n"); + + ie = ntfs_ie_get_first(ih); + while (!ntfs_ie_end(ie)) { + ntfs_ie_filename_dump(ie); + ie = ntfs_ie_get_next(ie); + } } -static int ntfs_ih_numof_entries( INDEX_HEADER *ih ) +static int ntfs_ih_numof_entries(INDEX_HEADER *ih) { - int n; - INDEX_ENTRY *ie; - u8 *end; - - ntfs_log_trace( "Entering\n" ); - - end = ntfs_ie_get_end( ih ); - ie = ntfs_ie_get_first( ih ); - for ( n = 0; !ntfs_ie_end( ie ) && ( u8 * )ie < end; n++ ) - ie = ntfs_ie_get_next( ie ); - return n; + int n; + INDEX_ENTRY *ie; + u8 *end; + + ntfs_log_trace("Entering\n"); + + end = ntfs_ie_get_end(ih); + ie = ntfs_ie_get_first(ih); + for (n = 0; !ntfs_ie_end(ie) && (u8 *)ie < end; n++) + ie = ntfs_ie_get_next(ie); + return n; } -static int ntfs_ih_one_entry( INDEX_HEADER *ih ) +static int ntfs_ih_one_entry(INDEX_HEADER *ih) { - return ( ntfs_ih_numof_entries( ih ) == 1 ); + return (ntfs_ih_numof_entries(ih) == 1); } -static int ntfs_ih_zero_entry( INDEX_HEADER *ih ) +static int ntfs_ih_zero_entry(INDEX_HEADER *ih) { - return ( ntfs_ih_numof_entries( ih ) == 0 ); + return (ntfs_ih_numof_entries(ih) == 0); } -static void ntfs_ie_delete( INDEX_HEADER *ih, INDEX_ENTRY *ie ) +static void ntfs_ie_delete(INDEX_HEADER *ih, INDEX_ENTRY *ie) { - u32 new_size; - - ntfs_log_trace( "Entering\n" ); - - new_size = le32_to_cpu( ih->index_length ) - le16_to_cpu( ie->length ); - ih->index_length = cpu_to_le32( new_size ); - memmove( ie, ( u8 * )ie + le16_to_cpu( ie->length ), - new_size - ( ( u8 * )ie - ( u8 * )ih ) ); + u32 new_size; + + ntfs_log_trace("Entering\n"); + + new_size = le32_to_cpu(ih->index_length) - le16_to_cpu(ie->length); + ih->index_length = cpu_to_le32(new_size); + memmove(ie, (u8 *)ie + le16_to_cpu(ie->length), + new_size - ((u8 *)ie - (u8 *)ih)); } -static void ntfs_ie_set_vcn( INDEX_ENTRY *ie, VCN vcn ) +static void ntfs_ie_set_vcn(INDEX_ENTRY *ie, VCN vcn) { - *ntfs_ie_get_vcn_addr( ie ) = cpu_to_le64( vcn ); + *ntfs_ie_get_vcn_addr(ie) = cpu_to_le64(vcn); } /** * Insert @ie index entry at @pos entry. Used @ih values should be ok already. */ -static void ntfs_ie_insert( INDEX_HEADER *ih, INDEX_ENTRY *ie, INDEX_ENTRY *pos ) +static void ntfs_ie_insert(INDEX_HEADER *ih, INDEX_ENTRY *ie, INDEX_ENTRY *pos) { - int ie_size = le16_to_cpu( ie->length ); - - ntfs_log_trace( "Entering\n" ); - - ih->index_length = cpu_to_le32( le32_to_cpu( ih->index_length ) + ie_size ); - memmove( ( u8 * )pos + ie_size, pos, - le32_to_cpu( ih->index_length ) - ( ( u8 * )pos - ( u8 * )ih ) - ie_size ); - memcpy( pos, ie, ie_size ); + int ie_size = le16_to_cpu(ie->length); + + ntfs_log_trace("Entering\n"); + + ih->index_length = cpu_to_le32(le32_to_cpu(ih->index_length) + ie_size); + memmove((u8 *)pos + ie_size, pos, + le32_to_cpu(ih->index_length) - ((u8 *)pos - (u8 *)ih) - ie_size); + memcpy(pos, ie, ie_size); } -static INDEX_ENTRY *ntfs_ie_dup( INDEX_ENTRY *ie ) +static INDEX_ENTRY *ntfs_ie_dup(INDEX_ENTRY *ie) { - INDEX_ENTRY *dup; - - ntfs_log_trace( "Entering\n" ); - - dup = ntfs_malloc( le16_to_cpu( ie->length ) ); - if ( dup ) - memcpy( dup, ie, le16_to_cpu( ie->length ) ); - - return dup; + INDEX_ENTRY *dup; + + ntfs_log_trace("Entering\n"); + + dup = ntfs_malloc(le16_to_cpu(ie->length)); + if (dup) + memcpy(dup, ie, le16_to_cpu(ie->length)); + + return dup; } -static INDEX_ENTRY *ntfs_ie_dup_novcn( INDEX_ENTRY *ie ) +static INDEX_ENTRY *ntfs_ie_dup_novcn(INDEX_ENTRY *ie) { - INDEX_ENTRY *dup; - int size = le16_to_cpu( ie->length ); - - ntfs_log_trace( "Entering\n" ); - - if ( ie->ie_flags & INDEX_ENTRY_NODE ) - size -= sizeof( VCN ); - - dup = ntfs_malloc( size ); - if ( dup ) - { - memcpy( dup, ie, size ); - dup->ie_flags &= ~INDEX_ENTRY_NODE; - dup->length = cpu_to_le16( size ); - } - return dup; + INDEX_ENTRY *dup; + int size = le16_to_cpu(ie->length); + + ntfs_log_trace("Entering\n"); + + if (ie->ie_flags & INDEX_ENTRY_NODE) + size -= sizeof(VCN); + + dup = ntfs_malloc(size); + if (dup) { + memcpy(dup, ie, size); + dup->ie_flags &= ~INDEX_ENTRY_NODE; + dup->length = cpu_to_le16(size); + } + return dup; } -static int ntfs_ia_check( ntfs_index_context *icx, INDEX_BLOCK *ib, VCN vcn ) +static int ntfs_ia_check(ntfs_index_context *icx, INDEX_BLOCK *ib, VCN vcn) { - u32 ib_size = ( unsigned )le32_to_cpu( ib->index.allocated_size ) + 0x18; - - ntfs_log_trace( "Entering\n" ); - - if ( !ntfs_is_indx_record( ib->magic ) ) - { - - ntfs_log_error( "Corrupt index block signature: vcn %lld inode " - "%llu\n", ( long long )vcn, - ( unsigned long long )icx->ni->mft_no ); - return -1; - } - - if ( sle64_to_cpu( ib->index_block_vcn ) != vcn ) - { - - ntfs_log_error( "Corrupt index block: VCN (%lld) is different " - "from expected VCN (%lld) in inode %llu\n", - ( long long )sle64_to_cpu( ib->index_block_vcn ), - ( long long )vcn, - ( unsigned long long )icx->ni->mft_no ); - return -1; - } - - if ( ib_size != icx->block_size ) - { - - ntfs_log_error( "Corrupt index block : VCN (%lld) of inode %llu " - "has a size (%u) differing from the index " - "specified size (%u)\n", ( long long )vcn, - ( unsigned long long )icx->ni->mft_no, ib_size, - icx->block_size ); - return -1; - } - return 0; + u32 ib_size = (unsigned)le32_to_cpu(ib->index.allocated_size) + 0x18; + + ntfs_log_trace("Entering\n"); + + if (!ntfs_is_indx_record(ib->magic)) { + + ntfs_log_error("Corrupt index block signature: vcn %lld inode " + "%llu\n", (long long)vcn, + (unsigned long long)icx->ni->mft_no); + return -1; + } + + if (sle64_to_cpu(ib->index_block_vcn) != vcn) { + + ntfs_log_error("Corrupt index block: VCN (%lld) is different " + "from expected VCN (%lld) in inode %llu\n", + (long long)sle64_to_cpu(ib->index_block_vcn), + (long long)vcn, + (unsigned long long)icx->ni->mft_no); + return -1; + } + + if (ib_size != icx->block_size) { + + ntfs_log_error("Corrupt index block : VCN (%lld) of inode %llu " + "has a size (%u) differing from the index " + "specified size (%u)\n", (long long)vcn, + (unsigned long long)icx->ni->mft_no, ib_size, + icx->block_size); + return -1; + } + return 0; } -static INDEX_ROOT *ntfs_ir_lookup( ntfs_inode *ni, ntfschar *name, - u32 name_len, ntfs_attr_search_ctx **ctx ) +static INDEX_ROOT *ntfs_ir_lookup(ntfs_inode *ni, ntfschar *name, + u32 name_len, ntfs_attr_search_ctx **ctx) { - ATTR_RECORD *a; - INDEX_ROOT *ir = NULL; + ATTR_RECORD *a; + INDEX_ROOT *ir = NULL; - ntfs_log_trace( "Entering\n" ); - - *ctx = ntfs_attr_get_search_ctx( ni, NULL ); - if ( !*ctx ) - return NULL; - - if ( ntfs_attr_lookup( AT_INDEX_ROOT, name, name_len, CASE_SENSITIVE, - 0, NULL, 0, *ctx ) ) - { - ntfs_log_perror( "Failed to lookup $INDEX_ROOT" ); - goto err_out; - } - - a = ( *ctx )->attr; - if ( a->non_resident ) - { - errno = EINVAL; - ntfs_log_perror( "Non-resident $INDEX_ROOT detected" ); - goto err_out; - } - - ir = ( INDEX_ROOT * )( ( char * )a + le16_to_cpu( a->value_offset ) ); + ntfs_log_trace("Entering\n"); + + *ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!*ctx) + return NULL; + + if (ntfs_attr_lookup(AT_INDEX_ROOT, name, name_len, CASE_SENSITIVE, + 0, NULL, 0, *ctx)) { + ntfs_log_perror("Failed to lookup $INDEX_ROOT"); + goto err_out; + } + + a = (*ctx)->attr; + if (a->non_resident) { + errno = EINVAL; + ntfs_log_perror("Non-resident $INDEX_ROOT detected"); + goto err_out; + } + + ir = (INDEX_ROOT *)((char *)a + le16_to_cpu(a->value_offset)); err_out: - if ( !ir ) - { - ntfs_attr_put_search_ctx( *ctx ); - *ctx = NULL; - } - return ir; + if (!ir) { + ntfs_attr_put_search_ctx(*ctx); + *ctx = NULL; + } + return ir; } -static INDEX_ROOT *ntfs_ir_lookup2( ntfs_inode *ni, ntfschar *name, u32 len ) +static INDEX_ROOT *ntfs_ir_lookup2(ntfs_inode *ni, ntfschar *name, u32 len) { - ntfs_attr_search_ctx *ctx; - INDEX_ROOT *ir; + ntfs_attr_search_ctx *ctx; + INDEX_ROOT *ir; - ir = ntfs_ir_lookup( ni, name, len, &ctx ); - if ( ir ) - ntfs_attr_put_search_ctx( ctx ); - return ir; + ir = ntfs_ir_lookup(ni, name, len, &ctx); + if (ir) + ntfs_attr_put_search_ctx(ctx); + return ir; } -/** +/** * Find a key in the index block. - * + * * Return values: - * STATUS_OK with errno set to ESUCCESS if we know for sure that the + * STATUS_OK with errno set to ESUCCESS if we know for sure that the * entry exists and @ie_out points to this entry. * STATUS_NOT_FOUND with errno set to ENOENT if we know for sure the * entry doesn't exist and @ie_out is the insertion point. @@ -496,175 +481,164 @@ static INDEX_ROOT *ntfs_ir_lookup2( ntfs_inode *ni, ntfschar *name, u32 len ) * @vcn will contain the node index block. * STATUS_ERROR with errno set if on unexpected error during lookup. */ -static int ntfs_ie_lookup( const void *key, const int key_len, - ntfs_index_context *icx, INDEX_HEADER *ih, - VCN *vcn, INDEX_ENTRY **ie_out ) +static int ntfs_ie_lookup(const void *key, const int key_len, + ntfs_index_context *icx, INDEX_HEADER *ih, + VCN *vcn, INDEX_ENTRY **ie_out) { - INDEX_ENTRY *ie; - u8 *index_end; - int rc, item = 0; + INDEX_ENTRY *ie; + u8 *index_end; + int rc, item = 0; + + ntfs_log_trace("Entering\n"); + + index_end = ntfs_ie_get_end(ih); + + /* + * Loop until we exceed valid memory (corruption case) or until we + * reach the last entry. + */ + for (ie = ntfs_ie_get_first(ih); ; ie = ntfs_ie_get_next(ie)) { + /* Bounds checks. */ + if ((u8 *)ie + sizeof(INDEX_ENTRY_HEADER) > index_end || + (u8 *)ie + le16_to_cpu(ie->length) > index_end) { + errno = ERANGE; + ntfs_log_error("Index entry out of bounds in inode " + "%llu.\n", + (unsigned long long)icx->ni->mft_no); + return STATUS_ERROR; + } + /* + * The last entry cannot contain a key. It can however contain + * a pointer to a child node in the B+tree so we just break out. + */ + if (ntfs_ie_end(ie)) + break; + /* + * Not a perfect match, need to do full blown collation so we + * know which way in the B+tree we have to go. + */ + if (!icx->collate) { + ntfs_log_error("Collation function not defined\n"); + errno = EOPNOTSUPP; + return STATUS_ERROR; + } + rc = icx->collate(icx->ni->vol, key, key_len, + &ie->key, le16_to_cpu(ie->key_length)); + if (rc == NTFS_COLLATION_ERROR) { + ntfs_log_error("Collation error. Perhaps a filename " + "contains invalid characters?\n"); + errno = ERANGE; + return STATUS_ERROR; + } + /* + * If @key collates before the key of the current entry, there + * is definitely no such key in this index but we might need to + * descend into the B+tree so we just break out of the loop. + */ + if (rc == -1) + break; + + if (!rc) { + *ie_out = ie; + errno = 0; + icx->parent_pos[icx->pindex] = item; + return STATUS_OK; + } + + item++; + } + /* + * We have finished with this index block without success. Check for the + * presence of a child node and if not present return with errno ENOENT, + * otherwise we will keep searching in another index block. + */ + if (!(ie->ie_flags & INDEX_ENTRY_NODE)) { + ntfs_log_debug("Index entry wasn't found.\n"); + *ie_out = ie; + errno = ENOENT; + return STATUS_NOT_FOUND; + } + + /* Get the starting vcn of the index_block holding the child node. */ + *vcn = ntfs_ie_get_vcn(ie); + if (*vcn < 0) { + errno = EINVAL; + ntfs_log_perror("Negative vcn in inode %llu", + (unsigned long long)icx->ni->mft_no); + return STATUS_ERROR; + } - ntfs_log_trace( "Entering\n" ); - - index_end = ntfs_ie_get_end( ih ); - - /* - * Loop until we exceed valid memory (corruption case) or until we - * reach the last entry. - */ - for ( ie = ntfs_ie_get_first( ih ); ; ie = ntfs_ie_get_next( ie ) ) - { - /* Bounds checks. */ - if ( ( u8 * )ie + sizeof( INDEX_ENTRY_HEADER ) > index_end || - ( u8 * )ie + le16_to_cpu( ie->length ) > index_end ) - { - errno = ERANGE; - ntfs_log_error( "Index entry out of bounds in inode " - "%llu.\n", - ( unsigned long long )icx->ni->mft_no ); - return STATUS_ERROR; - } - /* - * The last entry cannot contain a key. It can however contain - * a pointer to a child node in the B+tree so we just break out. - */ - if ( ntfs_ie_end( ie ) ) - break; - /* - * Not a perfect match, need to do full blown collation so we - * know which way in the B+tree we have to go. - */ - if ( !icx->collate ) - { - ntfs_log_error( "Collation function not defined\n" ); - errno = EOPNOTSUPP; - return STATUS_ERROR; - } - rc = icx->collate( icx->ni->vol, key, key_len, - &ie->key, le16_to_cpu( ie->key_length ) ); - if ( rc == NTFS_COLLATION_ERROR ) - { - ntfs_log_error( "Collation error. Perhaps a filename " - "contains invalid characters?\n" ); - errno = ERANGE; - return STATUS_ERROR; - } - /* - * If @key collates before the key of the current entry, there - * is definitely no such key in this index but we might need to - * descend into the B+tree so we just break out of the loop. - */ - if ( rc == -1 ) - break; - - if ( !rc ) - { - *ie_out = ie; - errno = 0; - icx->parent_pos[icx->pindex] = item; - return STATUS_OK; - } - - item++; - } - /* - * We have finished with this index block without success. Check for the - * presence of a child node and if not present return with errno ENOENT, - * otherwise we will keep searching in another index block. - */ - if ( !( ie->ie_flags & INDEX_ENTRY_NODE ) ) - { - ntfs_log_debug( "Index entry wasn't found.\n" ); - *ie_out = ie; - errno = ENOENT; - return STATUS_NOT_FOUND; - } - - /* Get the starting vcn of the index_block holding the child node. */ - *vcn = ntfs_ie_get_vcn( ie ); - if ( *vcn < 0 ) - { - errno = EINVAL; - ntfs_log_perror( "Negative vcn in inode %llu", - ( unsigned long long )icx->ni->mft_no ); - return STATUS_ERROR; - } - - ntfs_log_trace( "Parent entry number %d\n", item ); - icx->parent_pos[icx->pindex] = item; - - return STATUS_KEEP_SEARCHING; + ntfs_log_trace("Parent entry number %d\n", item); + icx->parent_pos[icx->pindex] = item; + + return STATUS_KEEP_SEARCHING; } -static ntfs_attr *ntfs_ia_open( ntfs_index_context *icx, ntfs_inode *ni ) +static ntfs_attr *ntfs_ia_open(ntfs_index_context *icx, ntfs_inode *ni) { - ntfs_attr *na; - - na = ntfs_attr_open( ni, AT_INDEX_ALLOCATION, icx->name, icx->name_len ); - if ( !na ) - { - ntfs_log_perror( "Failed to open index allocation of inode " - "%llu", ( unsigned long long )ni->mft_no ); - return NULL; - } - - return na; + ntfs_attr *na; + + na = ntfs_attr_open(ni, AT_INDEX_ALLOCATION, icx->name, icx->name_len); + if (!na) { + ntfs_log_perror("Failed to open index allocation of inode " + "%llu", (unsigned long long)ni->mft_no); + return NULL; + } + + return na; } -static int ntfs_ib_read( ntfs_index_context *icx, VCN vcn, INDEX_BLOCK *dst ) +static int ntfs_ib_read(ntfs_index_context *icx, VCN vcn, INDEX_BLOCK *dst) { - s64 pos, ret; + s64 pos, ret; - ntfs_log_trace( "vcn: %lld\n", ( long long )vcn ); + ntfs_log_trace("vcn: %lld\n", (long long)vcn); + + pos = ntfs_ib_vcn_to_pos(icx, vcn); - pos = ntfs_ib_vcn_to_pos( icx, vcn ); - - ret = ntfs_attr_mst_pread( icx->ia_na, pos, 1, icx->block_size, ( u8 * )dst ); - if ( ret != 1 ) - { - if ( ret == -1 ) - ntfs_log_perror( "Failed to read index block" ); - else - ntfs_log_error( "Failed to read full index block at " - "%lld\n", ( long long )pos ); - return -1; - } - - if ( ntfs_ia_check( icx, dst, vcn ) ) - return -1; - - return 0; + ret = ntfs_attr_mst_pread(icx->ia_na, pos, 1, icx->block_size, (u8 *)dst); + if (ret != 1) { + if (ret == -1) + ntfs_log_perror("Failed to read index block"); + else + ntfs_log_error("Failed to read full index block at " + "%lld\n", (long long)pos); + return -1; + } + + if (ntfs_ia_check(icx, dst, vcn)) + return -1; + + return 0; } -static int ntfs_icx_parent_inc( ntfs_index_context *icx ) +static int ntfs_icx_parent_inc(ntfs_index_context *icx) { - icx->pindex++; - if ( icx->pindex >= MAX_PARENT_VCN ) - { - errno = EOPNOTSUPP; - ntfs_log_perror( "Index is over %d level deep", MAX_PARENT_VCN ); - return STATUS_ERROR; - } - return STATUS_OK; + icx->pindex++; + if (icx->pindex >= MAX_PARENT_VCN) { + errno = EOPNOTSUPP; + ntfs_log_perror("Index is over %d level deep", MAX_PARENT_VCN); + return STATUS_ERROR; + } + return STATUS_OK; } -static int ntfs_icx_parent_dec( ntfs_index_context *icx ) +static int ntfs_icx_parent_dec(ntfs_index_context *icx) { - icx->pindex--; - if ( icx->pindex < 0 ) - { - errno = EINVAL; - ntfs_log_perror( "Corrupt index pointer (%d)", icx->pindex ); - return STATUS_ERROR; - } - return STATUS_OK; + icx->pindex--; + if (icx->pindex < 0) { + errno = EINVAL; + ntfs_log_perror("Corrupt index pointer (%d)", icx->pindex); + return STATUS_ERROR; + } + return STATUS_OK; } - + /** * ntfs_index_lookup - find a key in an index and return its index entry - * @key: [IN] key for which to search in the index - * @key_len: [IN] length of @key in bytes - * @icx: [IN/OUT] context describing the index and the returned entry + * @key: [IN] key for which to search in the index + * @key_len: [IN] length of @key in bytes + * @icx: [IN/OUT] context describing the index and the returned entry * * Before calling ntfs_index_lookup(), @icx must have been obtained from a * call to ntfs_index_ctx_get(). @@ -692,1266 +666,1206 @@ static int ntfs_icx_parent_dec( ntfs_index_context *icx ) * the call to ntfs_index_ctx_put() to ensure that the changes are written * to disk. */ -int ntfs_index_lookup( const void *key, const int key_len, ntfs_index_context *icx ) +int ntfs_index_lookup(const void *key, const int key_len, ntfs_index_context *icx) { - VCN old_vcn, vcn; - ntfs_inode *ni = icx->ni; - INDEX_ROOT *ir; - INDEX_ENTRY *ie; - INDEX_BLOCK *ib = NULL; - int ret, err = 0; + VCN old_vcn, vcn; + ntfs_inode *ni = icx->ni; + INDEX_ROOT *ir; + INDEX_ENTRY *ie; + INDEX_BLOCK *ib = NULL; + int ret, err = 0; - ntfs_log_trace( "Entering\n" ); + ntfs_log_trace("Entering\n"); + + if (!key || key_len <= 0) { + errno = EINVAL; + ntfs_log_perror("key: %p key_len: %d", key, key_len); + return -1; + } - if ( !key || key_len <= 0 ) - { - errno = EINVAL; - ntfs_log_perror( "key: %p key_len: %d", key, key_len ); - return -1; - } - - ir = ntfs_ir_lookup( ni, icx->name, icx->name_len, &icx->actx ); - if ( !ir ) - { - if ( errno == ENOENT ) - errno = EIO; - return -1; - } - - icx->block_size = le32_to_cpu( ir->index_block_size ); - if ( icx->block_size < NTFS_BLOCK_SIZE ) - { - errno = EINVAL; - ntfs_log_perror( "Index block size (%d) is smaller than the " - "sector size (%d)", icx->block_size, NTFS_BLOCK_SIZE ); - goto err_out; - } - - if ( ni->vol->cluster_size <= icx->block_size ) - icx->vcn_size_bits = ni->vol->cluster_size_bits; - else - icx->vcn_size_bits = ni->vol->sector_size_bits; - /* get the appropriate collation function */ - icx->collate = ntfs_get_collate_function( ir->collation_rule ); - if ( !icx->collate ) - { - err = errno = EOPNOTSUPP; - ntfs_log_perror( "Unknown collation rule 0x%x", - ( unsigned )le32_to_cpu( ir->collation_rule ) ); - goto err_out; - } - - old_vcn = VCN_INDEX_ROOT_PARENT; - /* - * FIXME: check for both ir and ib that the first index entry is - * within the index block. - */ - ret = ntfs_ie_lookup( key, key_len, icx, &ir->index, &vcn, &ie ); - if ( ret == STATUS_ERROR ) - { - err = errno; - goto err_out; - } - - icx->ir = ir; - - if ( ret != STATUS_KEEP_SEARCHING ) - { - /* STATUS_OK or STATUS_NOT_FOUND */ - err = errno; - icx->is_in_root = TRUE; - icx->parent_vcn[icx->pindex] = old_vcn; - goto done; - } - - /* Child node present, descend into it. */ - - icx->ia_na = ntfs_ia_open( icx, ni ); - if ( !icx->ia_na ) - goto err_out; - - ib = ntfs_malloc( icx->block_size ); - if ( !ib ) - { - err = errno; - goto err_out; - } + ir = ntfs_ir_lookup(ni, icx->name, icx->name_len, &icx->actx); + if (!ir) { + if (errno == ENOENT) + errno = EIO; + return -1; + } + + icx->block_size = le32_to_cpu(ir->index_block_size); + if (icx->block_size < NTFS_BLOCK_SIZE) { + errno = EINVAL; + ntfs_log_perror("Index block size (%d) is smaller than the " + "sector size (%d)", icx->block_size, NTFS_BLOCK_SIZE); + goto err_out; + } + if (ni->vol->cluster_size <= icx->block_size) + icx->vcn_size_bits = ni->vol->cluster_size_bits; + else + icx->vcn_size_bits = ni->vol->sector_size_bits; + /* get the appropriate collation function */ + icx->collate = ntfs_get_collate_function(ir->collation_rule); + if (!icx->collate) { + err = errno = EOPNOTSUPP; + ntfs_log_perror("Unknown collation rule 0x%x", + (unsigned)le32_to_cpu(ir->collation_rule)); + goto err_out; + } + + old_vcn = VCN_INDEX_ROOT_PARENT; + /* + * FIXME: check for both ir and ib that the first index entry is + * within the index block. + */ + ret = ntfs_ie_lookup(key, key_len, icx, &ir->index, &vcn, &ie); + if (ret == STATUS_ERROR) { + err = errno; + goto err_out; + } + + icx->ir = ir; + + if (ret != STATUS_KEEP_SEARCHING) { + /* STATUS_OK or STATUS_NOT_FOUND */ + err = errno; + icx->is_in_root = TRUE; + icx->parent_vcn[icx->pindex] = old_vcn; + goto done; + } + + /* Child node present, descend into it. */ + + icx->ia_na = ntfs_ia_open(icx, ni); + if (!icx->ia_na) + goto err_out; + + ib = ntfs_malloc(icx->block_size); + if (!ib) { + err = errno; + goto err_out; + } + descend_into_child_node: - icx->parent_vcn[icx->pindex] = old_vcn; - if ( ntfs_icx_parent_inc( icx ) ) - { - err = errno; - goto err_out; - } - old_vcn = vcn; + icx->parent_vcn[icx->pindex] = old_vcn; + if (ntfs_icx_parent_inc(icx)) { + err = errno; + goto err_out; + } + old_vcn = vcn; - ntfs_log_debug( "Descend into node with VCN %lld\n", ( long long )vcn ); + ntfs_log_debug("Descend into node with VCN %lld\n", (long long)vcn); + + if (ntfs_ib_read(icx, vcn, ib)) + goto err_out; + + ret = ntfs_ie_lookup(key, key_len, icx, &ib->index, &vcn, &ie); + if (ret != STATUS_KEEP_SEARCHING) { + err = errno; + if (ret == STATUS_ERROR) + goto err_out; + + /* STATUS_OK or STATUS_NOT_FOUND */ + icx->is_in_root = FALSE; + icx->ib = ib; + icx->parent_vcn[icx->pindex] = vcn; + goto done; + } - if ( ntfs_ib_read( icx, vcn, ib ) ) - goto err_out; - - ret = ntfs_ie_lookup( key, key_len, icx, &ib->index, &vcn, &ie ); - if ( ret != STATUS_KEEP_SEARCHING ) - { - err = errno; - if ( ret == STATUS_ERROR ) - goto err_out; - - /* STATUS_OK or STATUS_NOT_FOUND */ - icx->is_in_root = FALSE; - icx->ib = ib; - icx->parent_vcn[icx->pindex] = vcn; - goto done; - } - - if ( ( ib->index.ih_flags & NODE_MASK ) == LEAF_NODE ) - { - ntfs_log_error( "Index entry with child node found in a leaf " - "node in inode 0x%llx.\n", - ( unsigned long long )ni->mft_no ); - goto err_out; - } - - goto descend_into_child_node; + if ((ib->index.ih_flags & NODE_MASK) == LEAF_NODE) { + ntfs_log_error("Index entry with child node found in a leaf " + "node in inode 0x%llx.\n", + (unsigned long long)ni->mft_no); + goto err_out; + } + + goto descend_into_child_node; err_out: - free( ib ); - if ( !err ) - err = EIO; - errno = err; - return -1; + free(ib); + if (!err) + err = EIO; + errno = err; + return -1; done: - icx->entry = ie; - icx->data = ( u8 * )ie + offsetof( INDEX_ENTRY, key ); - icx->data_len = le16_to_cpu( ie->key_length ); - ntfs_log_trace( "Done.\n" ); - if ( err ) - { - errno = err; - return -1; - } - return 0; + icx->entry = ie; + icx->data = (u8 *)ie + offsetof(INDEX_ENTRY, key); + icx->data_len = le16_to_cpu(ie->key_length); + ntfs_log_trace("Done.\n"); + if (err) { + errno = err; + return -1; + } + return 0; } -static INDEX_BLOCK *ntfs_ib_alloc( VCN ib_vcn, u32 ib_size, - INDEX_HEADER_FLAGS node_type ) +static INDEX_BLOCK *ntfs_ib_alloc(VCN ib_vcn, u32 ib_size, + INDEX_HEADER_FLAGS node_type) { - INDEX_BLOCK *ib; - int ih_size = sizeof( INDEX_HEADER ); + INDEX_BLOCK *ib; + int ih_size = sizeof(INDEX_HEADER); + + ntfs_log_trace("ib_vcn: %lld ib_size: %u\n", (long long)ib_vcn, ib_size); + + ib = ntfs_calloc(ib_size); + if (!ib) + return NULL; + + ib->magic = magic_INDX; + ib->usa_ofs = cpu_to_le16(sizeof(INDEX_BLOCK)); + ib->usa_count = cpu_to_le16(ib_size / NTFS_BLOCK_SIZE + 1); + /* Set USN to 1 */ + *(u16 *)((char *)ib + le16_to_cpu(ib->usa_ofs)) = cpu_to_le16(1); + ib->lsn = cpu_to_le64(0); + + ib->index_block_vcn = cpu_to_sle64(ib_vcn); + + ib->index.entries_offset = cpu_to_le32((ih_size + + le16_to_cpu(ib->usa_count) * 2 + 7) & ~7); + ib->index.index_length = 0; + ib->index.allocated_size = cpu_to_le32(ib_size - + (sizeof(INDEX_BLOCK) - ih_size)); + ib->index.ih_flags = node_type; + + return ib; +} - ntfs_log_trace( "ib_vcn: %lld ib_size: %u\n", ( long long )ib_vcn, ib_size ); - - ib = ntfs_calloc( ib_size ); - if ( !ib ) - return NULL; - - ib->magic = magic_INDX; - ib->usa_ofs = cpu_to_le16( sizeof( INDEX_BLOCK ) ); - ib->usa_count = cpu_to_le16( ib_size / NTFS_BLOCK_SIZE + 1 ); - /* Set USN to 1 */ - *( u16 * )( ( char * )ib + le16_to_cpu( ib->usa_ofs ) ) = cpu_to_le16( 1 ); - ib->lsn = cpu_to_le64( 0 ); - - ib->index_block_vcn = cpu_to_sle64( ib_vcn ); - - ib->index.entries_offset = cpu_to_le32( ( ih_size + - le16_to_cpu( ib->usa_count ) * 2 + 7 ) & ~7 ); - ib->index.index_length = 0; - ib->index.allocated_size = cpu_to_le32( ib_size - - ( sizeof( INDEX_BLOCK ) - ih_size ) ); - ib->index.ih_flags = node_type; - - return ib; -} - -/** +/** * Find the median by going through all the entries */ -static INDEX_ENTRY *ntfs_ie_get_median( INDEX_HEADER *ih ) +static INDEX_ENTRY *ntfs_ie_get_median(INDEX_HEADER *ih) { - INDEX_ENTRY *ie, *ie_start; - u8 *ie_end; - int i = 0, median; - - ntfs_log_trace( "Entering\n" ); - - ie = ie_start = ntfs_ie_get_first( ih ); - ie_end = ( u8 * )ntfs_ie_get_end( ih ); - - while ( ( u8 * )ie < ie_end && !ntfs_ie_end( ie ) ) - { - ie = ntfs_ie_get_next( ie ); - i++; - } - /* - * NOTE: this could be also the entry at the half of the index block. - */ - median = i / 2 - 1; - - ntfs_log_trace( "Entries: %d median: %d\n", i, median ); - - for ( i = 0, ie = ie_start; i <= median; i++ ) - ie = ntfs_ie_get_next( ie ); - - return ie; + INDEX_ENTRY *ie, *ie_start; + u8 *ie_end; + int i = 0, median; + + ntfs_log_trace("Entering\n"); + + ie = ie_start = ntfs_ie_get_first(ih); + ie_end = (u8 *)ntfs_ie_get_end(ih); + + while ((u8 *)ie < ie_end && !ntfs_ie_end(ie)) { + ie = ntfs_ie_get_next(ie); + i++; + } + /* + * NOTE: this could be also the entry at the half of the index block. + */ + median = i / 2 - 1; + + ntfs_log_trace("Entries: %d median: %d\n", i, median); + + for (i = 0, ie = ie_start; i <= median; i++) + ie = ntfs_ie_get_next(ie); + + return ie; } -static s64 ntfs_ibm_vcn_to_pos( ntfs_index_context *icx, VCN vcn ) +static s64 ntfs_ibm_vcn_to_pos(ntfs_index_context *icx, VCN vcn) { - return ntfs_ib_vcn_to_pos( icx, vcn ) / icx->block_size; + return ntfs_ib_vcn_to_pos(icx, vcn) / icx->block_size; } -static s64 ntfs_ibm_pos_to_vcn( ntfs_index_context *icx, s64 pos ) +static s64 ntfs_ibm_pos_to_vcn(ntfs_index_context *icx, s64 pos) { - return ntfs_ib_pos_to_vcn( icx, pos * icx->block_size ); + return ntfs_ib_pos_to_vcn(icx, pos * icx->block_size); } -static int ntfs_ibm_add( ntfs_index_context *icx ) +static int ntfs_ibm_add(ntfs_index_context *icx) { - u8 bmp[8]; + u8 bmp[8]; - ntfs_log_trace( "Entering\n" ); - - if ( ntfs_attr_exist( icx->ni, AT_BITMAP, icx->name, icx->name_len ) ) - return STATUS_OK; - /* - * AT_BITMAP must be at least 8 bytes. - */ - memset( bmp, 0, sizeof( bmp ) ); - if ( ntfs_attr_add( icx->ni, AT_BITMAP, icx->name, icx->name_len, - bmp, sizeof( bmp ) ) ) - { - ntfs_log_perror( "Failed to add AT_BITMAP" ); - return STATUS_ERROR; - } - - return STATUS_OK; + ntfs_log_trace("Entering\n"); + + if (ntfs_attr_exist(icx->ni, AT_BITMAP, icx->name, icx->name_len)) + return STATUS_OK; + /* + * AT_BITMAP must be at least 8 bytes. + */ + memset(bmp, 0, sizeof(bmp)); + if (ntfs_attr_add(icx->ni, AT_BITMAP, icx->name, icx->name_len, + bmp, sizeof(bmp))) { + ntfs_log_perror("Failed to add AT_BITMAP"); + return STATUS_ERROR; + } + + return STATUS_OK; } -static int ntfs_ibm_modify( ntfs_index_context *icx, VCN vcn, int set ) +static int ntfs_ibm_modify(ntfs_index_context *icx, VCN vcn, int set) { - u8 byte; - s64 pos = ntfs_ibm_vcn_to_pos( icx, vcn ); - u32 bpos = pos / 8; - u32 bit = 1 << ( pos % 8 ); - ntfs_attr *na; - int ret = STATUS_ERROR; + u8 byte; + s64 pos = ntfs_ibm_vcn_to_pos(icx, vcn); + u32 bpos = pos / 8; + u32 bit = 1 << (pos % 8); + ntfs_attr *na; + int ret = STATUS_ERROR; - ntfs_log_trace( "%s vcn: %lld\n", set ? "set" : "clear", ( long long )vcn ); + ntfs_log_trace("%s vcn: %lld\n", set ? "set" : "clear", (long long)vcn); + + na = ntfs_attr_open(icx->ni, AT_BITMAP, icx->name, icx->name_len); + if (!na) { + ntfs_log_perror("Failed to open $BITMAP attribute"); + return -1; + } - na = ntfs_attr_open( icx->ni, AT_BITMAP, icx->name, icx->name_len ); - if ( !na ) - { - ntfs_log_perror( "Failed to open $BITMAP attribute" ); - return -1; - } + if (set) { + if (na->data_size < bpos + 1) { + if (ntfs_attr_truncate(na, (na->data_size + 8) & ~7)) { + ntfs_log_perror("Failed to truncate AT_BITMAP"); + goto err_na; + } + } + } + + if (ntfs_attr_pread(na, bpos, 1, &byte) != 1) { + ntfs_log_perror("Failed to read $BITMAP"); + goto err_na; + } - if ( set ) - { - if ( na->data_size < bpos + 1 ) - { - if ( ntfs_attr_truncate( na, ( na->data_size + 8 ) & ~7 ) ) - { - ntfs_log_perror( "Failed to truncate AT_BITMAP" ); - goto err_na; - } - } - } + if (set) + byte |= bit; + else + byte &= ~bit; + + if (ntfs_attr_pwrite(na, bpos, 1, &byte) != 1) { + ntfs_log_perror("Failed to write $Bitmap"); + goto err_na; + } - if ( ntfs_attr_pread( na, bpos, 1, &byte ) != 1 ) - { - ntfs_log_perror( "Failed to read $BITMAP" ); - goto err_na; - } - - if ( set ) - byte |= bit; - else - byte &= ~bit; - - if ( ntfs_attr_pwrite( na, bpos, 1, &byte ) != 1 ) - { - ntfs_log_perror( "Failed to write $Bitmap" ); - goto err_na; - } - - ret = STATUS_OK; + ret = STATUS_OK; err_na: - ntfs_attr_close( na ); - return ret; + ntfs_attr_close(na); + return ret; } -static int ntfs_ibm_set( ntfs_index_context *icx, VCN vcn ) +static int ntfs_ibm_set(ntfs_index_context *icx, VCN vcn) { - return ntfs_ibm_modify( icx, vcn, 1 ); + return ntfs_ibm_modify(icx, vcn, 1); } -static int ntfs_ibm_clear( ntfs_index_context *icx, VCN vcn ) +static int ntfs_ibm_clear(ntfs_index_context *icx, VCN vcn) { - return ntfs_ibm_modify( icx, vcn, 0 ); + return ntfs_ibm_modify(icx, vcn, 0); } -static VCN ntfs_ibm_get_free( ntfs_index_context *icx ) +static VCN ntfs_ibm_get_free(ntfs_index_context *icx) { - u8 *bm; - int bit; - s64 vcn, byte, size; + u8 *bm; + int bit; + s64 vcn, byte, size; - ntfs_log_trace( "Entering\n" ); + ntfs_log_trace("Entering\n"); + + bm = ntfs_attr_readall(icx->ni, AT_BITMAP, icx->name, icx->name_len, + &size); + if (!bm) + return (VCN)-1; + + for (byte = 0; byte < size; byte++) { + + if (bm[byte] == 255) + continue; + + for (bit = 0; bit < 8; bit++) { + if (!(bm[byte] & (1 << bit))) { + vcn = ntfs_ibm_pos_to_vcn(icx, byte * 8 + bit); + goto out; + } + } + } + + vcn = ntfs_ibm_pos_to_vcn(icx, size * 8); +out: + ntfs_log_trace("allocated vcn: %lld\n", (long long)vcn); - bm = ntfs_attr_readall( icx->ni, AT_BITMAP, icx->name, icx->name_len, - &size ); - if ( !bm ) - return ( VCN ) - 1; - - for ( byte = 0; byte < size; byte++ ) - { - - if ( bm[byte] == 255 ) - continue; - - for ( bit = 0; bit < 8; bit++ ) - { - if ( !( bm[byte] & ( 1 << bit ) ) ) - { - vcn = ntfs_ibm_pos_to_vcn( icx, byte * 8 + bit ); - goto out; - } - } - } - - vcn = ntfs_ibm_pos_to_vcn( icx, size * 8 ); -out: - ntfs_log_trace( "allocated vcn: %lld\n", ( long long )vcn ); - - if ( ntfs_ibm_set( icx, vcn ) ) - vcn = ( VCN ) - 1; - - free( bm ); - return vcn; + if (ntfs_ibm_set(icx, vcn)) + vcn = (VCN)-1; + + free(bm); + return vcn; } -static INDEX_BLOCK *ntfs_ir_to_ib( INDEX_ROOT *ir, VCN ib_vcn ) +static INDEX_BLOCK *ntfs_ir_to_ib(INDEX_ROOT *ir, VCN ib_vcn) { - INDEX_BLOCK *ib; - INDEX_ENTRY *ie_last; - char *ies_start, *ies_end; - int i; - - ntfs_log_trace( "Entering\n" ); - - ib = ntfs_ib_alloc( ib_vcn, le32_to_cpu( ir->index_block_size ), LEAF_NODE ); - if ( !ib ) - return NULL; - - ies_start = ( char * )ntfs_ie_get_first( &ir->index ); - ies_end = ( char * )ntfs_ie_get_end( &ir->index ); - ie_last = ntfs_ie_get_last( ( INDEX_ENTRY * )ies_start, ies_end ); - /* - * Copy all entries, including the termination entry - * as well, which can never have any data. - */ - i = ( char * )ie_last - ies_start + le16_to_cpu( ie_last->length ); - memcpy( ntfs_ie_get_first( &ib->index ), ies_start, i ); - - ib->index.ih_flags = ir->index.ih_flags; - ib->index.index_length = cpu_to_le32( i + - le32_to_cpu( ib->index.entries_offset ) ); - return ib; + INDEX_BLOCK *ib; + INDEX_ENTRY *ie_last; + char *ies_start, *ies_end; + int i; + + ntfs_log_trace("Entering\n"); + + ib = ntfs_ib_alloc(ib_vcn, le32_to_cpu(ir->index_block_size), LEAF_NODE); + if (!ib) + return NULL; + + ies_start = (char *)ntfs_ie_get_first(&ir->index); + ies_end = (char *)ntfs_ie_get_end(&ir->index); + ie_last = ntfs_ie_get_last((INDEX_ENTRY *)ies_start, ies_end); + /* + * Copy all entries, including the termination entry + * as well, which can never have any data. + */ + i = (char *)ie_last - ies_start + le16_to_cpu(ie_last->length); + memcpy(ntfs_ie_get_first(&ib->index), ies_start, i); + + ib->index.ih_flags = ir->index.ih_flags; + ib->index.index_length = cpu_to_le32(i + + le32_to_cpu(ib->index.entries_offset)); + return ib; } -static void ntfs_ir_nill( INDEX_ROOT *ir ) +static void ntfs_ir_nill(INDEX_ROOT *ir) { - INDEX_ENTRY *ie_last; - char *ies_start, *ies_end; - - ntfs_log_trace( "Entering\n" ); - /* - * TODO: This function could be much simpler. - */ - ies_start = ( char * )ntfs_ie_get_first( &ir->index ); - ies_end = ( char * )ntfs_ie_get_end( &ir->index ); - ie_last = ntfs_ie_get_last( ( INDEX_ENTRY * )ies_start, ies_end ); - /* - * Move the index root termination entry forward - */ - if ( ( char * )ie_last > ies_start ) - { - memmove( ies_start, ( char * )ie_last, le16_to_cpu( ie_last->length ) ); - ie_last = ( INDEX_ENTRY * )ies_start; - } + INDEX_ENTRY *ie_last; + char *ies_start, *ies_end; + + ntfs_log_trace("Entering\n"); + /* + * TODO: This function could be much simpler. + */ + ies_start = (char *)ntfs_ie_get_first(&ir->index); + ies_end = (char *)ntfs_ie_get_end(&ir->index); + ie_last = ntfs_ie_get_last((INDEX_ENTRY *)ies_start, ies_end); + /* + * Move the index root termination entry forward + */ + if ((char *)ie_last > ies_start) { + memmove(ies_start, (char *)ie_last, le16_to_cpu(ie_last->length)); + ie_last = (INDEX_ENTRY *)ies_start; + } } -static int ntfs_ib_copy_tail( ntfs_index_context *icx, INDEX_BLOCK *src, - INDEX_ENTRY *median, VCN new_vcn ) +static int ntfs_ib_copy_tail(ntfs_index_context *icx, INDEX_BLOCK *src, + INDEX_ENTRY *median, VCN new_vcn) { - u8 *ies_end; - INDEX_ENTRY *ie_head; /* first entry after the median */ - int tail_size, ret; - INDEX_BLOCK *dst; + u8 *ies_end; + INDEX_ENTRY *ie_head; /* first entry after the median */ + int tail_size, ret; + INDEX_BLOCK *dst; + + ntfs_log_trace("Entering\n"); + + dst = ntfs_ib_alloc(new_vcn, icx->block_size, + src->index.ih_flags & NODE_MASK); + if (!dst) + return STATUS_ERROR; + + ie_head = ntfs_ie_get_next(median); + + ies_end = (u8 *)ntfs_ie_get_end(&src->index); + tail_size = ies_end - (u8 *)ie_head; + memcpy(ntfs_ie_get_first(&dst->index), ie_head, tail_size); + + dst->index.index_length = cpu_to_le32(tail_size + + le32_to_cpu(dst->index.entries_offset)); + ret = ntfs_ib_write(icx, dst); - ntfs_log_trace( "Entering\n" ); - - dst = ntfs_ib_alloc( new_vcn, icx->block_size, - src->index.ih_flags & NODE_MASK ); - if ( !dst ) - return STATUS_ERROR; - - ie_head = ntfs_ie_get_next( median ); - - ies_end = ( u8 * )ntfs_ie_get_end( &src->index ); - tail_size = ies_end - ( u8 * )ie_head; - memcpy( ntfs_ie_get_first( &dst->index ), ie_head, tail_size ); - - dst->index.index_length = cpu_to_le32( tail_size + - le32_to_cpu( dst->index.entries_offset ) ); - ret = ntfs_ib_write( icx, dst ); - - free( dst ); - return ret; + free(dst); + return ret; } -static int ntfs_ib_cut_tail( ntfs_index_context *icx, INDEX_BLOCK *ib, - INDEX_ENTRY *ie ) +static int ntfs_ib_cut_tail(ntfs_index_context *icx, INDEX_BLOCK *ib, + INDEX_ENTRY *ie) { - char *ies_start, *ies_end; - INDEX_ENTRY *ie_last; + char *ies_start, *ies_end; + INDEX_ENTRY *ie_last; + + ntfs_log_trace("Entering\n"); + + ies_start = (char *)ntfs_ie_get_first(&ib->index); + ies_end = (char *)ntfs_ie_get_end(&ib->index); + + ie_last = ntfs_ie_get_last((INDEX_ENTRY *)ies_start, ies_end); + if (ie_last->ie_flags & INDEX_ENTRY_NODE) + ntfs_ie_set_vcn(ie_last, ntfs_ie_get_vcn(ie)); + + memcpy(ie, ie_last, le16_to_cpu(ie_last->length)); + + ib->index.index_length = cpu_to_le32(((char *)ie - ies_start) + + le16_to_cpu(ie->length) + le32_to_cpu(ib->index.entries_offset)); + + if (ntfs_ib_write(icx, ib)) + return STATUS_ERROR; + + return STATUS_OK; +} + +static int ntfs_ia_add(ntfs_index_context *icx) +{ + ntfs_log_trace("Entering\n"); - ntfs_log_trace( "Entering\n" ); + if (ntfs_ibm_add(icx)) + return -1; + + if (!ntfs_attr_exist(icx->ni, AT_INDEX_ALLOCATION, icx->name, icx->name_len)) { + + if (ntfs_attr_add(icx->ni, AT_INDEX_ALLOCATION, icx->name, + icx->name_len, NULL, 0)) { + ntfs_log_perror("Failed to add AT_INDEX_ALLOCATION"); + return -1; + } + } + + icx->ia_na = ntfs_ia_open(icx, icx->ni); + if (!icx->ia_na) + return -1; - ies_start = ( char * )ntfs_ie_get_first( &ib->index ); - ies_end = ( char * )ntfs_ie_get_end( &ib->index ); - - ie_last = ntfs_ie_get_last( ( INDEX_ENTRY * )ies_start, ies_end ); - if ( ie_last->ie_flags & INDEX_ENTRY_NODE ) - ntfs_ie_set_vcn( ie_last, ntfs_ie_get_vcn( ie ) ); - - memcpy( ie, ie_last, le16_to_cpu( ie_last->length ) ); - - ib->index.index_length = cpu_to_le32( ( ( char * )ie - ies_start ) + - le16_to_cpu( ie->length ) + le32_to_cpu( ib->index.entries_offset ) ); - - if ( ntfs_ib_write( icx, ib ) ) - return STATUS_ERROR; - - return STATUS_OK; + return 0; } -static int ntfs_ia_add( ntfs_index_context *icx ) +static int ntfs_ir_reparent(ntfs_index_context *icx) { - ntfs_log_trace( "Entering\n" ); + ntfs_attr_search_ctx *ctx = NULL; + INDEX_ROOT *ir; + INDEX_ENTRY *ie; + INDEX_BLOCK *ib = NULL; + VCN new_ib_vcn; + int ret = STATUS_ERROR; - if ( ntfs_ibm_add( icx ) ) - return -1; - - if ( !ntfs_attr_exist( icx->ni, AT_INDEX_ALLOCATION, icx->name, icx->name_len ) ) - { - - if ( ntfs_attr_add( icx->ni, AT_INDEX_ALLOCATION, icx->name, - icx->name_len, NULL, 0 ) ) - { - ntfs_log_perror( "Failed to add AT_INDEX_ALLOCATION" ); - return -1; - } - } - - icx->ia_na = ntfs_ia_open( icx, icx->ni ); - if ( !icx->ia_na ) - return -1; - - return 0; -} - -static int ntfs_ir_reparent( ntfs_index_context *icx ) -{ - ntfs_attr_search_ctx *ctx = NULL; - INDEX_ROOT *ir; - INDEX_ENTRY *ie; - INDEX_BLOCK *ib = NULL; - VCN new_ib_vcn; - int ret = STATUS_ERROR; - - ntfs_log_trace( "Entering\n" ); - - ir = ntfs_ir_lookup2( icx->ni, icx->name, icx->name_len ); - if ( !ir ) - goto out; - - if ( ( ir->index.ih_flags & NODE_MASK ) == SMALL_INDEX ) - if ( ntfs_ia_add( icx ) ) - goto out; - - new_ib_vcn = ntfs_ibm_get_free( icx ); - if ( new_ib_vcn == -1 ) - goto out; - - ir = ntfs_ir_lookup2( icx->ni, icx->name, icx->name_len ); - if ( !ir ) - goto clear_bmp; - - ib = ntfs_ir_to_ib( ir, new_ib_vcn ); - if ( ib == NULL ) - { - ntfs_log_perror( "Failed to move index root to index block" ); - goto clear_bmp; - } - - if ( ntfs_ib_write( icx, ib ) ) - goto clear_bmp; - - ir = ntfs_ir_lookup( icx->ni, icx->name, icx->name_len, &ctx ); - if ( !ir ) - goto clear_bmp; - - ntfs_ir_nill( ir ); - - ie = ntfs_ie_get_first( &ir->index ); - ie->ie_flags |= INDEX_ENTRY_NODE; - ie->length = cpu_to_le16( sizeof( INDEX_ENTRY_HEADER ) + sizeof( VCN ) ); - - ir->index.ih_flags = LARGE_INDEX; - ir->index.index_length = cpu_to_le32( le32_to_cpu( ir->index.entries_offset ) - + le16_to_cpu( ie->length ) ); - ir->index.allocated_size = ir->index.index_length; - - if ( ntfs_resident_attr_value_resize( ctx->mrec, ctx->attr, - sizeof( INDEX_ROOT ) - sizeof( INDEX_HEADER ) + - le32_to_cpu( ir->index.allocated_size ) ) ) - /* FIXME: revert index root */ - goto clear_bmp; - /* - * FIXME: do it earlier if we have enough space in IR (should always), - * so in error case we wouldn't lose the IB. - */ - ntfs_ie_set_vcn( ie, new_ib_vcn ); - - ret = STATUS_OK; + ntfs_log_trace("Entering\n"); + + ir = ntfs_ir_lookup2(icx->ni, icx->name, icx->name_len); + if (!ir) + goto out; + + if ((ir->index.ih_flags & NODE_MASK) == SMALL_INDEX) + if (ntfs_ia_add(icx)) + goto out; + + new_ib_vcn = ntfs_ibm_get_free(icx); + if (new_ib_vcn == -1) + goto out; + + ir = ntfs_ir_lookup2(icx->ni, icx->name, icx->name_len); + if (!ir) + goto clear_bmp; + + ib = ntfs_ir_to_ib(ir, new_ib_vcn); + if (ib == NULL) { + ntfs_log_perror("Failed to move index root to index block"); + goto clear_bmp; + } + + if (ntfs_ib_write(icx, ib)) + goto clear_bmp; + + ir = ntfs_ir_lookup(icx->ni, icx->name, icx->name_len, &ctx); + if (!ir) + goto clear_bmp; + + ntfs_ir_nill(ir); + + ie = ntfs_ie_get_first(&ir->index); + ie->ie_flags |= INDEX_ENTRY_NODE; + ie->length = cpu_to_le16(sizeof(INDEX_ENTRY_HEADER) + sizeof(VCN)); + + ir->index.ih_flags = LARGE_INDEX; + ir->index.index_length = cpu_to_le32(le32_to_cpu(ir->index.entries_offset) + + le16_to_cpu(ie->length)); + ir->index.allocated_size = ir->index.index_length; + + if (ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr, + sizeof(INDEX_ROOT) - sizeof(INDEX_HEADER) + + le32_to_cpu(ir->index.allocated_size))) + /* FIXME: revert index root */ + goto clear_bmp; + /* + * FIXME: do it earlier if we have enough space in IR (should always), + * so in error case we wouldn't lose the IB. + */ + ntfs_ie_set_vcn(ie, new_ib_vcn); + + ret = STATUS_OK; err_out: - free( ib ); - ntfs_attr_put_search_ctx( ctx ); + free(ib); + ntfs_attr_put_search_ctx(ctx); out: - return ret; + return ret; clear_bmp: - ntfs_ibm_clear( icx, new_ib_vcn ); - goto err_out; + ntfs_ibm_clear(icx, new_ib_vcn); + goto err_out; } /** * ntfs_ir_truncate - Truncate index root attribute - * + * * Returns STATUS_OK, STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT or STATUS_ERROR. */ -static int ntfs_ir_truncate( ntfs_index_context *icx, int data_size ) -{ - ntfs_attr *na; - int ret; +static int ntfs_ir_truncate(ntfs_index_context *icx, int data_size) +{ + ntfs_attr *na; + int ret; - ntfs_log_trace( "Entering\n" ); - - na = ntfs_attr_open( icx->ni, AT_INDEX_ROOT, icx->name, icx->name_len ); - if ( !na ) - { - ntfs_log_perror( "Failed to open INDEX_ROOT" ); - return STATUS_ERROR; - } - /* - * INDEX_ROOT must be resident and its entries can be moved to - * INDEX_BLOCK, so ENOSPC isn't a real error. - */ - ret = ntfs_attr_truncate( na, data_size + offsetof( INDEX_ROOT, index ) ); - if ( ret == STATUS_OK ) - { - - icx->ir = ntfs_ir_lookup2( icx->ni, icx->name, icx->name_len ); - if ( !icx->ir ) - return STATUS_ERROR; - - icx->ir->index.allocated_size = cpu_to_le32( data_size ); - - } - else if ( ret == STATUS_ERROR ) - ntfs_log_perror( "Failed to truncate INDEX_ROOT" ); - - ntfs_attr_close( na ); - return ret; + ntfs_log_trace("Entering\n"); + + na = ntfs_attr_open(icx->ni, AT_INDEX_ROOT, icx->name, icx->name_len); + if (!na) { + ntfs_log_perror("Failed to open INDEX_ROOT"); + return STATUS_ERROR; + } + /* + * INDEX_ROOT must be resident and its entries can be moved to + * INDEX_BLOCK, so ENOSPC isn't a real error. + */ + ret = ntfs_attr_truncate(na, data_size + offsetof(INDEX_ROOT, index)); + if (ret == STATUS_OK) { + + icx->ir = ntfs_ir_lookup2(icx->ni, icx->name, icx->name_len); + if (!icx->ir) + return STATUS_ERROR; + + icx->ir->index.allocated_size = cpu_to_le32(data_size); + + } else if (ret == STATUS_ERROR) + ntfs_log_perror("Failed to truncate INDEX_ROOT"); + + ntfs_attr_close(na); + return ret; } - + /** * ntfs_ir_make_space - Make more space for the index root attribute - * + * * On success return STATUS_OK or STATUS_KEEP_SEARCHING. * On error return STATUS_ERROR. */ -static int ntfs_ir_make_space( ntfs_index_context *icx, int data_size ) -{ - int ret; +static int ntfs_ir_make_space(ntfs_index_context *icx, int data_size) +{ + int ret; - ntfs_log_trace( "Entering\n" ); + ntfs_log_trace("Entering\n"); - ret = ntfs_ir_truncate( icx, data_size ); - if ( ret == STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT ) - { + ret = ntfs_ir_truncate(icx, data_size); + if (ret == STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT) { + + ret = ntfs_ir_reparent(icx); + if (ret == STATUS_OK) + ret = STATUS_KEEP_SEARCHING; + else + ntfs_log_perror("Failed to nodify INDEX_ROOT"); + } - ret = ntfs_ir_reparent( icx ); - if ( ret == STATUS_OK ) - ret = STATUS_KEEP_SEARCHING; - else - ntfs_log_perror( "Failed to nodify INDEX_ROOT" ); - } - - return ret; + return ret; } /* * NOTE: 'ie' must be a copy of a real index entry. */ -static int ntfs_ie_add_vcn( INDEX_ENTRY **ie ) +static int ntfs_ie_add_vcn(INDEX_ENTRY **ie) { - INDEX_ENTRY *p, *old = *ie; + INDEX_ENTRY *p, *old = *ie; + + old->length = cpu_to_le16(le16_to_cpu(old->length) + sizeof(VCN)); + p = realloc(old, le16_to_cpu(old->length)); + if (!p) + return STATUS_ERROR; + + p->ie_flags |= INDEX_ENTRY_NODE; + *ie = p; - old->length = cpu_to_le16( le16_to_cpu( old->length ) + sizeof( VCN ) ); - p = realloc( old, le16_to_cpu( old->length ) ); - if ( !p ) - return STATUS_ERROR; - - p->ie_flags |= INDEX_ENTRY_NODE; - *ie = p; - - return STATUS_OK; + return STATUS_OK; } -static int ntfs_ih_insert( INDEX_HEADER *ih, INDEX_ENTRY *orig_ie, VCN new_vcn, - int pos ) +static int ntfs_ih_insert(INDEX_HEADER *ih, INDEX_ENTRY *orig_ie, VCN new_vcn, + int pos) { - INDEX_ENTRY *ie_node, *ie; - int ret = STATUS_ERROR; - VCN old_vcn; + INDEX_ENTRY *ie_node, *ie; + int ret = STATUS_ERROR; + VCN old_vcn; + + ntfs_log_trace("Entering\n"); + + ie = ntfs_ie_dup(orig_ie); + if (!ie) + return STATUS_ERROR; + + if (!(ie->ie_flags & INDEX_ENTRY_NODE)) + if (ntfs_ie_add_vcn(&ie)) + goto out; - ntfs_log_trace( "Entering\n" ); - - ie = ntfs_ie_dup( orig_ie ); - if ( !ie ) - return STATUS_ERROR; - - if ( !( ie->ie_flags & INDEX_ENTRY_NODE ) ) - if ( ntfs_ie_add_vcn( &ie ) ) - goto out; - - ie_node = ntfs_ie_get_by_pos( ih, pos ); - old_vcn = ntfs_ie_get_vcn( ie_node ); - ntfs_ie_set_vcn( ie_node, new_vcn ); - - ntfs_ie_insert( ih, ie, ie_node ); - ntfs_ie_set_vcn( ie_node, old_vcn ); - ret = STATUS_OK; -out: - free( ie ); - - return ret; + ie_node = ntfs_ie_get_by_pos(ih, pos); + old_vcn = ntfs_ie_get_vcn(ie_node); + ntfs_ie_set_vcn(ie_node, new_vcn); + + ntfs_ie_insert(ih, ie, ie_node); + ntfs_ie_set_vcn(ie_node, old_vcn); + ret = STATUS_OK; +out: + free(ie); + + return ret; } -static VCN ntfs_icx_parent_vcn( ntfs_index_context *icx ) +static VCN ntfs_icx_parent_vcn(ntfs_index_context *icx) { - return icx->parent_vcn[icx->pindex]; + return icx->parent_vcn[icx->pindex]; } -static VCN ntfs_icx_parent_pos( ntfs_index_context *icx ) +static VCN ntfs_icx_parent_pos(ntfs_index_context *icx) { - return icx->parent_pos[icx->pindex]; + return icx->parent_pos[icx->pindex]; } -static int ntfs_ir_insert_median( ntfs_index_context *icx, INDEX_ENTRY *median, - VCN new_vcn ) +static int ntfs_ir_insert_median(ntfs_index_context *icx, INDEX_ENTRY *median, + VCN new_vcn) { - u32 new_size; - int ret; + u32 new_size; + int ret; + + ntfs_log_trace("Entering\n"); + + icx->ir = ntfs_ir_lookup2(icx->ni, icx->name, icx->name_len); + if (!icx->ir) + return STATUS_ERROR; - ntfs_log_trace( "Entering\n" ); + new_size = le32_to_cpu(icx->ir->index.index_length) + + le16_to_cpu(median->length); + if (!(median->ie_flags & INDEX_ENTRY_NODE)) + new_size += sizeof(VCN); - icx->ir = ntfs_ir_lookup2( icx->ni, icx->name, icx->name_len ); - if ( !icx->ir ) - return STATUS_ERROR; + ret = ntfs_ir_make_space(icx, new_size); + if (ret != STATUS_OK) + return ret; + + icx->ir = ntfs_ir_lookup2(icx->ni, icx->name, icx->name_len); + if (!icx->ir) + return STATUS_ERROR; - new_size = le32_to_cpu( icx->ir->index.index_length ) + - le16_to_cpu( median->length ); - if ( !( median->ie_flags & INDEX_ENTRY_NODE ) ) - new_size += sizeof( VCN ); - - ret = ntfs_ir_make_space( icx, new_size ); - if ( ret != STATUS_OK ) - return ret; - - icx->ir = ntfs_ir_lookup2( icx->ni, icx->name, icx->name_len ); - if ( !icx->ir ) - return STATUS_ERROR; - - return ntfs_ih_insert( &icx->ir->index, median, new_vcn, - ntfs_icx_parent_pos( icx ) ); + return ntfs_ih_insert(&icx->ir->index, median, new_vcn, + ntfs_icx_parent_pos(icx)); } -static int ntfs_ib_split( ntfs_index_context *icx, INDEX_BLOCK *ib ); +static int ntfs_ib_split(ntfs_index_context *icx, INDEX_BLOCK *ib); /** * On success return STATUS_OK or STATUS_KEEP_SEARCHING. * On error return STATUS_ERROR. */ -static int ntfs_ib_insert( ntfs_index_context *icx, INDEX_ENTRY *ie, VCN new_vcn ) -{ - INDEX_BLOCK *ib; - u32 idx_size, allocated_size; - int err = STATUS_ERROR; - VCN old_vcn; +static int ntfs_ib_insert(ntfs_index_context *icx, INDEX_ENTRY *ie, VCN new_vcn) +{ + INDEX_BLOCK *ib; + u32 idx_size, allocated_size; + int err = STATUS_ERROR; + VCN old_vcn; - ntfs_log_trace( "Entering\n" ); + ntfs_log_trace("Entering\n"); + + ib = ntfs_malloc(icx->block_size); + if (!ib) + return -1; + + old_vcn = ntfs_icx_parent_vcn(icx); + + if (ntfs_ib_read(icx, old_vcn, ib)) + goto err_out; - ib = ntfs_malloc( icx->block_size ); - if ( !ib ) - return -1; - - old_vcn = ntfs_icx_parent_vcn( icx ); - - if ( ntfs_ib_read( icx, old_vcn, ib ) ) - goto err_out; - - idx_size = le32_to_cpu( ib->index.index_length ); - allocated_size = le32_to_cpu( ib->index.allocated_size ); - /* FIXME: sizeof(VCN) should be included only if ie has no VCN */ - if ( idx_size + le16_to_cpu( ie->length ) + sizeof( VCN ) > allocated_size ) - { - err = ntfs_ib_split( icx, ib ); - if ( err == STATUS_OK ) - err = STATUS_KEEP_SEARCHING; - goto err_out; - } - - if ( ntfs_ih_insert( &ib->index, ie, new_vcn, ntfs_icx_parent_pos( icx ) ) ) - goto err_out; - - if ( ntfs_ib_write( icx, ib ) ) - goto err_out; - - err = STATUS_OK; -err_out: - free( ib ); - return err; + idx_size = le32_to_cpu(ib->index.index_length); + allocated_size = le32_to_cpu(ib->index.allocated_size); + /* FIXME: sizeof(VCN) should be included only if ie has no VCN */ + if (idx_size + le16_to_cpu(ie->length) + sizeof(VCN) > allocated_size) { + err = ntfs_ib_split(icx, ib); + if (err == STATUS_OK) + err = STATUS_KEEP_SEARCHING; + goto err_out; + } + + if (ntfs_ih_insert(&ib->index, ie, new_vcn, ntfs_icx_parent_pos(icx))) + goto err_out; + + if (ntfs_ib_write(icx, ib)) + goto err_out; + + err = STATUS_OK; +err_out: + free(ib); + return err; } /** * ntfs_ib_split - Split an index block - * + * * On success return STATUS_OK or STATUS_KEEP_SEARCHING. * On error return is STATUS_ERROR. */ -static int ntfs_ib_split( ntfs_index_context *icx, INDEX_BLOCK *ib ) -{ - INDEX_ENTRY *median; - VCN new_vcn; - int ret; +static int ntfs_ib_split(ntfs_index_context *icx, INDEX_BLOCK *ib) +{ + INDEX_ENTRY *median; + VCN new_vcn; + int ret; - ntfs_log_trace( "Entering\n" ); - - if ( ntfs_icx_parent_dec( icx ) ) - return STATUS_ERROR; - - median = ntfs_ie_get_median( &ib->index ); - new_vcn = ntfs_ibm_get_free( icx ); - if ( new_vcn == -1 ) - return STATUS_ERROR; - - if ( ntfs_ib_copy_tail( icx, ib, median, new_vcn ) ) - { - ntfs_ibm_clear( icx, new_vcn ); - return STATUS_ERROR; - } - - if ( ntfs_icx_parent_vcn( icx ) == VCN_INDEX_ROOT_PARENT ) - ret = ntfs_ir_insert_median( icx, median, new_vcn ); - else - ret = ntfs_ib_insert( icx, median, new_vcn ); - - if ( ret != STATUS_OK ) - { - ntfs_ibm_clear( icx, new_vcn ); - return ret; - } - - ret = ntfs_ib_cut_tail( icx, ib, median ); - - return ret; + ntfs_log_trace("Entering\n"); + + if (ntfs_icx_parent_dec(icx)) + return STATUS_ERROR; + + median = ntfs_ie_get_median(&ib->index); + new_vcn = ntfs_ibm_get_free(icx); + if (new_vcn == -1) + return STATUS_ERROR; + + if (ntfs_ib_copy_tail(icx, ib, median, new_vcn)) { + ntfs_ibm_clear(icx, new_vcn); + return STATUS_ERROR; + } + + if (ntfs_icx_parent_vcn(icx) == VCN_INDEX_ROOT_PARENT) + ret = ntfs_ir_insert_median(icx, median, new_vcn); + else + ret = ntfs_ib_insert(icx, median, new_vcn); + + if (ret != STATUS_OK) { + ntfs_ibm_clear(icx, new_vcn); + return ret; + } + + ret = ntfs_ib_cut_tail(icx, ib, median); + + return ret; } /* JPA static */ -int ntfs_ie_add( ntfs_index_context *icx, INDEX_ENTRY *ie ) +int ntfs_ie_add(ntfs_index_context *icx, INDEX_ENTRY *ie) { - INDEX_HEADER *ih; - int allocated_size, new_size; - int ret = STATUS_ERROR; - + INDEX_HEADER *ih; + int allocated_size, new_size; + int ret = STATUS_ERROR; + #ifdef DEBUG - /* removed by JPA to make function usable for security indexes - char *fn; - fn = ntfs_ie_filename_get(ie); - ntfs_log_trace("file: '%s'\n", fn); - ntfs_attr_name_free(&fn); - */ +/* removed by JPA to make function usable for security indexes + char *fn; + fn = ntfs_ie_filename_get(ie); + ntfs_log_trace("file: '%s'\n", fn); + ntfs_attr_name_free(&fn); +*/ #endif - - while ( 1 ) - { - - if ( !ntfs_index_lookup( &ie->key, le16_to_cpu( ie->key_length ), icx ) ) - { - errno = EEXIST; - ntfs_log_perror( "Index already have such entry" ); - goto err_out; - } - if ( errno != ENOENT ) - { - ntfs_log_perror( "Failed to find place for new entry" ); - goto err_out; - } - - if ( icx->is_in_root ) - ih = &icx->ir->index; - else - ih = &icx->ib->index; - - allocated_size = le32_to_cpu( ih->allocated_size ); - new_size = le32_to_cpu( ih->index_length ) + le16_to_cpu( ie->length ); - - if ( new_size <= allocated_size ) - break; - - ntfs_log_trace( "index block sizes: allocated: %d needed: %d\n", - allocated_size, new_size ); - - if ( icx->is_in_root ) - { - if ( ntfs_ir_make_space( icx, new_size ) == STATUS_ERROR ) - goto err_out; - } - else - { - if ( ntfs_ib_split( icx, icx->ib ) == STATUS_ERROR ) - goto err_out; - } - - ntfs_inode_mark_dirty( icx->actx->ntfs_ino ); - ntfs_index_ctx_reinit( icx ); - } - - ntfs_ie_insert( ih, ie, icx->entry ); - ntfs_index_entry_mark_dirty( icx ); - - ret = STATUS_OK; + + while (1) { + + if (!ntfs_index_lookup(&ie->key, le16_to_cpu(ie->key_length), icx)) { + errno = EEXIST; + ntfs_log_perror("Index already have such entry"); + goto err_out; + } + if (errno != ENOENT) { + ntfs_log_perror("Failed to find place for new entry"); + goto err_out; + } + + if (icx->is_in_root) + ih = &icx->ir->index; + else + ih = &icx->ib->index; + + allocated_size = le32_to_cpu(ih->allocated_size); + new_size = le32_to_cpu(ih->index_length) + le16_to_cpu(ie->length); + + if (new_size <= allocated_size) + break; + + ntfs_log_trace("index block sizes: allocated: %d needed: %d\n", + allocated_size, new_size); + + if (icx->is_in_root) { + if (ntfs_ir_make_space(icx, new_size) == STATUS_ERROR) + goto err_out; + } else { + if (ntfs_ib_split(icx, icx->ib) == STATUS_ERROR) + goto err_out; + } + + ntfs_inode_mark_dirty(icx->actx->ntfs_ino); + ntfs_index_ctx_reinit(icx); + } + + ntfs_ie_insert(ih, ie, icx->entry); + ntfs_index_entry_mark_dirty(icx); + + ret = STATUS_OK; err_out: - ntfs_log_trace( "%s\n", ret ? "Failed" : "Done" ); - return ret; + ntfs_log_trace("%s\n", ret ? "Failed" : "Done"); + return ret; } /** * ntfs_index_add_filename - add filename to directory index - * @ni: ntfs inode describing directory to which index add filename - * @fn: FILE_NAME attribute to add - * @mref: reference of the inode which @fn describes + * @ni: ntfs inode describing directory to which index add filename + * @fn: FILE_NAME attribute to add + * @mref: reference of the inode which @fn describes * * Return 0 on success or -1 on error with errno set to the error code. */ -int ntfs_index_add_filename( ntfs_inode *ni, FILE_NAME_ATTR *fn, MFT_REF mref ) +int ntfs_index_add_filename(ntfs_inode *ni, FILE_NAME_ATTR *fn, MFT_REF mref) { - INDEX_ENTRY *ie; - ntfs_index_context *icx; - int fn_size, ie_size, err, ret = -1; + INDEX_ENTRY *ie; + ntfs_index_context *icx; + int fn_size, ie_size, err, ret = -1; - ntfs_log_trace( "Entering\n" ); + ntfs_log_trace("Entering\n"); + + if (!ni || !fn) { + ntfs_log_error("Invalid arguments.\n"); + errno = EINVAL; + return -1; + } + + fn_size = (fn->file_name_length * sizeof(ntfschar)) + + sizeof(FILE_NAME_ATTR); + ie_size = (sizeof(INDEX_ENTRY_HEADER) + fn_size + 7) & ~7; + + ie = ntfs_calloc(ie_size); + if (!ie) + return -1; - if ( !ni || !fn ) - { - ntfs_log_error( "Invalid arguments.\n" ); - errno = EINVAL; - return -1; - } - - fn_size = ( fn->file_name_length * sizeof( ntfschar ) ) + - sizeof( FILE_NAME_ATTR ); - ie_size = ( sizeof( INDEX_ENTRY_HEADER ) + fn_size + 7 ) & ~7; - - ie = ntfs_calloc( ie_size ); - if ( !ie ) - return -1; - - ie->indexed_file = cpu_to_le64( mref ); - ie->length = cpu_to_le16( ie_size ); - ie->key_length = cpu_to_le16( fn_size ); - memcpy( &ie->key, fn, fn_size ); - - icx = ntfs_index_ctx_get( ni, NTFS_INDEX_I30, 4 ); - if ( !icx ) - goto out; - - ret = ntfs_ie_add( icx, ie ); - err = errno; - ntfs_index_ctx_put( icx ); - errno = err; + ie->indexed_file = cpu_to_le64(mref); + ie->length = cpu_to_le16(ie_size); + ie->key_length = cpu_to_le16(fn_size); + memcpy(&ie->key, fn, fn_size); + + icx = ntfs_index_ctx_get(ni, NTFS_INDEX_I30, 4); + if (!icx) + goto out; + + ret = ntfs_ie_add(icx, ie); + err = errno; + ntfs_index_ctx_put(icx); + errno = err; out: - free( ie ); - return ret; + free(ie); + return ret; } -static int ntfs_ih_takeout( ntfs_index_context *icx, INDEX_HEADER *ih, - INDEX_ENTRY *ie, INDEX_BLOCK *ib ) +static int ntfs_ih_takeout(ntfs_index_context *icx, INDEX_HEADER *ih, + INDEX_ENTRY *ie, INDEX_BLOCK *ib) { - INDEX_ENTRY *ie_roam; - int ret = STATUS_ERROR; + INDEX_ENTRY *ie_roam; + int ret = STATUS_ERROR; + + ntfs_log_trace("Entering\n"); + + ie_roam = ntfs_ie_dup_novcn(ie); + if (!ie_roam) + return STATUS_ERROR; - ntfs_log_trace( "Entering\n" ); + ntfs_ie_delete(ih, ie); - ie_roam = ntfs_ie_dup_novcn( ie ); - if ( !ie_roam ) - return STATUS_ERROR; + if (ntfs_icx_parent_vcn(icx) == VCN_INDEX_ROOT_PARENT) + ntfs_inode_mark_dirty(icx->actx->ntfs_ino); + else + if (ntfs_ib_write(icx, ib)) + goto out; + + ntfs_index_ctx_reinit(icx); - ntfs_ie_delete( ih, ie ); - - if ( ntfs_icx_parent_vcn( icx ) == VCN_INDEX_ROOT_PARENT ) - ntfs_inode_mark_dirty( icx->actx->ntfs_ino ); - else if ( ntfs_ib_write( icx, ib ) ) - goto out; - - ntfs_index_ctx_reinit( icx ); - - ret = ntfs_ie_add( icx, ie_roam ); + ret = ntfs_ie_add(icx, ie_roam); out: - free( ie_roam ); - return ret; + free(ie_roam); + return ret; } /** * Used if an empty index block to be deleted has END entry as the parent * in the INDEX_ROOT which is the only one there. */ -static void ntfs_ir_leafify( ntfs_index_context *icx, INDEX_HEADER *ih ) +static void ntfs_ir_leafify(ntfs_index_context *icx, INDEX_HEADER *ih) { - INDEX_ENTRY *ie; - - ntfs_log_trace( "Entering\n" ); - - ie = ntfs_ie_get_first( ih ); - ie->ie_flags &= ~INDEX_ENTRY_NODE; - ie->length = cpu_to_le16( le16_to_cpu( ie->length ) - sizeof( VCN ) ); - - ih->index_length = cpu_to_le32( le32_to_cpu( ih->index_length ) - sizeof( VCN ) ); - ih->ih_flags &= ~LARGE_INDEX; - - /* Not fatal error */ - ntfs_ir_truncate( icx, le32_to_cpu( ih->index_length ) ); + INDEX_ENTRY *ie; + + ntfs_log_trace("Entering\n"); + + ie = ntfs_ie_get_first(ih); + ie->ie_flags &= ~INDEX_ENTRY_NODE; + ie->length = cpu_to_le16(le16_to_cpu(ie->length) - sizeof(VCN)); + + ih->index_length = cpu_to_le32(le32_to_cpu(ih->index_length) - sizeof(VCN)); + ih->ih_flags &= ~LARGE_INDEX; + + /* Not fatal error */ + ntfs_ir_truncate(icx, le32_to_cpu(ih->index_length)); } /** - * Used if an empty index block to be deleted has END entry as the parent + * Used if an empty index block to be deleted has END entry as the parent * in the INDEX_ROOT which is not the only one there. */ -static int ntfs_ih_reparent_end( ntfs_index_context *icx, INDEX_HEADER *ih, - INDEX_BLOCK *ib ) +static int ntfs_ih_reparent_end(ntfs_index_context *icx, INDEX_HEADER *ih, + INDEX_BLOCK *ib) { - INDEX_ENTRY *ie, *ie_prev; - - ntfs_log_trace( "Entering\n" ); - - ie = ntfs_ie_get_by_pos( ih, ntfs_icx_parent_pos( icx ) ); - ie_prev = ntfs_ie_prev( ih, ie ); - - ntfs_ie_set_vcn( ie, ntfs_ie_get_vcn( ie_prev ) ); - - return ntfs_ih_takeout( icx, ih, ie_prev, ib ); + INDEX_ENTRY *ie, *ie_prev; + + ntfs_log_trace("Entering\n"); + + ie = ntfs_ie_get_by_pos(ih, ntfs_icx_parent_pos(icx)); + ie_prev = ntfs_ie_prev(ih, ie); + + ntfs_ie_set_vcn(ie, ntfs_ie_get_vcn(ie_prev)); + + return ntfs_ih_takeout(icx, ih, ie_prev, ib); } -static int ntfs_index_rm_leaf( ntfs_index_context *icx ) +static int ntfs_index_rm_leaf(ntfs_index_context *icx) { - INDEX_BLOCK *ib = NULL; - INDEX_HEADER *parent_ih; - INDEX_ENTRY *ie; - int ret = STATUS_ERROR; + INDEX_BLOCK *ib = NULL; + INDEX_HEADER *parent_ih; + INDEX_ENTRY *ie; + int ret = STATUS_ERROR; + + ntfs_log_trace("pindex: %d\n", icx->pindex); + + if (ntfs_icx_parent_dec(icx)) + return STATUS_ERROR; - ntfs_log_trace( "pindex: %d\n", icx->pindex ); - - if ( ntfs_icx_parent_dec( icx ) ) - return STATUS_ERROR; - - if ( ntfs_ibm_clear( icx, icx->parent_vcn[icx->pindex + 1] ) ) - return STATUS_ERROR; - - if ( ntfs_icx_parent_vcn( icx ) == VCN_INDEX_ROOT_PARENT ) - parent_ih = &icx->ir->index; - else - { - ib = ntfs_malloc( icx->block_size ); - if ( !ib ) - return STATUS_ERROR; - - if ( ntfs_ib_read( icx, ntfs_icx_parent_vcn( icx ), ib ) ) - goto out; - - parent_ih = &ib->index; - } - - ie = ntfs_ie_get_by_pos( parent_ih, ntfs_icx_parent_pos( icx ) ); - if ( !ntfs_ie_end( ie ) ) - { - ret = ntfs_ih_takeout( icx, parent_ih, ie, ib ); - goto out; - } - - if ( ntfs_ih_zero_entry( parent_ih ) ) - { - - if ( ntfs_icx_parent_vcn( icx ) == VCN_INDEX_ROOT_PARENT ) - { - ntfs_ir_leafify( icx, parent_ih ); - goto ok; - } - - ret = ntfs_index_rm_leaf( icx ); - goto out; - } - - if ( ntfs_ih_reparent_end( icx, parent_ih, ib ) ) - goto out; -ok: - ret = STATUS_OK; + if (ntfs_ibm_clear(icx, icx->parent_vcn[icx->pindex + 1])) + return STATUS_ERROR; + + if (ntfs_icx_parent_vcn(icx) == VCN_INDEX_ROOT_PARENT) + parent_ih = &icx->ir->index; + else { + ib = ntfs_malloc(icx->block_size); + if (!ib) + return STATUS_ERROR; + + if (ntfs_ib_read(icx, ntfs_icx_parent_vcn(icx), ib)) + goto out; + + parent_ih = &ib->index; + } + + ie = ntfs_ie_get_by_pos(parent_ih, ntfs_icx_parent_pos(icx)); + if (!ntfs_ie_end(ie)) { + ret = ntfs_ih_takeout(icx, parent_ih, ie, ib); + goto out; + } + + if (ntfs_ih_zero_entry(parent_ih)) { + + if (ntfs_icx_parent_vcn(icx) == VCN_INDEX_ROOT_PARENT) { + ntfs_ir_leafify(icx, parent_ih); + goto ok; + } + + ret = ntfs_index_rm_leaf(icx); + goto out; + } + + if (ntfs_ih_reparent_end(icx, parent_ih, ib)) + goto out; +ok: + ret = STATUS_OK; out: - free( ib ); - return ret; + free(ib); + return ret; } -static int ntfs_index_rm_node( ntfs_index_context *icx ) +static int ntfs_index_rm_node(ntfs_index_context *icx) { - int entry_pos, pindex; - VCN vcn; - INDEX_BLOCK *ib = NULL; - INDEX_ENTRY *ie_succ, *ie, *entry = icx->entry; - INDEX_HEADER *ih; - u32 new_size; - int delta, ret = STATUS_ERROR; + int entry_pos, pindex; + VCN vcn; + INDEX_BLOCK *ib = NULL; + INDEX_ENTRY *ie_succ, *ie, *entry = icx->entry; + INDEX_HEADER *ih; + u32 new_size; + int delta, ret = STATUS_ERROR; - ntfs_log_trace( "Entering\n" ); + ntfs_log_trace("Entering\n"); + + if (!icx->ia_na) { + icx->ia_na = ntfs_ia_open(icx, icx->ni); + if (!icx->ia_na) + return STATUS_ERROR; + } - if ( !icx->ia_na ) - { - icx->ia_na = ntfs_ia_open( icx, icx->ni ); - if ( !icx->ia_na ) - return STATUS_ERROR; - } - - ib = ntfs_malloc( icx->block_size ); - if ( !ib ) - return STATUS_ERROR; - - ie_succ = ntfs_ie_get_next( icx->entry ); - entry_pos = icx->parent_pos[icx->pindex]++; - pindex = icx->pindex; + ib = ntfs_malloc(icx->block_size); + if (!ib) + return STATUS_ERROR; + + ie_succ = ntfs_ie_get_next(icx->entry); + entry_pos = icx->parent_pos[icx->pindex]++; + pindex = icx->pindex; descend: - vcn = ntfs_ie_get_vcn( ie_succ ); - if ( ntfs_ib_read( icx, vcn, ib ) ) - goto out; + vcn = ntfs_ie_get_vcn(ie_succ); + if (ntfs_ib_read(icx, vcn, ib)) + goto out; + + ie_succ = ntfs_ie_get_first(&ib->index); - ie_succ = ntfs_ie_get_first( &ib->index ); + if (ntfs_icx_parent_inc(icx)) + goto out; + + icx->parent_vcn[icx->pindex] = vcn; + icx->parent_pos[icx->pindex] = 0; - if ( ntfs_icx_parent_inc( icx ) ) - goto out; + if ((ib->index.ih_flags & NODE_MASK) == INDEX_NODE) + goto descend; - icx->parent_vcn[icx->pindex] = vcn; - icx->parent_pos[icx->pindex] = 0; + if (ntfs_ih_zero_entry(&ib->index)) { + errno = EIO; + ntfs_log_perror("Empty index block"); + goto out; + } - if ( ( ib->index.ih_flags & NODE_MASK ) == INDEX_NODE ) - goto descend; + ie = ntfs_ie_dup(ie_succ); + if (!ie) + goto out; + + if (ntfs_ie_add_vcn(&ie)) + goto out2; - if ( ntfs_ih_zero_entry( &ib->index ) ) - { - errno = EIO; - ntfs_log_perror( "Empty index block" ); - goto out; - } + ntfs_ie_set_vcn(ie, ntfs_ie_get_vcn(icx->entry)); - ie = ntfs_ie_dup( ie_succ ); - if ( !ie ) - goto out; + if (icx->is_in_root) + ih = &icx->ir->index; + else + ih = &icx->ib->index; - if ( ntfs_ie_add_vcn( &ie ) ) - goto out2; + delta = le16_to_cpu(ie->length) - le16_to_cpu(icx->entry->length); + new_size = le32_to_cpu(ih->index_length) + delta; + if (delta > 0) { + if (icx->is_in_root) { + ret = ntfs_ir_make_space(icx, new_size); + if (ret != STATUS_OK) + goto out2; + + ih = &icx->ir->index; + entry = ntfs_ie_get_by_pos(ih, entry_pos); + + } else if (new_size > le32_to_cpu(ih->allocated_size)) { + icx->pindex = pindex; + ret = ntfs_ib_split(icx, icx->ib); + if (ret == STATUS_OK) + ret = STATUS_KEEP_SEARCHING; + goto out2; + } + } - ntfs_ie_set_vcn( ie, ntfs_ie_get_vcn( icx->entry ) ); + ntfs_ie_delete(ih, entry); + ntfs_ie_insert(ih, ie, entry); + + if (icx->is_in_root) { + if (ntfs_ir_truncate(icx, new_size)) + goto out2; + } else + if (ntfs_icx_ib_write(icx)) + goto out2; + + ntfs_ie_delete(&ib->index, ie_succ); + + if (ntfs_ih_zero_entry(&ib->index)) { + if (ntfs_index_rm_leaf(icx)) + goto out2; + } else + if (ntfs_ib_write(icx, ib)) + goto out2; - if ( icx->is_in_root ) - ih = &icx->ir->index; - else - ih = &icx->ib->index; - - delta = le16_to_cpu( ie->length ) - le16_to_cpu( icx->entry->length ); - new_size = le32_to_cpu( ih->index_length ) + delta; - if ( delta > 0 ) - { - if ( icx->is_in_root ) - { - ret = ntfs_ir_make_space( icx, new_size ); - if ( ret != STATUS_OK ) - goto out2; - - ih = &icx->ir->index; - entry = ntfs_ie_get_by_pos( ih, entry_pos ); - - } - else if ( new_size > le32_to_cpu( ih->allocated_size ) ) - { - icx->pindex = pindex; - ret = ntfs_ib_split( icx, icx->ib ); - if ( ret == STATUS_OK ) - ret = STATUS_KEEP_SEARCHING; - goto out2; - } - } - - ntfs_ie_delete( ih, entry ); - ntfs_ie_insert( ih, ie, entry ); - - if ( icx->is_in_root ) - { - if ( ntfs_ir_truncate( icx, new_size ) ) - goto out2; - } - else if ( ntfs_icx_ib_write( icx ) ) - goto out2; - - ntfs_ie_delete( &ib->index, ie_succ ); - - if ( ntfs_ih_zero_entry( &ib->index ) ) - { - if ( ntfs_index_rm_leaf( icx ) ) - goto out2; - } - else if ( ntfs_ib_write( icx, ib ) ) - goto out2; - - ret = STATUS_OK; + ret = STATUS_OK; out2: - free( ie ); + free(ie); out: - free( ib ); - return ret; + free(ib); + return ret; } /** * ntfs_index_rm - remove entry from the index - * @icx: index context describing entry to delete + * @icx: index context describing entry to delete * - * Delete entry described by @icx from the index. Index context is always - * reinitialized after use of this function, so it can be used for index + * Delete entry described by @icx from the index. Index context is always + * reinitialized after use of this function, so it can be used for index * lookup once again. * * Return 0 on success or -1 on error with errno set to the error code. */ /*static JPA*/ -int ntfs_index_rm( ntfs_index_context *icx ) +int ntfs_index_rm(ntfs_index_context *icx) { - INDEX_HEADER *ih; - int err, ret = STATUS_OK; + INDEX_HEADER *ih; + int err, ret = STATUS_OK; - ntfs_log_trace( "Entering\n" ); + ntfs_log_trace("Entering\n"); + + if (!icx || (!icx->ib && !icx->ir) || ntfs_ie_end(icx->entry)) { + ntfs_log_error("Invalid arguments.\n"); + errno = EINVAL; + goto err_out; + } + if (icx->is_in_root) + ih = &icx->ir->index; + else + ih = &icx->ib->index; + + if (icx->entry->ie_flags & INDEX_ENTRY_NODE) { + + ret = ntfs_index_rm_node(icx); - if ( !icx || ( !icx->ib && !icx->ir ) || ntfs_ie_end( icx->entry ) ) - { - ntfs_log_error( "Invalid arguments.\n" ); - errno = EINVAL; - goto err_out; - } - if ( icx->is_in_root ) - ih = &icx->ir->index; - else - ih = &icx->ib->index; - - if ( icx->entry->ie_flags & INDEX_ENTRY_NODE ) - { - - ret = ntfs_index_rm_node( icx ); - - } - else if ( icx->is_in_root || !ntfs_ih_one_entry( ih ) ) - { - - ntfs_ie_delete( ih, icx->entry ); - - if ( icx->is_in_root ) - { - err = ntfs_ir_truncate( icx, le32_to_cpu( ih->index_length ) ); - if ( err != STATUS_OK ) - goto err_out; - } - else if ( ntfs_icx_ib_write( icx ) ) - goto err_out; - } - else - { - if ( ntfs_index_rm_leaf( icx ) ) - goto err_out; - } + } else if (icx->is_in_root || !ntfs_ih_one_entry(ih)) { + + ntfs_ie_delete(ih, icx->entry); + + if (icx->is_in_root) { + err = ntfs_ir_truncate(icx, le32_to_cpu(ih->index_length)); + if (err != STATUS_OK) + goto err_out; + } else + if (ntfs_icx_ib_write(icx)) + goto err_out; + } else { + if (ntfs_index_rm_leaf(icx)) + goto err_out; + } out: - return ret; + return ret; err_out: - ret = STATUS_ERROR; - goto out; + ret = STATUS_ERROR; + goto out; } -int ntfs_index_remove( ntfs_inode *dir_ni, ntfs_inode *ni, - const void *key, const int keylen ) +int ntfs_index_remove(ntfs_inode *dir_ni, ntfs_inode *ni, + const void *key, const int keylen) { - int ret = STATUS_ERROR; - ntfs_index_context *icx; + int ret = STATUS_ERROR; + ntfs_index_context *icx; - icx = ntfs_index_ctx_get( dir_ni, NTFS_INDEX_I30, 4 ); - if ( !icx ) - return -1; + icx = ntfs_index_ctx_get(dir_ni, NTFS_INDEX_I30, 4); + if (!icx) + return -1; - while ( 1 ) - { + while (1) { + + if (ntfs_index_lookup(key, keylen, icx)) + goto err_out; - if ( ntfs_index_lookup( key, keylen, icx ) ) - goto err_out; + if ((((FILE_NAME_ATTR *)icx->data)->file_attributes & + FILE_ATTR_REPARSE_POINT) + && !ntfs_possible_symlink(ni)) { + errno = EOPNOTSUPP; + goto err_out; + } - if ( ( ( ( FILE_NAME_ATTR * )icx->data )->file_attributes & - FILE_ATTR_REPARSE_POINT ) - && !ntfs_possible_symlink( ni ) ) - { - errno = EOPNOTSUPP; - goto err_out; - } + ret = ntfs_index_rm(icx); + if (ret == STATUS_ERROR) + goto err_out; + else if (ret == STATUS_OK) + break; + + ntfs_inode_mark_dirty(icx->actx->ntfs_ino); + ntfs_index_ctx_reinit(icx); + } - ret = ntfs_index_rm( icx ); - if ( ret == STATUS_ERROR ) - goto err_out; - else if ( ret == STATUS_OK ) - break; - - ntfs_inode_mark_dirty( icx->actx->ntfs_ino ); - ntfs_index_ctx_reinit( icx ); - } - - ntfs_inode_mark_dirty( icx->actx->ntfs_ino ); -out: - ntfs_index_ctx_put( icx ); - return ret; + ntfs_inode_mark_dirty(icx->actx->ntfs_ino); +out: + ntfs_index_ctx_put(icx); + return ret; err_out: - ret = STATUS_ERROR; - ntfs_log_perror( "Delete failed" ); - goto out; + ret = STATUS_ERROR; + ntfs_log_perror("Delete failed"); + goto out; } /** * ntfs_index_root_get - read the index root of an attribute - * @ni: open ntfs inode in which the ntfs attribute resides - * @attr: attribute for which we want its index root + * @ni: open ntfs inode in which the ntfs attribute resides + * @attr: attribute for which we want its index root * * This function will read the related index root an ntfs attribute. * @@ -1960,145 +1874,129 @@ err_out: * * On error NULL is returned with errno set to the error code. */ -INDEX_ROOT *ntfs_index_root_get( ntfs_inode *ni, ATTR_RECORD *attr ) +INDEX_ROOT *ntfs_index_root_get(ntfs_inode *ni, ATTR_RECORD *attr) { - ntfs_attr_search_ctx *ctx; - ntfschar *name; - INDEX_ROOT *root = NULL; + ntfs_attr_search_ctx *ctx; + ntfschar *name; + INDEX_ROOT *root = NULL; - name = ( ntfschar * )( ( u8 * )attr + le16_to_cpu( attr->name_offset ) ); + name = (ntfschar *)((u8 *)attr + le16_to_cpu(attr->name_offset)); - if ( !ntfs_ir_lookup( ni, name, attr->name_length, &ctx ) ) - return NULL; - - root = ntfs_malloc( sizeof( INDEX_ROOT ) ); - if ( !root ) - goto out; - - *root = *( ( INDEX_ROOT * )( ( u8 * )ctx->attr + - le16_to_cpu( ctx->attr->value_offset ) ) ); -out: - ntfs_attr_put_search_ctx( ctx ); - return root; + if (!ntfs_ir_lookup(ni, name, attr->name_length, &ctx)) + return NULL; + + root = ntfs_malloc(sizeof(INDEX_ROOT)); + if (!root) + goto out; + + *root = *((INDEX_ROOT *)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->value_offset))); +out: + ntfs_attr_put_search_ctx(ctx); + return root; } /* - * Walk down the index tree (leaf bound) - * until there are no subnode in the first index entry - * returns the entry at the bottom left in subnode + * Walk down the index tree (leaf bound) + * until there are no subnode in the first index entry + * returns the entry at the bottom left in subnode */ -static INDEX_ENTRY *ntfs_index_walk_down( INDEX_ENTRY *ie, - ntfs_index_context *ictx ) +static INDEX_ENTRY *ntfs_index_walk_down(INDEX_ENTRY *ie, + ntfs_index_context *ictx) { - INDEX_ENTRY *entry; - s64 vcn; + INDEX_ENTRY *entry; + s64 vcn; - entry = ie; - do - { - vcn = ntfs_ie_get_vcn( entry ); - if ( ictx->is_in_root ) - { + entry = ie; + do { + vcn = ntfs_ie_get_vcn(entry); + if (ictx->is_in_root) { - /* down from level zero */ + /* down from level zero */ - ictx->ir = ( INDEX_ROOT* )NULL; - ictx->ib = ( INDEX_BLOCK* )ntfs_malloc( ictx->block_size ); - ictx->pindex = 1; - ictx->is_in_root = FALSE; - } - else - { + ictx->ir = (INDEX_ROOT*)NULL; + ictx->ib = (INDEX_BLOCK*)ntfs_malloc(ictx->block_size); + ictx->pindex = 1; + ictx->is_in_root = FALSE; + } else { - /* down from non-zero level */ - - ictx->pindex++; - } - ictx->parent_pos[ictx->pindex] = 0; - ictx->parent_vcn[ictx->pindex] = vcn; - if ( !ntfs_ib_read( ictx, vcn, ictx->ib ) ) - { - ictx->entry = ntfs_ie_get_first( &ictx->ib->index ); - entry = ictx->entry; - } - else - entry = ( INDEX_ENTRY* )NULL; - } - while ( entry && ( entry->ie_flags & INDEX_ENTRY_NODE ) ); - return ( entry ); + /* down from non-zero level */ + + ictx->pindex++; + } + ictx->parent_pos[ictx->pindex] = 0; + ictx->parent_vcn[ictx->pindex] = vcn; + if (!ntfs_ib_read(ictx,vcn,ictx->ib)) { + ictx->entry = ntfs_ie_get_first(&ictx->ib->index); + entry = ictx->entry; + } else + entry = (INDEX_ENTRY*)NULL; + } while (entry && (entry->ie_flags & INDEX_ENTRY_NODE)); + return (entry); } /* - * Walk up the index tree (root bound) - * until there is a valid data entry in parent - * returns the parent entry or NULL if no more parent + * Walk up the index tree (root bound) + * until there is a valid data entry in parent + * returns the parent entry or NULL if no more parent */ -static INDEX_ENTRY *ntfs_index_walk_up( INDEX_ENTRY *ie, - ntfs_index_context *ictx ) +static INDEX_ENTRY *ntfs_index_walk_up(INDEX_ENTRY *ie, + ntfs_index_context *ictx) { - INDEX_ENTRY *entry; - s64 vcn; + INDEX_ENTRY *entry; + s64 vcn; - entry = ie; - if ( ictx->pindex > 0 ) - { - do - { - ictx->pindex--; - if ( !ictx->pindex ) - { + entry = ie; + if (ictx->pindex > 0) { + do { + ictx->pindex--; + if (!ictx->pindex) { - /* we have reached the root */ + /* we have reached the root */ - free( ictx->ib ); - ictx->ib = ( INDEX_BLOCK* )NULL; - ictx->is_in_root = TRUE; - /* a new search context is to be allocated */ - if ( ictx->actx ) - free( ictx->actx ); - ictx->ir = ntfs_ir_lookup( ictx->ni, - ictx->name, ictx->name_len, - &ictx->actx ); - if ( ictx->ir ) - entry = ntfs_ie_get_by_pos( - &ictx->ir->index, - ictx->parent_pos[ictx->pindex] ); - else - entry = ( INDEX_ENTRY* )NULL; - } - else - { - /* up into non-root node */ - vcn = ictx->parent_vcn[ictx->pindex]; - if ( !ntfs_ib_read( ictx, vcn, ictx->ib ) ) - { - entry = ntfs_ie_get_by_pos( - &ictx->ib->index, - ictx->parent_pos[ictx->pindex] ); - } - else - entry = ( INDEX_ENTRY* )NULL; - } - ictx->entry = entry; - } - while ( entry && ( ictx->pindex > 0 ) - && ( entry->ie_flags & INDEX_ENTRY_END ) ); - } - else - entry = ( INDEX_ENTRY* )NULL; - return ( entry ); + free(ictx->ib); + ictx->ib = (INDEX_BLOCK*)NULL; + ictx->is_in_root = TRUE; + /* a new search context is to be allocated */ + if (ictx->actx) + free(ictx->actx); + ictx->ir = ntfs_ir_lookup(ictx->ni, + ictx->name, ictx->name_len, + &ictx->actx); + if (ictx->ir) + entry = ntfs_ie_get_by_pos( + &ictx->ir->index, + ictx->parent_pos[ictx->pindex]); + else + entry = (INDEX_ENTRY*)NULL; + } else { + /* up into non-root node */ + vcn = ictx->parent_vcn[ictx->pindex]; + if (!ntfs_ib_read(ictx,vcn,ictx->ib)) { + entry = ntfs_ie_get_by_pos( + &ictx->ib->index, + ictx->parent_pos[ictx->pindex]); + } else + entry = (INDEX_ENTRY*)NULL; + } + ictx->entry = entry; + } while (entry && (ictx->pindex > 0) + && (entry->ie_flags & INDEX_ENTRY_END)); + } else + entry = (INDEX_ENTRY*)NULL; + return (entry); } /* - * Get next entry in an index according to collating sequence. - * Must be initialized through a ntfs_index_lookup() + * Get next entry in an index according to collating sequence. + * Must be initialized through a ntfs_index_lookup() * - * Returns next entry or NULL if none + * Returns next entry or NULL if none * - * Sample layout : + * Sample layout : * * +---+---+---+---+---+---+---+---+ n ptrs to subnodes * | | | 10| 25| 33| | | | n-1 keys in between @@ -2119,52 +2017,47 @@ static INDEX_ENTRY *ntfs_index_walk_up( INDEX_ENTRY *ie, * +---+---+---+---+---+---+---+---+ */ -INDEX_ENTRY *ntfs_index_next( INDEX_ENTRY *ie, ntfs_index_context *ictx ) +INDEX_ENTRY *ntfs_index_next(INDEX_ENTRY *ie, ntfs_index_context *ictx) { - INDEX_ENTRY *next; - int flags; + INDEX_ENTRY *next; + int flags; - /* - * lookup() may have returned an invalid node - * when searching for a partial key - * if this happens, walk up - */ + /* + * lookup() may have returned an invalid node + * when searching for a partial key + * if this happens, walk up + */ - if ( ie->ie_flags & INDEX_ENTRY_END ) - next = ntfs_index_walk_up( ie, ictx ); - else - { - /* - * get next entry in same node - * there is always one after any entry with data - */ + if (ie->ie_flags & INDEX_ENTRY_END) + next = ntfs_index_walk_up(ie, ictx); + else { + /* + * get next entry in same node + * there is always one after any entry with data + */ - next = ( INDEX_ENTRY* )( ( char* )ie + le16_to_cpu( ie->length ) ); - ++ictx->parent_pos[ictx->pindex]; - flags = next->ie_flags; + next = (INDEX_ENTRY*)((char*)ie + le16_to_cpu(ie->length)); + ++ictx->parent_pos[ictx->pindex]; + flags = next->ie_flags; - /* walk down if it has a subnode */ + /* walk down if it has a subnode */ - if ( flags & INDEX_ENTRY_NODE ) - { - next = ntfs_index_walk_down( next, ictx ); - } - else - { + if (flags & INDEX_ENTRY_NODE) { + next = ntfs_index_walk_down(next,ictx); + } else { - /* walk up it has no subnode, nor data */ + /* walk up it has no subnode, nor data */ - if ( flags & INDEX_ENTRY_END ) - { - next = ntfs_index_walk_up( next, ictx ); - } - } - } - /* return NULL if stuck at end of a block */ + if (flags & INDEX_ENTRY_END) { + next = ntfs_index_walk_up(next, ictx); + } + } + } + /* return NULL if stuck at end of a block */ - if ( next && ( next->ie_flags & INDEX_ENTRY_END ) ) - next = ( INDEX_ENTRY* )NULL; - return ( next ); + if (next && (next->ie_flags & INDEX_ENTRY_END)) + next = (INDEX_ENTRY*)NULL; + return (next); } diff --git a/source/libntfs/index.h b/source/libntfs/index.h index 3a8725df..c0e76180 100644 --- a/source/libntfs/index.h +++ b/source/libntfs/index.h @@ -31,7 +31,7 @@ * ... code requiring gcc 2.8 or later ... * #endif * Note - they won't work for gcc1 or glibc1, since the _MINOR macros - * were not defined then. + * were not defined then. */ #ifndef __GNUC_PREREQ @@ -61,31 +61,31 @@ #define VCN_INDEX_ROOT_PARENT ((VCN)-2) -#define MAX_PARENT_VCN 32 +#define MAX_PARENT_VCN 32 -typedef int ( *COLLATE )( ntfs_volume *vol, const void *data1, int len1, - const void *data2, int len2 ); +typedef int (*COLLATE)(ntfs_volume *vol, const void *data1, int len1, + const void *data2, int len2); /** * struct ntfs_index_context - - * @ni: inode containing the @entry described by this context - * @name: name of the index described by this context - * @name_len: length of the index name - * @entry: index entry (points into @ir or @ia) - * @data: index entry data (points into @entry) - * @data_len: length in bytes of @data - * @is_in_root: TRUE if @entry is in @ir or FALSE if it is in @ia - * @ir: index root if @is_in_root or NULL otherwise - * @actx: attribute search context if in root or NULL otherwise - * @ia: index block if @is_in_root is FALSE or NULL otherwise - * @ia_na: opened INDEX_ALLOCATION attribute - * @parent_pos: parent entries' positions in the index block - * @parent_vcn: entry's parent node or VCN_INDEX_ROOT_PARENT + * @ni: inode containing the @entry described by this context + * @name: name of the index described by this context + * @name_len: length of the index name + * @entry: index entry (points into @ir or @ia) + * @data: index entry data (points into @entry) + * @data_len: length in bytes of @data + * @is_in_root: TRUE if @entry is in @ir or FALSE if it is in @ia + * @ir: index root if @is_in_root or NULL otherwise + * @actx: attribute search context if in root or NULL otherwise + * @ia: index block if @is_in_root is FALSE or NULL otherwise + * @ia_na: opened INDEX_ALLOCATION attribute + * @parent_pos: parent entries' positions in the index block + * @parent_vcn: entry's parent node or VCN_INDEX_ROOT_PARENT * @new_vcn: new VCN if we need to create a new index block - * @median: move to the parent if splitting index blocks - * @ib_dirty: TRUE if index block was changed - * @block_size: index block size - * @vcn_size_bits: VCN size bits for this index block + * @median: move to the parent if splitting index blocks + * @ib_dirty: TRUE if index block was changed + * @block_size: index block size + * @vcn_size_bits: VCN size bits for this index block * * @ni is the inode this context belongs to. * @@ -112,57 +112,56 @@ typedef int ( *COLLATE )( ntfs_volume *vol, const void *data1, int len1, * the call to ntfs_index_ctx_put() to ensure that the changes are written * to disk. */ -typedef struct -{ - ntfs_inode *ni; - ntfschar *name; - u32 name_len; - INDEX_ENTRY *entry; - void *data; - u16 data_len; - COLLATE collate; - BOOL is_in_root; - INDEX_ROOT *ir; - ntfs_attr_search_ctx *actx; - INDEX_BLOCK *ib; - ntfs_attr *ia_na; - int parent_pos[MAX_PARENT_VCN]; /* parent entries' positions */ - VCN parent_vcn[MAX_PARENT_VCN]; /* entry's parent nodes */ - int pindex; /* maximum it's the number of the parent nodes */ - BOOL ib_dirty; - u32 block_size; - u8 vcn_size_bits; +typedef struct { + ntfs_inode *ni; + ntfschar *name; + u32 name_len; + INDEX_ENTRY *entry; + void *data; + u16 data_len; + COLLATE collate; + BOOL is_in_root; + INDEX_ROOT *ir; + ntfs_attr_search_ctx *actx; + INDEX_BLOCK *ib; + ntfs_attr *ia_na; + int parent_pos[MAX_PARENT_VCN]; /* parent entries' positions */ + VCN parent_vcn[MAX_PARENT_VCN]; /* entry's parent nodes */ + int pindex; /* maximum it's the number of the parent nodes */ + BOOL ib_dirty; + u32 block_size; + u8 vcn_size_bits; } ntfs_index_context; -extern ntfs_index_context *ntfs_index_ctx_get( ntfs_inode *ni, - ntfschar *name, u32 name_len ); -extern void ntfs_index_ctx_put( ntfs_index_context *ictx ); -extern void ntfs_index_ctx_reinit( ntfs_index_context *ictx ); +extern ntfs_index_context *ntfs_index_ctx_get(ntfs_inode *ni, + ntfschar *name, u32 name_len); +extern void ntfs_index_ctx_put(ntfs_index_context *ictx); +extern void ntfs_index_ctx_reinit(ntfs_index_context *ictx); -extern int ntfs_index_lookup( const void *key, const int key_len, - ntfs_index_context *ictx ) __attribute_warn_unused_result__; +extern int ntfs_index_lookup(const void *key, const int key_len, + ntfs_index_context *ictx) __attribute_warn_unused_result__; -extern INDEX_ENTRY *ntfs_index_next( INDEX_ENTRY *ie, - ntfs_index_context *ictx ); +extern INDEX_ENTRY *ntfs_index_next(INDEX_ENTRY *ie, + ntfs_index_context *ictx); -extern int ntfs_index_add_filename( ntfs_inode *ni, FILE_NAME_ATTR *fn, - MFT_REF mref ); -extern int ntfs_index_remove( ntfs_inode *dir_ni, ntfs_inode *ni, - const void *key, const int keylen ); +extern int ntfs_index_add_filename(ntfs_inode *ni, FILE_NAME_ATTR *fn, + MFT_REF mref); +extern int ntfs_index_remove(ntfs_inode *dir_ni, ntfs_inode *ni, + const void *key, const int keylen); -extern INDEX_ROOT *ntfs_index_root_get( ntfs_inode *ni, ATTR_RECORD *attr ); +extern INDEX_ROOT *ntfs_index_root_get(ntfs_inode *ni, ATTR_RECORD *attr); -extern VCN ntfs_ie_get_vcn( INDEX_ENTRY *ie ); +extern VCN ntfs_ie_get_vcn(INDEX_ENTRY *ie); -extern void ntfs_index_entry_mark_dirty( ntfs_index_context *ictx ); +extern void ntfs_index_entry_mark_dirty(ntfs_index_context *ictx); -extern char *ntfs_ie_filename_get( INDEX_ENTRY *ie ); -extern void ntfs_ie_filename_dump( INDEX_ENTRY *ie ); -extern void ntfs_ih_filename_dump( INDEX_HEADER *ih ); +extern char *ntfs_ie_filename_get(INDEX_ENTRY *ie); +extern void ntfs_ie_filename_dump(INDEX_ENTRY *ie); +extern void ntfs_ih_filename_dump(INDEX_HEADER *ih); /* the following was added by JPA for use in security.c */ -extern int ntfs_ie_add( ntfs_index_context *icx, INDEX_ENTRY *ie ); -extern int ntfs_index_rm( ntfs_index_context *icx ); +extern int ntfs_ie_add(ntfs_index_context *icx, INDEX_ENTRY *ie); +extern int ntfs_index_rm(ntfs_index_context *icx); #endif /* _NTFS_INDEX_H */ diff --git a/source/libntfs/inode.c b/source/libntfs/inode.c index 92a24524..6f3fa060 100644 --- a/source/libntfs/inode.c +++ b/source/libntfs/inode.c @@ -58,16 +58,16 @@ #include "logging.h" #include "misc.h" -ntfs_inode *ntfs_inode_base( ntfs_inode *ni ) +ntfs_inode *ntfs_inode_base(ntfs_inode *ni) { - if ( ni->nr_extents == -1 ) - return ni->base_ni; - return ni; + if (ni->nr_extents == -1) + return ni->base_ni; + return ni; } /** * ntfs_inode_mark_dirty - set the inode (and its base inode if it exists) dirty - * @ni: ntfs inode to set dirty + * @ni: ntfs inode to set dirty * * Set the inode @ni dirty so it is written out later (at the latest at * ntfs_inode_close() time). If @ni is an extent inode, set the base inode @@ -75,11 +75,11 @@ ntfs_inode *ntfs_inode_base( ntfs_inode *ni ) * * This function cannot fail. */ -void ntfs_inode_mark_dirty( ntfs_inode *ni ) +void ntfs_inode_mark_dirty(ntfs_inode *ni) { - NInoSetDirty( ni ); - if ( ni->nr_extents == -1 ) - NInoSetDirty( ni->base_ni ); + NInoSetDirty(ni); + if (ni->nr_extents == -1) + NInoSetDirty(ni->base_ni); } /** @@ -90,14 +90,14 @@ void ntfs_inode_mark_dirty( ntfs_inode *ni ) * * Returns: */ -static ntfs_inode *__ntfs_inode_allocate( ntfs_volume *vol ) +static ntfs_inode *__ntfs_inode_allocate(ntfs_volume *vol) { - ntfs_inode *ni; + ntfs_inode *ni; - ni = ( ntfs_inode* )ntfs_calloc( sizeof( ntfs_inode ) ); - if ( ni ) - ni->vol = vol; - return ni; + ni = (ntfs_inode*)ntfs_calloc(sizeof(ntfs_inode)); + if (ni) + ni->vol = vol; + return ni; } /** @@ -108,9 +108,9 @@ static ntfs_inode *__ntfs_inode_allocate( ntfs_volume *vol ) * * Returns: */ -ntfs_inode *ntfs_inode_allocate( ntfs_volume *vol ) +ntfs_inode *ntfs_inode_allocate(ntfs_volume *vol) { - return __ntfs_inode_allocate( vol ); + return __ntfs_inode_allocate(vol); } /** @@ -121,22 +121,22 @@ ntfs_inode *ntfs_inode_allocate( ntfs_volume *vol ) * * Returns: */ -static void __ntfs_inode_release( ntfs_inode *ni ) +static void __ntfs_inode_release(ntfs_inode *ni) { - if ( NInoDirty( ni ) ) - ntfs_log_error( "Releasing dirty inode %lld!\n", - ( long long )ni->mft_no ); - if ( NInoAttrList( ni ) && ni->attr_list ) - free( ni->attr_list ); - free( ni->mrec ); - free( ni ); - return; + if (NInoDirty(ni)) + ntfs_log_error("Releasing dirty inode %lld!\n", + (long long)ni->mft_no); + if (NInoAttrList(ni) && ni->attr_list) + free(ni->attr_list); + free(ni->mrec); + free(ni); + return; } /** * ntfs_inode_open - open an inode ready for access - * @vol: volume to get the inode from - * @mref: inode number / mft record number to open + * @vol: volume to get the inode from + * @mref: inode number / mft record number to open * * Allocate an ntfs_inode structure and initialize it for the given inode * specified by @mref. @mref specifies the inode number / mft record to read, @@ -156,153 +156,138 @@ static void __ntfs_inode_release( ntfs_inode *ni ) * Return a pointer to the ntfs_inode structure on success or NULL on error, * with errno set to the error code. */ -static ntfs_inode *ntfs_inode_real_open( ntfs_volume *vol, const MFT_REF mref ) +static ntfs_inode *ntfs_inode_real_open(ntfs_volume *vol, const MFT_REF mref) { - s64 l; - ntfs_inode *ni = NULL; - ntfs_attr_search_ctx *ctx; - STANDARD_INFORMATION *std_info; - le32 lthle; - int olderrno; + s64 l; + ntfs_inode *ni = NULL; + ntfs_attr_search_ctx *ctx; + STANDARD_INFORMATION *std_info; + le32 lthle; + int olderrno; - ntfs_log_enter( "Entering for inode %lld\n", ( long long )MREF( mref ) ); - if ( !vol ) - { - errno = EINVAL; - goto out; - } - ni = __ntfs_inode_allocate( vol ); - if ( !ni ) - goto out; - if ( ntfs_file_record_read( vol, mref, &ni->mrec, NULL ) ) - goto err_out; - if ( !( ni->mrec->flags & MFT_RECORD_IN_USE ) ) - { - errno = ENOENT; - goto err_out; - } - ni->mft_no = MREF( mref ); - ctx = ntfs_attr_get_search_ctx( ni, NULL ); - if ( !ctx ) - goto err_out; - /* Receive some basic information about inode. */ - if ( ntfs_attr_lookup( AT_STANDARD_INFORMATION, AT_UNNAMED, - 0, CASE_SENSITIVE, 0, NULL, 0, ctx ) ) - { - if ( !ni->mrec->base_mft_record ) - ntfs_log_perror( "No STANDARD_INFORMATION in base record" - " %lld", ( long long )MREF( mref ) ); - goto put_err_out; - } - std_info = ( STANDARD_INFORMATION * )( ( u8 * )ctx->attr + - le16_to_cpu( ctx->attr->value_offset ) ); - ni->flags = std_info->file_attributes; - ni->creation_time = std_info->creation_time; - ni->last_data_change_time = std_info->last_data_change_time; - ni->last_mft_change_time = std_info->last_mft_change_time; - ni->last_access_time = std_info->last_access_time; - /* JPA insert v3 extensions if present */ - /* length may be seen as 72 (v1.x) or 96 (v3.x) */ - lthle = ctx->attr->length; - if ( le32_to_cpu( lthle ) > sizeof( STANDARD_INFORMATION ) ) - { - set_nino_flag( ni, v3_Extensions ); - ni->owner_id = std_info->owner_id; - ni->security_id = std_info->security_id; - ni->quota_charged = std_info->quota_charged; - ni->usn = std_info->usn; - } - else - { - clear_nino_flag( ni, v3_Extensions ); - ni->owner_id = const_cpu_to_le32( 0 ); - ni->security_id = const_cpu_to_le32( 0 ); - } - /* Set attribute list information. */ - olderrno = errno; - if ( ntfs_attr_lookup( AT_ATTRIBUTE_LIST, AT_UNNAMED, 0, - CASE_SENSITIVE, 0, NULL, 0, ctx ) ) - { - if ( errno != ENOENT ) - goto put_err_out; - /* Attribute list attribute does not present. */ - /* restore previous errno to avoid misinterpretation */ - errno = olderrno; - goto get_size; - } - NInoSetAttrList( ni ); - l = ntfs_get_attribute_value_length( ctx->attr ); - if ( !l ) - goto put_err_out; - if ( l > 0x40000 ) - { - errno = EIO; - ntfs_log_perror( "Too large attrlist attribute (%lld), inode " - "%lld", ( long long )l, ( long long )MREF( mref ) ); - goto put_err_out; - } - ni->attr_list_size = l; - ni->attr_list = ntfs_malloc( ni->attr_list_size ); - if ( !ni->attr_list ) - goto put_err_out; - l = ntfs_get_attribute_value( vol, ctx->attr, ni->attr_list ); - if ( !l ) - goto put_err_out; - if ( l != ni->attr_list_size ) - { - errno = EIO; - ntfs_log_perror( "Unexpected attrlist size (%lld <> %u), inode " - "%lld", ( long long )l, ni->attr_list_size, - ( long long )MREF( mref ) ); - goto put_err_out; - } + ntfs_log_enter("Entering for inode %lld\n", (long long)MREF(mref)); + if (!vol) { + errno = EINVAL; + goto out; + } + ni = __ntfs_inode_allocate(vol); + if (!ni) + goto out; + if (ntfs_file_record_read(vol, mref, &ni->mrec, NULL)) + goto err_out; + if (!(ni->mrec->flags & MFT_RECORD_IN_USE)) { + errno = ENOENT; + goto err_out; + } + ni->mft_no = MREF(mref); + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + goto err_out; + /* Receive some basic information about inode. */ + if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED, + 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { + if (!ni->mrec->base_mft_record) + ntfs_log_perror("No STANDARD_INFORMATION in base record" + " %lld", (long long)MREF(mref)); + goto put_err_out; + } + std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + ni->flags = std_info->file_attributes; + ni->creation_time = std_info->creation_time; + ni->last_data_change_time = std_info->last_data_change_time; + ni->last_mft_change_time = std_info->last_mft_change_time; + ni->last_access_time = std_info->last_access_time; + /* JPA insert v3 extensions if present */ + /* length may be seen as 72 (v1.x) or 96 (v3.x) */ + lthle = ctx->attr->length; + if (le32_to_cpu(lthle) > sizeof(STANDARD_INFORMATION)) { + set_nino_flag(ni, v3_Extensions); + ni->owner_id = std_info->owner_id; + ni->security_id = std_info->security_id; + ni->quota_charged = std_info->quota_charged; + ni->usn = std_info->usn; + } else { + clear_nino_flag(ni, v3_Extensions); + ni->owner_id = const_cpu_to_le32(0); + ni->security_id = const_cpu_to_le32(0); + } + /* Set attribute list information. */ + olderrno = errno; + if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, AT_UNNAMED, 0, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + if (errno != ENOENT) + goto put_err_out; + /* Attribute list attribute does not present. */ + /* restore previous errno to avoid misinterpretation */ + errno = olderrno; + goto get_size; + } + NInoSetAttrList(ni); + l = ntfs_get_attribute_value_length(ctx->attr); + if (!l) + goto put_err_out; + if (l > 0x40000) { + errno = EIO; + ntfs_log_perror("Too large attrlist attribute (%lld), inode " + "%lld", (long long)l, (long long)MREF(mref)); + goto put_err_out; + } + ni->attr_list_size = l; + ni->attr_list = ntfs_malloc(ni->attr_list_size); + if (!ni->attr_list) + goto put_err_out; + l = ntfs_get_attribute_value(vol, ctx->attr, ni->attr_list); + if (!l) + goto put_err_out; + if (l != ni->attr_list_size) { + errno = EIO; + ntfs_log_perror("Unexpected attrlist size (%lld <> %u), inode " + "%lld", (long long)l, ni->attr_list_size, + (long long)MREF(mref)); + goto put_err_out; + } get_size: - olderrno = errno; - if ( ntfs_attr_lookup( AT_DATA, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx ) ) - { - if ( errno != ENOENT ) - goto put_err_out; - /* Directory or special file. */ - /* restore previous errno to avoid misinterpretation */ - errno = olderrno; - ni->data_size = ni->allocated_size = 0; - } - else - { - if ( ctx->attr->non_resident ) - { - ni->data_size = sle64_to_cpu( ctx->attr->data_size ); - if ( ctx->attr->flags & - ( ATTR_IS_COMPRESSED | ATTR_IS_SPARSE ) ) - ni->allocated_size = sle64_to_cpu( - ctx->attr->compressed_size ); - else - ni->allocated_size = sle64_to_cpu( - ctx->attr->allocated_size ); - } - else - { - ni->data_size = le32_to_cpu( ctx->attr->value_length ); - ni->allocated_size = ( ni->data_size + 7 ) & ~7; - } - set_nino_flag( ni, KnownSize ); - } - ntfs_attr_put_search_ctx( ctx ); -out: - ntfs_log_leave( "\n" ); - return ni; + olderrno = errno; + if (ntfs_attr_lookup(AT_DATA, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { + if (errno != ENOENT) + goto put_err_out; + /* Directory or special file. */ + /* restore previous errno to avoid misinterpretation */ + errno = olderrno; + ni->data_size = ni->allocated_size = 0; + } else { + if (ctx->attr->non_resident) { + ni->data_size = sle64_to_cpu(ctx->attr->data_size); + if (ctx->attr->flags & + (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE)) + ni->allocated_size = sle64_to_cpu( + ctx->attr->compressed_size); + else + ni->allocated_size = sle64_to_cpu( + ctx->attr->allocated_size); + } else { + ni->data_size = le32_to_cpu(ctx->attr->value_length); + ni->allocated_size = (ni->data_size + 7) & ~7; + } + set_nino_flag(ni,KnownSize); + } + ntfs_attr_put_search_ctx(ctx); +out: + ntfs_log_leave("\n"); + return ni; put_err_out: - ntfs_attr_put_search_ctx( ctx ); + ntfs_attr_put_search_ctx(ctx); err_out: - __ntfs_inode_release( ni ); - ni = NULL; - goto out; + __ntfs_inode_release(ni); + ni = NULL; + goto out; } /** * ntfs_inode_close - close an ntfs inode and free all associated memory - * @ni: ntfs inode to close + * @ni: ntfs inode to close * * Make sure the ntfs inode @ni is clean. * @@ -319,272 +304,249 @@ err_out: * error, @ni has not been freed. The user should attempt to handle the error * and call ntfs_inode_close() again. The following error codes are defined: * - * EBUSY @ni and/or its attribute list runlist is/are dirty and the - * attempt to write it/them to disk failed. - * EINVAL @ni is invalid (probably it is an extent inode). - * EIO I/O error while trying to write inode to disk. + * EBUSY @ni and/or its attribute list runlist is/are dirty and the + * attempt to write it/them to disk failed. + * EINVAL @ni is invalid (probably it is an extent inode). + * EIO I/O error while trying to write inode to disk. */ -int ntfs_inode_real_close( ntfs_inode *ni ) +int ntfs_inode_real_close(ntfs_inode *ni) { - int ret = -1; + int ret = -1; + + if (!ni) + return 0; - if ( !ni ) - return 0; + ntfs_log_enter("Entering for inode %lld\n", (long long)ni->mft_no); - ntfs_log_enter( "Entering for inode %lld\n", ( long long )ni->mft_no ); + /* If we have dirty metadata, write it out. */ + if (NInoDirty(ni) || NInoAttrListDirty(ni)) { + if (ntfs_inode_sync(ni)) { + if (errno != EIO) + errno = EBUSY; + goto err; + } + } + /* Is this a base inode with mapped extent inodes? */ + if (ni->nr_extents > 0) { + while (ni->nr_extents > 0) { + if (ntfs_inode_real_close(ni->extent_nis[0])) { + if (errno != EIO) + errno = EBUSY; + goto err; + } + } + } else if (ni->nr_extents == -1) { + ntfs_inode **tmp_nis; + ntfs_inode *base_ni; + s32 i; - /* If we have dirty metadata, write it out. */ - if ( NInoDirty( ni ) || NInoAttrListDirty( ni ) ) - { - if ( ntfs_inode_sync( ni ) ) - { - if ( errno != EIO ) - errno = EBUSY; - goto err; - } - } - /* Is this a base inode with mapped extent inodes? */ - if ( ni->nr_extents > 0 ) - { - while ( ni->nr_extents > 0 ) - { - if ( ntfs_inode_real_close( ni->extent_nis[0] ) ) - { - if ( errno != EIO ) - errno = EBUSY; - goto err; - } - } - } - else if ( ni->nr_extents == -1 ) - { - ntfs_inode **tmp_nis; - ntfs_inode *base_ni; - s32 i; - - /* - * If the inode is an extent inode, disconnect it from the - * base inode before destroying it. - */ - base_ni = ni->base_ni; - for ( i = 0; i < base_ni->nr_extents; ++i ) - { - tmp_nis = base_ni->extent_nis; - if ( tmp_nis[i] != ni ) - continue; - /* Found it. Disconnect. */ - memmove( tmp_nis + i, tmp_nis + i + 1, - ( base_ni->nr_extents - i - 1 ) * - sizeof( ntfs_inode * ) ); - /* Buffer should be for multiple of four extents. */ - if ( ( --base_ni->nr_extents ) & 3 ) - { - i = -1; - break; - } - /* - * ElectricFence is unhappy with realloc(x,0) as free(x) - * thus we explicitly separate these two cases. - */ - if ( base_ni->nr_extents ) - { - /* Resize the memory buffer. */ - tmp_nis = realloc( tmp_nis, base_ni->nr_extents * - sizeof( ntfs_inode * ) ); - /* Ignore errors, they don't really matter. */ - if ( tmp_nis ) - base_ni->extent_nis = tmp_nis; - } - else if ( tmp_nis ) - { - free( tmp_nis ); - base_ni->extent_nis = ( ntfs_inode** )NULL; - } - /* Allow for error checking. */ - i = -1; - break; - } - - /* - * We could successfully sync, so only log this error - * and try to sync other inode extents too. - */ - if ( i != -1 ) - ntfs_log_error( "Extent inode %lld was not found\n", - ( long long )ni->mft_no ); - } - - __ntfs_inode_release( ni ); - ret = 0; + /* + * If the inode is an extent inode, disconnect it from the + * base inode before destroying it. + */ + base_ni = ni->base_ni; + for (i = 0; i < base_ni->nr_extents; ++i) { + tmp_nis = base_ni->extent_nis; + if (tmp_nis[i] != ni) + continue; + /* Found it. Disconnect. */ + memmove(tmp_nis + i, tmp_nis + i + 1, + (base_ni->nr_extents - i - 1) * + sizeof(ntfs_inode *)); + /* Buffer should be for multiple of four extents. */ + if ((--base_ni->nr_extents) & 3) { + i = -1; + break; + } + /* + * ElectricFence is unhappy with realloc(x,0) as free(x) + * thus we explicitly separate these two cases. + */ + if (base_ni->nr_extents) { + /* Resize the memory buffer. */ + tmp_nis = realloc(tmp_nis, base_ni->nr_extents * + sizeof(ntfs_inode *)); + /* Ignore errors, they don't really matter. */ + if (tmp_nis) + base_ni->extent_nis = tmp_nis; + } else if (tmp_nis) { + free(tmp_nis); + base_ni->extent_nis = (ntfs_inode**)NULL; + } + /* Allow for error checking. */ + i = -1; + break; + } + + /* + * We could successfully sync, so only log this error + * and try to sync other inode extents too. + */ + if (i != -1) + ntfs_log_error("Extent inode %lld was not found\n", + (long long)ni->mft_no); + } + + __ntfs_inode_release(ni); + ret = 0; err: - ntfs_log_leave( "\n" ); - return ret; + ntfs_log_leave("\n"); + return ret; } #if CACHE_NIDATA_SIZE /* - * Free an inode structure when there is not more space - * in the cache + * Free an inode structure when there is not more space + * in the cache */ -void ntfs_inode_nidata_free( const struct CACHED_GENERIC *cached ) +void ntfs_inode_nidata_free(const struct CACHED_GENERIC *cached) { - ntfs_inode_real_close( ( ( const struct CACHED_NIDATA* )cached )->ni ); + ntfs_inode_real_close(((const struct CACHED_NIDATA*)cached)->ni); } /* - * Compute a hash value for an inode entry + * Compute a hash value for an inode entry */ -int ntfs_inode_nidata_hash( const struct CACHED_GENERIC *item ) +int ntfs_inode_nidata_hash(const struct CACHED_GENERIC *item) { - return ( ( ( const struct CACHED_NIDATA* )item )->inum - % ( 2*CACHE_NIDATA_SIZE ) ); + return (((const struct CACHED_NIDATA*)item)->inum + % (2*CACHE_NIDATA_SIZE)); } /* - * inum comparing for entering/fetching from cache + * inum comparing for entering/fetching from cache */ -static int idata_cache_compare( const struct CACHED_GENERIC *cached, - const struct CACHED_GENERIC *wanted ) +static int idata_cache_compare(const struct CACHED_GENERIC *cached, + const struct CACHED_GENERIC *wanted) { - return ( ( ( const struct CACHED_NIDATA* )cached )->inum - != ( ( const struct CACHED_NIDATA* )wanted )->inum ); + return (((const struct CACHED_NIDATA*)cached)->inum + != ((const struct CACHED_NIDATA*)wanted)->inum); } /* - * Invalidate an inode entry when not needed anymore. - * The entry should have been synced, it may be reused later, - * if it is requested before it is dropped from cache. + * Invalidate an inode entry when not needed anymore. + * The entry should have been synced, it may be reused later, + * if it is requested before it is dropped from cache. */ -void ntfs_inode_invalidate( ntfs_volume *vol, const MFT_REF mref ) +void ntfs_inode_invalidate(ntfs_volume *vol, const MFT_REF mref) { - struct CACHED_NIDATA item; - int count; + struct CACHED_NIDATA item; + int count; - item.inum = MREF( mref ); - item.ni = ( ntfs_inode* )NULL; - item.pathname = ( const char* )NULL; - item.varsize = 0; - count = ntfs_invalidate_cache( vol->nidata_cache, - GENERIC( &item ), idata_cache_compare, CACHE_FREE ); + item.inum = MREF(mref); + item.ni = (ntfs_inode*)NULL; + item.pathname = (const char*)NULL; + item.varsize = 0; + count = ntfs_invalidate_cache(vol->nidata_cache, + GENERIC(&item),idata_cache_compare,CACHE_FREE); } #endif /* - * Open an inode + * Open an inode * - * When possible, an entry recorded in the cache is reused + * When possible, an entry recorded in the cache is reused * - * **NEVER REOPEN** an inode, this can lead to a duplicated - * cache entry (hard to detect), and to an obsolete one being - * reused. System files are however protected from being cached. + * **NEVER REOPEN** an inode, this can lead to a duplicated + * cache entry (hard to detect), and to an obsolete one being + * reused. System files are however protected from being cached. */ -ntfs_inode *ntfs_inode_open( ntfs_volume *vol, const MFT_REF mref ) +ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref) { - ntfs_inode *ni; + ntfs_inode *ni; #if CACHE_NIDATA_SIZE - struct CACHED_NIDATA item; - struct CACHED_NIDATA *cached; + struct CACHED_NIDATA item; + struct CACHED_NIDATA *cached; - /* fetch idata from cache */ - item.inum = MREF( mref ); - debug_double_inode( item.inum, 1 ); - item.pathname = ( const char* )NULL; - item.varsize = 0; - cached = ( struct CACHED_NIDATA* )ntfs_fetch_cache( vol->nidata_cache, - GENERIC( &item ), idata_cache_compare ); - if ( cached ) - { - ni = cached->ni; - /* do not keep open entries in cache */ - ntfs_remove_cache( vol->nidata_cache, - ( struct CACHED_GENERIC* )cached, 0 ); - } - else - { - ni = ntfs_inode_real_open( vol, mref ); - } + /* fetch idata from cache */ + item.inum = MREF(mref); + debug_double_inode(item.inum,1); + item.pathname = (const char*)NULL; + item.varsize = 0; + cached = (struct CACHED_NIDATA*)ntfs_fetch_cache(vol->nidata_cache, + GENERIC(&item),idata_cache_compare); + if (cached) { + ni = cached->ni; + /* do not keep open entries in cache */ + ntfs_remove_cache(vol->nidata_cache, + (struct CACHED_GENERIC*)cached,0); + } else { + ni = ntfs_inode_real_open(vol, mref); + } #else - ni = ntfs_inode_real_open( vol, mref ); + ni = ntfs_inode_real_open(vol, mref); #endif - return ( ni ); + return (ni); } /* - * Close an inode entry + * Close an inode entry * - * If cacheing is in use, the entry is synced and kept available - * in cache for further use. + * If cacheing is in use, the entry is synced and kept available + * in cache for further use. * - * System files (inode < 16 or having the IS_4 flag) are protected - * against being cached. + * System files (inode < 16 or having the IS_4 flag) are protected + * against being cached. */ -int ntfs_inode_close( ntfs_inode *ni ) +int ntfs_inode_close(ntfs_inode *ni) { - int res; + int res; #if CACHE_NIDATA_SIZE - BOOL dirty; - struct CACHED_NIDATA item; + BOOL dirty; + struct CACHED_NIDATA item; - if ( ni ) - { - debug_double_inode( ni->mft_no, 0 ); - /* do not cache system files : could lead to double entries */ - if ( ni->vol && ni->vol->nidata_cache - && ( ( ni->mft_no == FILE_root ) - || ( ( ni->mft_no >= FILE_first_user ) - && !( ni->mrec->flags & MFT_RECORD_IS_4 ) ) ) ) - { - /* If we have dirty metadata, write it out. */ - dirty = NInoDirty( ni ) || NInoAttrListDirty( ni ); - if ( dirty ) - { - res = ntfs_inode_sync( ni ); - /* do a real close if sync failed */ - if ( res ) - ntfs_inode_real_close( ni ); - } - else - res = 0; + if (ni) { + debug_double_inode(ni->mft_no,0); + /* do not cache system files : could lead to double entries */ + if (ni->vol && ni->vol->nidata_cache + && ((ni->mft_no == FILE_root) + || ((ni->mft_no >= FILE_first_user) + && !(ni->mrec->flags & MFT_RECORD_IS_4)))) { + /* If we have dirty metadata, write it out. */ + dirty = NInoDirty(ni) || NInoAttrListDirty(ni); + if (dirty) { + res = ntfs_inode_sync(ni); + /* do a real close if sync failed */ + if (res) + ntfs_inode_real_close(ni); + } else + res = 0; - if ( !res ) - { - /* feed idata into cache */ - item.inum = ni->mft_no; - item.ni = ni; - item.pathname = ( const char* )NULL; - item.varsize = 0; - debug_cached_inode( ni ); - ntfs_enter_cache( ni->vol->nidata_cache, - GENERIC( &item ), idata_cache_compare ); - } - } - else - { - /* cache not ready or system file, really close */ - res = ntfs_inode_real_close( ni ); - } - } - else - res = 0; + if (!res) { + /* feed idata into cache */ + item.inum = ni->mft_no; + item.ni = ni; + item.pathname = (const char*)NULL; + item.varsize = 0; + debug_cached_inode(ni); + ntfs_enter_cache(ni->vol->nidata_cache, + GENERIC(&item), idata_cache_compare); + } + } else { + /* cache not ready or system file, really close */ + res = ntfs_inode_real_close(ni); + } + } else + res = 0; #else - res = ntfs_inode_real_close( ni ); + res = ntfs_inode_real_close(ni); #endif - return ( res ); + return (res); } /** * ntfs_extent_inode_open - load an extent inode and attach it to its base - * @base_ni: base ntfs inode - * @mref: mft reference of the extent inode to load (in little endian) + * @base_ni: base ntfs inode + * @mref: mft reference of the extent inode to load (in little endian) * * First check if the extent inode @mref is already attached to the base ntfs * inode @base_ni, and if so, return a pointer to the attached extent inode. @@ -606,352 +568,323 @@ int ntfs_inode_close( ntfs_inode *ni ) * Note, extent inodes are never closed directly. They are automatically * disposed off by the closing of the base inode. */ -ntfs_inode *ntfs_extent_inode_open( ntfs_inode *base_ni, const MFT_REF mref ) +ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni, const MFT_REF mref) { - u64 mft_no = MREF_LE( mref ); - ntfs_inode *ni = NULL; - ntfs_inode **extent_nis; - int i; + u64 mft_no = MREF_LE(mref); + ntfs_inode *ni = NULL; + ntfs_inode **extent_nis; + int i; - if ( !base_ni ) - { - errno = EINVAL; - ntfs_log_perror( "%s", __FUNCTION__ ); - return NULL; - } + if (!base_ni) { + errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); + return NULL; + } + + ntfs_log_enter("Opening extent inode %lld (base mft record %lld).\n", + (unsigned long long)mft_no, + (unsigned long long)base_ni->mft_no); + + /* Is the extent inode already open and attached to the base inode? */ + if (base_ni->nr_extents > 0) { + extent_nis = base_ni->extent_nis; + for (i = 0; i < base_ni->nr_extents; i++) { + u16 seq_no; - ntfs_log_enter( "Opening extent inode %lld (base mft record %lld).\n", - ( unsigned long long )mft_no, - ( unsigned long long )base_ni->mft_no ); + ni = extent_nis[i]; + if (mft_no != ni->mft_no) + continue; + /* Verify the sequence number if given. */ + seq_no = MSEQNO_LE(mref); + if (seq_no && seq_no != le16_to_cpu( + ni->mrec->sequence_number)) { + errno = EIO; + ntfs_log_perror("Found stale extent mft " + "reference mft=%lld", + (long long)ni->mft_no); + goto out; + } + goto out; + } + } + /* Wasn't there, we need to load the extent inode. */ + ni = __ntfs_inode_allocate(base_ni->vol); + if (!ni) + goto out; + if (ntfs_file_record_read(base_ni->vol, le64_to_cpu(mref), &ni->mrec, NULL)) + goto err_out; + ni->mft_no = mft_no; + ni->nr_extents = -1; + ni->base_ni = base_ni; + /* Attach extent inode to base inode, reallocating memory if needed. */ + if (!(base_ni->nr_extents & 3)) { + i = (base_ni->nr_extents + 4) * sizeof(ntfs_inode *); - /* Is the extent inode already open and attached to the base inode? */ - if ( base_ni->nr_extents > 0 ) - { - extent_nis = base_ni->extent_nis; - for ( i = 0; i < base_ni->nr_extents; i++ ) - { - u16 seq_no; - - ni = extent_nis[i]; - if ( mft_no != ni->mft_no ) - continue; - /* Verify the sequence number if given. */ - seq_no = MSEQNO_LE( mref ); - if ( seq_no && seq_no != le16_to_cpu( - ni->mrec->sequence_number ) ) - { - errno = EIO; - ntfs_log_perror( "Found stale extent mft " - "reference mft=%lld", - ( long long )ni->mft_no ); - goto out; - } - goto out; - } - } - /* Wasn't there, we need to load the extent inode. */ - ni = __ntfs_inode_allocate( base_ni->vol ); - if ( !ni ) - goto out; - if ( ntfs_file_record_read( base_ni->vol, le64_to_cpu( mref ), &ni->mrec, NULL ) ) - goto err_out; - ni->mft_no = mft_no; - ni->nr_extents = -1; - ni->base_ni = base_ni; - /* Attach extent inode to base inode, reallocating memory if needed. */ - if ( !( base_ni->nr_extents & 3 ) ) - { - i = ( base_ni->nr_extents + 4 ) * sizeof( ntfs_inode * ); - - extent_nis = ntfs_malloc( i ); - if ( !extent_nis ) - goto err_out; - if ( base_ni->nr_extents ) - { - memcpy( extent_nis, base_ni->extent_nis, - i - 4 * sizeof( ntfs_inode * ) ); - free( base_ni->extent_nis ); - } - base_ni->extent_nis = extent_nis; - } - base_ni->extent_nis[base_ni->nr_extents++] = ni; + extent_nis = ntfs_malloc(i); + if (!extent_nis) + goto err_out; + if (base_ni->nr_extents) { + memcpy(extent_nis, base_ni->extent_nis, + i - 4 * sizeof(ntfs_inode *)); + free(base_ni->extent_nis); + } + base_ni->extent_nis = extent_nis; + } + base_ni->extent_nis[base_ni->nr_extents++] = ni; out: - ntfs_log_leave( "\n" ); - return ni; + ntfs_log_leave("\n"); + return ni; err_out: - __ntfs_inode_release( ni ); - ni = NULL; - goto out; + __ntfs_inode_release(ni); + ni = NULL; + goto out; } /** * ntfs_inode_attach_all_extents - attach all extents for target inode - * @ni: opened ntfs inode for which perform attach + * @ni: opened ntfs inode for which perform attach * * Return 0 on success and -1 on error with errno set to the error code. */ -int ntfs_inode_attach_all_extents( ntfs_inode *ni ) +int ntfs_inode_attach_all_extents(ntfs_inode *ni) { - ATTR_LIST_ENTRY *ale; - u64 prev_attached = 0; + ATTR_LIST_ENTRY *ale; + u64 prev_attached = 0; - if ( !ni ) - { - ntfs_log_trace( "Invalid arguments.\n" ); - errno = EINVAL; - return -1; - } + if (!ni) { + ntfs_log_trace("Invalid arguments.\n"); + errno = EINVAL; + return -1; + } - if ( ni->nr_extents == -1 ) - ni = ni->base_ni; + if (ni->nr_extents == -1) + ni = ni->base_ni; - ntfs_log_trace( "Entering for inode 0x%llx.\n", ( long long ) ni->mft_no ); + ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no); - /* Inode haven't got attribute list, thus nothing to attach. */ - if ( !NInoAttrList( ni ) ) - return 0; + /* Inode haven't got attribute list, thus nothing to attach. */ + if (!NInoAttrList(ni)) + return 0; - if ( !ni->attr_list ) - { - ntfs_log_trace( "Corrupt in-memory struct.\n" ); - errno = EINVAL; - return -1; - } + if (!ni->attr_list) { + ntfs_log_trace("Corrupt in-memory struct.\n"); + errno = EINVAL; + return -1; + } - /* Walk through attribute list and attach all extents. */ - errno = 0; - ale = ( ATTR_LIST_ENTRY * )ni->attr_list; - while ( ( u8* )ale < ni->attr_list + ni->attr_list_size ) - { - if ( ni->mft_no != MREF_LE( ale->mft_reference ) && - prev_attached != MREF_LE( ale->mft_reference ) ) - { - if ( !ntfs_extent_inode_open( ni, ale->mft_reference ) ) - { - ntfs_log_trace( "Couldn't attach extent inode.\n" ); - return -1; - } - prev_attached = MREF_LE( ale->mft_reference ); - } - ale = ( ATTR_LIST_ENTRY * )( ( u8* )ale + le16_to_cpu( ale->length ) ); - } - return 0; + /* Walk through attribute list and attach all extents. */ + errno = 0; + ale = (ATTR_LIST_ENTRY *)ni->attr_list; + while ((u8*)ale < ni->attr_list + ni->attr_list_size) { + if (ni->mft_no != MREF_LE(ale->mft_reference) && + prev_attached != MREF_LE(ale->mft_reference)) { + if (!ntfs_extent_inode_open(ni, ale->mft_reference)) { + ntfs_log_trace("Couldn't attach extent inode.\n"); + return -1; + } + prev_attached = MREF_LE(ale->mft_reference); + } + ale = (ATTR_LIST_ENTRY *)((u8*)ale + le16_to_cpu(ale->length)); + } + return 0; } /** * ntfs_inode_sync_standard_information - update standard information attribute - * @ni: ntfs inode to update standard information + * @ni: ntfs inode to update standard information * * Return 0 on success or -1 on error with errno set to the error code. */ -static int ntfs_inode_sync_standard_information( ntfs_inode *ni ) +static int ntfs_inode_sync_standard_information(ntfs_inode *ni) { - ntfs_attr_search_ctx *ctx; - STANDARD_INFORMATION *std_info; - u32 lth; - le32 lthle; + ntfs_attr_search_ctx *ctx; + STANDARD_INFORMATION *std_info; + u32 lth; + le32 lthle; - ntfs_log_trace( "Entering for inode %lld\n", ( long long )ni->mft_no ); + ntfs_log_trace("Entering for inode %lld\n", (long long)ni->mft_no); - ctx = ntfs_attr_get_search_ctx( ni, NULL ); - if ( !ctx ) - return -1; - if ( ntfs_attr_lookup( AT_STANDARD_INFORMATION, AT_UNNAMED, - 0, CASE_SENSITIVE, 0, NULL, 0, ctx ) ) - { - ntfs_log_perror( "Failed to sync standard info (inode %lld)", - ( long long )ni->mft_no ); - ntfs_attr_put_search_ctx( ctx ); - return -1; - } - std_info = ( STANDARD_INFORMATION * )( ( u8 * )ctx->attr + - le16_to_cpu( ctx->attr->value_offset ) ); - std_info->file_attributes = ni->flags; - if ( !test_nino_flag( ni, TimesSet ) ) - { - std_info->creation_time = ni->creation_time; - std_info->last_data_change_time = ni->last_data_change_time; - std_info->last_mft_change_time = ni->last_mft_change_time; - std_info->last_access_time = ni->last_access_time; - } + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return -1; + if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED, + 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { + ntfs_log_perror("Failed to sync standard info (inode %lld)", + (long long)ni->mft_no); + ntfs_attr_put_search_ctx(ctx); + return -1; + } + std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + std_info->file_attributes = ni->flags; + if (!test_nino_flag(ni, TimesSet)) { + std_info->creation_time = ni->creation_time; + std_info->last_data_change_time = ni->last_data_change_time; + std_info->last_mft_change_time = ni->last_mft_change_time; + std_info->last_access_time = ni->last_access_time; + } - /* JPA update v3.x extensions, ensuring consistency */ + /* JPA update v3.x extensions, ensuring consistency */ - lthle = ctx->attr->length; - lth = le32_to_cpu( lthle ); - if ( test_nino_flag( ni, v3_Extensions ) - && ( lth <= sizeof( STANDARD_INFORMATION ) ) ) - ntfs_log_error( "bad sync of standard information\n" ); + lthle = ctx->attr->length; + lth = le32_to_cpu(lthle); + if (test_nino_flag(ni, v3_Extensions) + && (lth <= sizeof(STANDARD_INFORMATION))) + ntfs_log_error("bad sync of standard information\n"); - if ( lth > sizeof( STANDARD_INFORMATION ) ) - { - std_info->owner_id = ni->owner_id; - std_info->security_id = ni->security_id; - std_info->quota_charged = ni->quota_charged; - std_info->usn = ni->usn; - } - ntfs_inode_mark_dirty( ctx->ntfs_ino ); - ntfs_attr_put_search_ctx( ctx ); - return 0; + if (lth > sizeof(STANDARD_INFORMATION)) { + std_info->owner_id = ni->owner_id; + std_info->security_id = ni->security_id; + std_info->quota_charged = ni->quota_charged; + std_info->usn = ni->usn; + } + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + return 0; } /** * ntfs_inode_sync_file_name - update FILE_NAME attributes - * @ni: ntfs inode to update FILE_NAME attributes + * @ni: ntfs inode to update FILE_NAME attributes * * Update all FILE_NAME attributes for inode @ni in the index. * * Return 0 on success or -1 on error with errno set to the error code. */ -static int ntfs_inode_sync_file_name( ntfs_inode *ni, ntfs_inode *dir_ni ) +static int ntfs_inode_sync_file_name(ntfs_inode *ni, ntfs_inode *dir_ni) { - ntfs_attr_search_ctx *ctx = NULL; - ntfs_index_context *ictx; - ntfs_inode *index_ni; - FILE_NAME_ATTR *fn; - FILE_NAME_ATTR *fnx; - REPARSE_POINT *rpp; - le32 reparse_tag; - int err = 0; + ntfs_attr_search_ctx *ctx = NULL; + ntfs_index_context *ictx; + ntfs_inode *index_ni; + FILE_NAME_ATTR *fn; + FILE_NAME_ATTR *fnx; + REPARSE_POINT *rpp; + le32 reparse_tag; + int err = 0; - ntfs_log_trace( "Entering for inode %lld\n", ( long long )ni->mft_no ); + ntfs_log_trace("Entering for inode %lld\n", (long long)ni->mft_no); - ctx = ntfs_attr_get_search_ctx( ni, NULL ); - if ( !ctx ) - { - err = errno; - goto err_out; - } - /* Collect the reparse tag, if any */ - reparse_tag = cpu_to_le32( 0 ); - if ( ni->flags & FILE_ATTR_REPARSE_POINT ) - { - if ( !ntfs_attr_lookup( AT_REPARSE_POINT, NULL, - 0, CASE_SENSITIVE, 0, NULL, 0, ctx ) ) - { - rpp = ( REPARSE_POINT* )( ( u8 * )ctx->attr + - le16_to_cpu( ctx->attr->value_offset ) ); - reparse_tag = rpp->reparse_tag; - } - ntfs_attr_reinit_search_ctx( ctx ); - } - /* Walk through all FILE_NAME attributes and update them. */ - while ( !ntfs_attr_lookup( AT_FILE_NAME, NULL, 0, 0, 0, NULL, 0, ctx ) ) - { - fn = ( FILE_NAME_ATTR * )( ( u8 * )ctx->attr + - le16_to_cpu( ctx->attr->value_offset ) ); - if ( MREF_LE( fn->parent_directory ) == ni->mft_no ) - { - /* - * WARNING: We cheat here and obtain 2 attribute - * search contexts for one inode (first we obtained - * above, second will be obtained inside - * ntfs_index_lookup), it's acceptable for library, - * but will deadlock in the kernel. - */ - index_ni = ni; - } - else if ( dir_ni ) - index_ni = dir_ni; - else - index_ni = ntfs_inode_open( ni->vol, - le64_to_cpu( fn->parent_directory ) ); - if ( !index_ni ) - { - if ( !err ) - err = errno; - ntfs_log_perror( "Failed to open inode %lld with index", - ( long long )le64_to_cpu( fn->parent_directory ) ); - continue; - } - ictx = ntfs_index_ctx_get( index_ni, NTFS_INDEX_I30, 4 ); - if ( !ictx ) - { - if ( !err ) - err = errno; - ntfs_log_perror( "Failed to get index ctx, inode %lld", - ( long long )index_ni->mft_no ); - if ( ( ni != index_ni ) && !dir_ni - && ntfs_inode_close( index_ni ) && !err ) - err = errno; - continue; - } - if ( ntfs_index_lookup( fn, sizeof( FILE_NAME_ATTR ), ictx ) ) - { - if ( !err ) - { - if ( errno == ENOENT ) - err = EIO; - else - err = errno; - } - ntfs_log_perror( "Index lookup failed, inode %lld", - ( long long )index_ni->mft_no ); - ntfs_index_ctx_put( ictx ); - if ( ni != index_ni && ntfs_inode_close( index_ni ) && !err ) - err = errno; - continue; - } - /* Update flags and file size. */ - fnx = ( FILE_NAME_ATTR * )ictx->data; - fnx->file_attributes = - ( fnx->file_attributes & ~FILE_ATTR_VALID_FLAGS ) | - ( ni->flags & FILE_ATTR_VALID_FLAGS ); - if ( ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ) - fnx->data_size = fnx->allocated_size - = const_cpu_to_le64( 0 ); - else - { - fnx->allocated_size = cpu_to_sle64( ni->allocated_size ); - fnx->data_size = cpu_to_sle64( ni->data_size ); - } - /* update or clear the reparse tag in the index */ - fnx->reparse_point_tag = reparse_tag; - if ( !test_nino_flag( ni, TimesSet ) ) - { - fnx->creation_time = ni->creation_time; - fnx->last_data_change_time = ni->last_data_change_time; - fnx->last_mft_change_time = ni->last_mft_change_time; - fnx->last_access_time = ni->last_access_time; - } - else - { - fnx->creation_time = fn->creation_time; - fnx->last_data_change_time = fn->last_data_change_time; - fnx->last_mft_change_time = fn->last_mft_change_time; - fnx->last_access_time = fn->last_access_time; - } - ntfs_index_entry_mark_dirty( ictx ); - ntfs_index_ctx_put( ictx ); - if ( ( ni != index_ni ) && !dir_ni - && ntfs_inode_close( index_ni ) && !err ) - err = errno; - } - /* Check for real error occurred. */ - if ( errno != ENOENT ) - { - err = errno; - ntfs_log_perror( "Attribute lookup failed, inode %lld", - ( long long )ni->mft_no ); - goto err_out; - } - ntfs_attr_put_search_ctx( ctx ); - if ( err ) - { - errno = err; - return -1; - } - return 0; + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) { + err = errno; + goto err_out; + } + /* Collect the reparse tag, if any */ + reparse_tag = cpu_to_le32(0); + if (ni->flags & FILE_ATTR_REPARSE_POINT) { + if (!ntfs_attr_lookup(AT_REPARSE_POINT, NULL, + 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { + rpp = (REPARSE_POINT*)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + reparse_tag = rpp->reparse_tag; + } + ntfs_attr_reinit_search_ctx(ctx); + } + /* Walk through all FILE_NAME attributes and update them. */ + while (!ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, 0, 0, NULL, 0, ctx)) { + fn = (FILE_NAME_ATTR *)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + if (MREF_LE(fn->parent_directory) == ni->mft_no) { + /* + * WARNING: We cheat here and obtain 2 attribute + * search contexts for one inode (first we obtained + * above, second will be obtained inside + * ntfs_index_lookup), it's acceptable for library, + * but will deadlock in the kernel. + */ + index_ni = ni; + } else + if (dir_ni) + index_ni = dir_ni; + else + index_ni = ntfs_inode_open(ni->vol, + le64_to_cpu(fn->parent_directory)); + if (!index_ni) { + if (!err) + err = errno; + ntfs_log_perror("Failed to open inode %lld with index", + (long long)le64_to_cpu(fn->parent_directory)); + continue; + } + ictx = ntfs_index_ctx_get(index_ni, NTFS_INDEX_I30, 4); + if (!ictx) { + if (!err) + err = errno; + ntfs_log_perror("Failed to get index ctx, inode %lld", + (long long)index_ni->mft_no); + if ((ni != index_ni) && !dir_ni + && ntfs_inode_close(index_ni) && !err) + err = errno; + continue; + } + if (ntfs_index_lookup(fn, sizeof(FILE_NAME_ATTR), ictx)) { + if (!err) { + if (errno == ENOENT) + err = EIO; + else + err = errno; + } + ntfs_log_perror("Index lookup failed, inode %lld", + (long long)index_ni->mft_no); + ntfs_index_ctx_put(ictx); + if (ni != index_ni && ntfs_inode_close(index_ni) && !err) + err = errno; + continue; + } + /* Update flags and file size. */ + fnx = (FILE_NAME_ATTR *)ictx->data; + fnx->file_attributes = + (fnx->file_attributes & ~FILE_ATTR_VALID_FLAGS) | + (ni->flags & FILE_ATTR_VALID_FLAGS); + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + fnx->data_size = fnx->allocated_size + = const_cpu_to_le64(0); + else { + fnx->allocated_size = cpu_to_sle64(ni->allocated_size); + fnx->data_size = cpu_to_sle64(ni->data_size); + } + /* update or clear the reparse tag in the index */ + fnx->reparse_point_tag = reparse_tag; + if (!test_nino_flag(ni, TimesSet)) { + fnx->creation_time = ni->creation_time; + fnx->last_data_change_time = ni->last_data_change_time; + fnx->last_mft_change_time = ni->last_mft_change_time; + fnx->last_access_time = ni->last_access_time; + } else { + fnx->creation_time = fn->creation_time; + fnx->last_data_change_time = fn->last_data_change_time; + fnx->last_mft_change_time = fn->last_mft_change_time; + fnx->last_access_time = fn->last_access_time; + } + ntfs_index_entry_mark_dirty(ictx); + ntfs_index_ctx_put(ictx); + if ((ni != index_ni) && !dir_ni + && ntfs_inode_close(index_ni) && !err) + err = errno; + } + /* Check for real error occurred. */ + if (errno != ENOENT) { + err = errno; + ntfs_log_perror("Attribute lookup failed, inode %lld", + (long long)ni->mft_no); + goto err_out; + } + ntfs_attr_put_search_ctx(ctx); + if (err) { + errno = err; + return -1; + } + return 0; err_out: - if ( ctx ) - ntfs_attr_put_search_ctx( ctx ); - errno = err; - return -1; + if (ctx) + ntfs_attr_put_search_ctx(ctx); + errno = err; + return -1; } /** * ntfs_inode_sync - write the inode (and its dirty extents) to disk - * @ni: ntfs inode to write + * @ni: ntfs inode to write * * Write the inode @ni to disk as well as its dirty extent inodes if such * exist and @ni is a base inode. If @ni is an extent inode, only @ni is @@ -964,181 +897,158 @@ err_out: * * Return 0 on success or -1 on error with errno set to the error code. * The following error codes are defined: - * EINVAL - Invalid arguments were passed to the function. - * EBUSY - Inode and/or one of its extents is busy, try again later. - * EIO - I/O error while writing the inode (or one of its extents). + * EINVAL - Invalid arguments were passed to the function. + * EBUSY - Inode and/or one of its extents is busy, try again later. + * EIO - I/O error while writing the inode (or one of its extents). */ -static int ntfs_inode_sync_in_dir( ntfs_inode *ni, ntfs_inode *dir_ni ) +static int ntfs_inode_sync_in_dir(ntfs_inode *ni, ntfs_inode *dir_ni) { - int ret = 0; - int err = 0; - if ( !ni ) - { - errno = EINVAL; - ntfs_log_error( "Failed to sync NULL inode\n" ); - return -1; - } + int ret = 0; + int err = 0; + if (!ni) { + errno = EINVAL; + ntfs_log_error("Failed to sync NULL inode\n"); + return -1; + } - ntfs_log_enter( "Entering for inode %lld\n", ( long long )ni->mft_no ); + ntfs_log_enter("Entering for inode %lld\n", (long long)ni->mft_no); - /* Update STANDARD_INFORMATION. */ - if ( ( ni->mrec->flags & MFT_RECORD_IN_USE ) && ni->nr_extents != -1 && - ntfs_inode_sync_standard_information( ni ) ) - { - if ( !err || errno == EIO ) - { - err = errno; - if ( err != EIO ) - err = EBUSY; - } - } + /* Update STANDARD_INFORMATION. */ + if ((ni->mrec->flags & MFT_RECORD_IN_USE) && ni->nr_extents != -1 && + ntfs_inode_sync_standard_information(ni)) { + if (!err || errno == EIO) { + err = errno; + if (err != EIO) + err = EBUSY; + } + } - /* Update FILE_NAME's in the index. */ - if ( ( ni->mrec->flags & MFT_RECORD_IN_USE ) && ni->nr_extents != -1 && - NInoFileNameTestAndClearDirty( ni ) && - ntfs_inode_sync_file_name( ni, dir_ni ) ) - { - if ( !err || errno == EIO ) - { - err = errno; - if ( err != EIO ) - err = EBUSY; - } - ntfs_log_perror( "Failed to sync FILE_NAME (inode %lld)", - ( long long )ni->mft_no ); - NInoFileNameSetDirty( ni ); - } + /* Update FILE_NAME's in the index. */ + if ((ni->mrec->flags & MFT_RECORD_IN_USE) && ni->nr_extents != -1 && + NInoFileNameTestAndClearDirty(ni) && + ntfs_inode_sync_file_name(ni, dir_ni)) { + if (!err || errno == EIO) { + err = errno; + if (err != EIO) + err = EBUSY; + } + ntfs_log_perror("Failed to sync FILE_NAME (inode %lld)", + (long long)ni->mft_no); + NInoFileNameSetDirty(ni); + } - /* Write out attribute list from cache to disk. */ - if ( ( ni->mrec->flags & MFT_RECORD_IN_USE ) && ni->nr_extents != -1 && - NInoAttrList( ni ) && NInoAttrListTestAndClearDirty( ni ) ) - { - ntfs_attr *na; - - na = ntfs_attr_open( ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0 ); - if ( !na ) - { - if ( !err || errno == EIO ) - { - err = errno; - if ( err != EIO ) - err = EBUSY; - ntfs_log_perror( "Attribute list sync failed " - "(open, inode %lld)", - ( long long )ni->mft_no ); - } - NInoAttrListSetDirty( ni ); - goto sync_inode; - } - - if ( na->data_size == ni->attr_list_size ) - { - if ( ntfs_attr_pwrite( na, 0, ni->attr_list_size, - ni->attr_list ) != ni->attr_list_size ) - { - if ( !err || errno == EIO ) - { - err = errno; - if ( err != EIO ) - err = EBUSY; - ntfs_log_perror( "Attribute list sync " - "failed (write, inode %lld)", - ( long long )ni->mft_no ); - } - NInoAttrListSetDirty( ni ); - } - } - else - { - err = EIO; - ntfs_log_error( "Attribute list sync failed (bad size, " - "inode %lld)\n", ( long long )ni->mft_no ); - NInoAttrListSetDirty( ni ); - } - ntfs_attr_close( na ); - } + /* Write out attribute list from cache to disk. */ + if ((ni->mrec->flags & MFT_RECORD_IN_USE) && ni->nr_extents != -1 && + NInoAttrList(ni) && NInoAttrListTestAndClearDirty(ni)) { + ntfs_attr *na; + na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); + if (!na) { + if (!err || errno == EIO) { + err = errno; + if (err != EIO) + err = EBUSY; + ntfs_log_perror("Attribute list sync failed " + "(open, inode %lld)", + (long long)ni->mft_no); + } + NInoAttrListSetDirty(ni); + goto sync_inode; + } + + if (na->data_size == ni->attr_list_size) { + if (ntfs_attr_pwrite(na, 0, ni->attr_list_size, + ni->attr_list) != ni->attr_list_size) { + if (!err || errno == EIO) { + err = errno; + if (err != EIO) + err = EBUSY; + ntfs_log_perror("Attribute list sync " + "failed (write, inode %lld)", + (long long)ni->mft_no); + } + NInoAttrListSetDirty(ni); + } + } else { + err = EIO; + ntfs_log_error("Attribute list sync failed (bad size, " + "inode %lld)\n", (long long)ni->mft_no); + NInoAttrListSetDirty(ni); + } + ntfs_attr_close(na); + } + sync_inode: - /* Write this inode out to the $MFT (and $MFTMirr if applicable). */ - if ( NInoTestAndClearDirty( ni ) ) - { - if ( ntfs_mft_record_write( ni->vol, ni->mft_no, ni->mrec ) ) - { - if ( !err || errno == EIO ) - { - err = errno; - if ( err != EIO ) - err = EBUSY; - } - NInoSetDirty( ni ); - ntfs_log_perror( "MFT record sync failed, inode %lld", - ( long long )ni->mft_no ); - } - } + /* Write this inode out to the $MFT (and $MFTMirr if applicable). */ + if (NInoTestAndClearDirty(ni)) { + if (ntfs_mft_record_write(ni->vol, ni->mft_no, ni->mrec)) { + if (!err || errno == EIO) { + err = errno; + if (err != EIO) + err = EBUSY; + } + NInoSetDirty(ni); + ntfs_log_perror("MFT record sync failed, inode %lld", + (long long)ni->mft_no); + } + } - /* If this is a base inode with extents write all dirty extents, too. */ - if ( ni->nr_extents > 0 ) - { - s32 i; + /* If this is a base inode with extents write all dirty extents, too. */ + if (ni->nr_extents > 0) { + s32 i; - for ( i = 0; i < ni->nr_extents; ++i ) - { - ntfs_inode *eni; + for (i = 0; i < ni->nr_extents; ++i) { + ntfs_inode *eni; - eni = ni->extent_nis[i]; - if ( !NInoTestAndClearDirty( eni ) ) - continue; + eni = ni->extent_nis[i]; + if (!NInoTestAndClearDirty(eni)) + continue; + + if (ntfs_mft_record_write(eni->vol, eni->mft_no, + eni->mrec)) { + if (!err || errno == EIO) { + err = errno; + if (err != EIO) + err = EBUSY; + } + NInoSetDirty(eni); + ntfs_log_perror("Extent MFT record sync failed," + " inode %lld/%lld", + (long long)ni->mft_no, + (long long)eni->mft_no); + } + } + } - if ( ntfs_mft_record_write( eni->vol, eni->mft_no, - eni->mrec ) ) - { - if ( !err || errno == EIO ) - { - err = errno; - if ( err != EIO ) - err = EBUSY; - } - NInoSetDirty( eni ); - ntfs_log_perror( "Extent MFT record sync failed," - " inode %lld/%lld", - ( long long )ni->mft_no, - ( long long )eni->mft_no ); - } - } - } - - if ( err ) - { - errno = err; - ret = -1; - } - - ntfs_log_leave( "\n" ); - return ret; + if (err) { + errno = err; + ret = -1; + } + + ntfs_log_leave("\n"); + return ret; } -int ntfs_inode_sync( ntfs_inode *ni ) +int ntfs_inode_sync(ntfs_inode *ni) { - return ( ntfs_inode_sync_in_dir( ni, ( ntfs_inode* )NULL ) ); + return (ntfs_inode_sync_in_dir(ni, (ntfs_inode*)NULL)); } /* - * Close an inode with an open parent inode + * Close an inode with an open parent inode */ -int ntfs_inode_close_in_dir( ntfs_inode *ni, ntfs_inode *dir_ni ) +int ntfs_inode_close_in_dir(ntfs_inode *ni, ntfs_inode *dir_ni) { - int res; + int res; - res = ntfs_inode_sync_in_dir( ni, dir_ni ); - if ( res ) - { - if ( errno != EIO ) - errno = EBUSY; - } - else - res = ntfs_inode_close( ni ); - return ( res ); + res = ntfs_inode_sync_in_dir(ni, dir_ni); + if (res) { + if (errno != EIO) + errno = EBUSY; + } else + res = ntfs_inode_close(ni); + return (res); } /** @@ -1147,553 +1057,510 @@ int ntfs_inode_close_in_dir( ntfs_inode *ni, ntfs_inode *dir_ni ) * * Return 0 on success or -1 on error with errno set to the error code. * The following error codes are defined: - * EINVAL - Invalid arguments were passed to the function. - * EEXIST - Attribute list already exist. - * EIO - Input/Ouput error occurred. - * ENOMEM - Not enough memory to perform add. + * EINVAL - Invalid arguments were passed to the function. + * EEXIST - Attribute list already exist. + * EIO - Input/Ouput error occurred. + * ENOMEM - Not enough memory to perform add. */ -int ntfs_inode_add_attrlist( ntfs_inode *ni ) +int ntfs_inode_add_attrlist(ntfs_inode *ni) { - int err; - ntfs_attr_search_ctx *ctx; - u8 *al = NULL, *aln; - int al_len = 0; - ATTR_LIST_ENTRY *ale = NULL; - ntfs_attr *na; + int err; + ntfs_attr_search_ctx *ctx; + u8 *al = NULL, *aln; + int al_len = 0; + ATTR_LIST_ENTRY *ale = NULL; + ntfs_attr *na; - if ( !ni ) - { - errno = EINVAL; - ntfs_log_perror( "%s", __FUNCTION__ ); - return -1; - } + if (!ni) { + errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); + return -1; + } - ntfs_log_trace( "inode %llu\n", ( unsigned long long ) ni->mft_no ); + ntfs_log_trace("inode %llu\n", (unsigned long long) ni->mft_no); - if ( NInoAttrList( ni ) || ni->nr_extents ) - { - errno = EEXIST; - ntfs_log_perror( "Inode already has attribute list" ); - return -1; - } + if (NInoAttrList(ni) || ni->nr_extents) { + errno = EEXIST; + ntfs_log_perror("Inode already has attribute list"); + return -1; + } - /* Form attribute list. */ - ctx = ntfs_attr_get_search_ctx( ni, NULL ); - if ( !ctx ) - { - err = errno; - goto err_out; - } - /* Walk through all attributes. */ - while ( !ntfs_attr_lookup( AT_UNUSED, NULL, 0, 0, 0, NULL, 0, ctx ) ) - { + /* Form attribute list. */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) { + err = errno; + goto err_out; + } + /* Walk through all attributes. */ + while (!ntfs_attr_lookup(AT_UNUSED, NULL, 0, 0, 0, NULL, 0, ctx)) { + + int ale_size; + + if (ctx->attr->type == AT_ATTRIBUTE_LIST) { + err = EIO; + ntfs_log_perror("Attribute list already present"); + goto put_err_out; + } + + ale_size = (sizeof(ATTR_LIST_ENTRY) + sizeof(ntfschar) * + ctx->attr->name_length + 7) & ~7; + al_len += ale_size; + + aln = realloc(al, al_len); + if (!aln) { + err = errno; + ntfs_log_perror("Failed to realloc %d bytes", al_len); + goto put_err_out; + } + ale = (ATTR_LIST_ENTRY *)(aln + ((u8 *)ale - al)); + al = aln; + + memset(ale, 0, ale_size); + + /* Add attribute to attribute list. */ + ale->type = ctx->attr->type; + ale->length = cpu_to_le16((sizeof(ATTR_LIST_ENTRY) + + sizeof(ntfschar) * ctx->attr->name_length + 7) & ~7); + ale->name_length = ctx->attr->name_length; + ale->name_offset = (u8 *)ale->name - (u8 *)ale; + if (ctx->attr->non_resident) + ale->lowest_vcn = ctx->attr->lowest_vcn; + else + ale->lowest_vcn = 0; + ale->mft_reference = MK_LE_MREF(ni->mft_no, + le16_to_cpu(ni->mrec->sequence_number)); + ale->instance = ctx->attr->instance; + memcpy(ale->name, (u8 *)ctx->attr + + le16_to_cpu(ctx->attr->name_offset), + ctx->attr->name_length * sizeof(ntfschar)); + ale = (ATTR_LIST_ENTRY *)(al + al_len); + } + /* Check for real error occurred. */ + if (errno != ENOENT) { + err = errno; + ntfs_log_perror("%s: Attribute lookup failed, inode %lld", + __FUNCTION__, (long long)ni->mft_no); + goto put_err_out; + } - int ale_size; + /* Set in-memory attribute list. */ + ni->attr_list = al; + ni->attr_list_size = al_len; + NInoSetAttrList(ni); + NInoAttrListSetDirty(ni); - if ( ctx->attr->type == AT_ATTRIBUTE_LIST ) - { - err = EIO; - ntfs_log_perror( "Attribute list already present" ); - goto put_err_out; - } + /* Free space if there is not enough it for $ATTRIBUTE_LIST. */ + if (le32_to_cpu(ni->mrec->bytes_allocated) - + le32_to_cpu(ni->mrec->bytes_in_use) < + offsetof(ATTR_RECORD, resident_end)) { + if (ntfs_inode_free_space(ni, + offsetof(ATTR_RECORD, resident_end))) { + /* Failed to free space. */ + err = errno; + ntfs_log_perror("Failed to free space for attrlist"); + goto rollback; + } + } - ale_size = ( sizeof( ATTR_LIST_ENTRY ) + sizeof( ntfschar ) * - ctx->attr->name_length + 7 ) & ~7; - al_len += ale_size; + /* Add $ATTRIBUTE_LIST to mft record. */ + if (ntfs_resident_attr_record_add(ni, + AT_ATTRIBUTE_LIST, NULL, 0, NULL, 0, 0) < 0) { + err = errno; + ntfs_log_perror("Couldn't add $ATTRIBUTE_LIST to MFT"); + goto rollback; + } - aln = realloc( al, al_len ); - if ( !aln ) - { - err = errno; - ntfs_log_perror( "Failed to realloc %d bytes", al_len ); - goto put_err_out; - } - ale = ( ATTR_LIST_ENTRY * )( aln + ( ( u8 * )ale - al ) ); - al = aln; - - memset( ale, 0, ale_size ); - - /* Add attribute to attribute list. */ - ale->type = ctx->attr->type; - ale->length = cpu_to_le16( ( sizeof( ATTR_LIST_ENTRY ) + - sizeof( ntfschar ) * ctx->attr->name_length + 7 ) & ~7 ); - ale->name_length = ctx->attr->name_length; - ale->name_offset = ( u8 * )ale->name - ( u8 * )ale; - if ( ctx->attr->non_resident ) - ale->lowest_vcn = ctx->attr->lowest_vcn; - else - ale->lowest_vcn = 0; - ale->mft_reference = MK_LE_MREF( ni->mft_no, - le16_to_cpu( ni->mrec->sequence_number ) ); - ale->instance = ctx->attr->instance; - memcpy( ale->name, ( u8 * )ctx->attr + - le16_to_cpu( ctx->attr->name_offset ), - ctx->attr->name_length * sizeof( ntfschar ) ); - ale = ( ATTR_LIST_ENTRY * )( al + al_len ); - } - /* Check for real error occurred. */ - if ( errno != ENOENT ) - { - err = errno; - ntfs_log_perror( "%s: Attribute lookup failed, inode %lld", - __FUNCTION__, ( long long )ni->mft_no ); - goto put_err_out; - } - - /* Set in-memory attribute list. */ - ni->attr_list = al; - ni->attr_list_size = al_len; - NInoSetAttrList( ni ); - NInoAttrListSetDirty( ni ); - - /* Free space if there is not enough it for $ATTRIBUTE_LIST. */ - if ( le32_to_cpu( ni->mrec->bytes_allocated ) - - le32_to_cpu( ni->mrec->bytes_in_use ) < - offsetof( ATTR_RECORD, resident_end ) ) - { - if ( ntfs_inode_free_space( ni, - offsetof( ATTR_RECORD, resident_end ) ) ) - { - /* Failed to free space. */ - err = errno; - ntfs_log_perror( "Failed to free space for attrlist" ); - goto rollback; - } - } - - /* Add $ATTRIBUTE_LIST to mft record. */ - if ( ntfs_resident_attr_record_add( ni, - AT_ATTRIBUTE_LIST, NULL, 0, NULL, 0, 0 ) < 0 ) - { - err = errno; - ntfs_log_perror( "Couldn't add $ATTRIBUTE_LIST to MFT" ); - goto rollback; - } - - /* Resize it. */ - na = ntfs_attr_open( ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0 ); - if ( !na ) - { - err = errno; - ntfs_log_perror( "Failed to open just added $ATTRIBUTE_LIST" ); - goto remove_attrlist_record; - } - if ( ntfs_attr_truncate( na, al_len ) ) - { - err = errno; - ntfs_log_perror( "Failed to resize just added $ATTRIBUTE_LIST" ); - ntfs_attr_close( na ); - goto remove_attrlist_record;; - } - - ntfs_attr_put_search_ctx( ctx ); - ntfs_attr_close( na ); - return 0; + /* Resize it. */ + na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); + if (!na) { + err = errno; + ntfs_log_perror("Failed to open just added $ATTRIBUTE_LIST"); + goto remove_attrlist_record; + } + if (ntfs_attr_truncate(na, al_len)) { + err = errno; + ntfs_log_perror("Failed to resize just added $ATTRIBUTE_LIST"); + ntfs_attr_close(na); + goto remove_attrlist_record;; + } + + ntfs_attr_put_search_ctx(ctx); + ntfs_attr_close(na); + return 0; remove_attrlist_record: - /* Prevent ntfs_attr_recorm_rm from freeing attribute list. */ - ni->attr_list = NULL; - NInoClearAttrList( ni ); - /* Remove $ATTRIBUTE_LIST record. */ - ntfs_attr_reinit_search_ctx( ctx ); - if ( !ntfs_attr_lookup( AT_ATTRIBUTE_LIST, NULL, 0, - CASE_SENSITIVE, 0, NULL, 0, ctx ) ) - { - if ( ntfs_attr_record_rm( ctx ) ) - ntfs_log_perror( "Rollback failed to remove attrlist" ); - } - else - ntfs_log_perror( "Rollback failed to find attrlist" ); - /* Setup back in-memory runlist. */ - ni->attr_list = al; - ni->attr_list_size = al_len; - NInoSetAttrList( ni ); + /* Prevent ntfs_attr_recorm_rm from freeing attribute list. */ + ni->attr_list = NULL; + NInoClearAttrList(ni); + /* Remove $ATTRIBUTE_LIST record. */ + ntfs_attr_reinit_search_ctx(ctx); + if (!ntfs_attr_lookup(AT_ATTRIBUTE_LIST, NULL, 0, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + if (ntfs_attr_record_rm(ctx)) + ntfs_log_perror("Rollback failed to remove attrlist"); + } else + ntfs_log_perror("Rollback failed to find attrlist"); + /* Setup back in-memory runlist. */ + ni->attr_list = al; + ni->attr_list_size = al_len; + NInoSetAttrList(ni); rollback: - /* - * Scan attribute list for attributes that placed not in the base MFT - * record and move them to it. - */ - ntfs_attr_reinit_search_ctx( ctx ); - ale = ( ATTR_LIST_ENTRY* )al; - while ( ( u8* )ale < al + al_len ) - { - if ( MREF_LE( ale->mft_reference ) != ni->mft_no ) - { - if ( !ntfs_attr_lookup( ale->type, ale->name, - ale->name_length, - CASE_SENSITIVE, - sle64_to_cpu( ale->lowest_vcn ), - NULL, 0, ctx ) ) - { - if ( ntfs_attr_record_move_to( ctx, ni ) ) - ntfs_log_perror( "Rollback failed to " - "move attribute" ); - } - else - ntfs_log_perror( "Rollback failed to find attr" ); - ntfs_attr_reinit_search_ctx( ctx ); - } - ale = ( ATTR_LIST_ENTRY* )( ( u8* )ale + le16_to_cpu( ale->length ) ); - } - /* Remove in-memory attribute list. */ - ni->attr_list = NULL; - ni->attr_list_size = 0; - NInoClearAttrList( ni ); - NInoAttrListClearDirty( ni ); + /* + * Scan attribute list for attributes that placed not in the base MFT + * record and move them to it. + */ + ntfs_attr_reinit_search_ctx(ctx); + ale = (ATTR_LIST_ENTRY*)al; + while ((u8*)ale < al + al_len) { + if (MREF_LE(ale->mft_reference) != ni->mft_no) { + if (!ntfs_attr_lookup(ale->type, ale->name, + ale->name_length, + CASE_SENSITIVE, + sle64_to_cpu(ale->lowest_vcn), + NULL, 0, ctx)) { + if (ntfs_attr_record_move_to(ctx, ni)) + ntfs_log_perror("Rollback failed to " + "move attribute"); + } else + ntfs_log_perror("Rollback failed to find attr"); + ntfs_attr_reinit_search_ctx(ctx); + } + ale = (ATTR_LIST_ENTRY*)((u8*)ale + le16_to_cpu(ale->length)); + } + /* Remove in-memory attribute list. */ + ni->attr_list = NULL; + ni->attr_list_size = 0; + NInoClearAttrList(ni); + NInoAttrListClearDirty(ni); put_err_out: - ntfs_attr_put_search_ctx( ctx ); + ntfs_attr_put_search_ctx(ctx); err_out: - free( al ); - errno = err; - return -1; + free(al); + errno = err; + return -1; } /** * ntfs_inode_free_space - free space in the MFT record of an inode - * @ni: ntfs inode in which MFT record needs more free space - * @size: amount of space needed to free + * @ni: ntfs inode in which MFT record needs more free space + * @size: amount of space needed to free * * Return 0 on success or -1 on error with errno set to the error code. */ -int ntfs_inode_free_space( ntfs_inode *ni, int size ) +int ntfs_inode_free_space(ntfs_inode *ni, int size) { - ntfs_attr_search_ctx *ctx; - int freed; + ntfs_attr_search_ctx *ctx; + int freed; - if ( !ni || size < 0 ) - { - errno = EINVAL; - ntfs_log_perror( "%s: ni=%p size=%d", __FUNCTION__, ni, size ); - return -1; - } + if (!ni || size < 0) { + errno = EINVAL; + ntfs_log_perror("%s: ni=%p size=%d", __FUNCTION__, ni, size); + return -1; + } - ntfs_log_trace( "Entering for inode %lld, size %d\n", - ( unsigned long long )ni->mft_no, size ); + ntfs_log_trace("Entering for inode %lld, size %d\n", + (unsigned long long)ni->mft_no, size); - freed = ( le32_to_cpu( ni->mrec->bytes_allocated ) - - le32_to_cpu( ni->mrec->bytes_in_use ) ); + freed = (le32_to_cpu(ni->mrec->bytes_allocated) - + le32_to_cpu(ni->mrec->bytes_in_use)); - if ( size <= freed ) - return 0; + if (size <= freed) + return 0; - ctx = ntfs_attr_get_search_ctx( ni, NULL ); - if ( !ctx ) - return -1; - /* - * $STANDARD_INFORMATION and $ATTRIBUTE_LIST must stay in the base MFT - * record, so position search context on the first attribute after them. - */ - if ( ntfs_attr_position( AT_FILE_NAME, ctx ) ) - goto put_err_out; + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return -1; + /* + * $STANDARD_INFORMATION and $ATTRIBUTE_LIST must stay in the base MFT + * record, so position search context on the first attribute after them. + */ + if (ntfs_attr_position(AT_FILE_NAME, ctx)) + goto put_err_out; - while ( 1 ) - { - int record_size; - /* - * Check whether attribute is from different MFT record. If so, - * find next, because we don't need such. - */ - while ( ctx->ntfs_ino->mft_no != ni->mft_no ) - { -retry: - if ( ntfs_attr_position( AT_UNUSED, ctx ) ) - goto put_err_out; - } + while (1) { + int record_size; + /* + * Check whether attribute is from different MFT record. If so, + * find next, because we don't need such. + */ + while (ctx->ntfs_ino->mft_no != ni->mft_no) { +retry: + if (ntfs_attr_position(AT_UNUSED, ctx)) + goto put_err_out; + } - if ( ntfs_inode_base( ctx->ntfs_ino )->mft_no == FILE_MFT && - ctx->attr->type == AT_DATA ) - goto retry; + if (ntfs_inode_base(ctx->ntfs_ino)->mft_no == FILE_MFT && + ctx->attr->type == AT_DATA) + goto retry; - if ( ctx->attr->type == AT_INDEX_ROOT ) - goto retry; + if (ctx->attr->type == AT_INDEX_ROOT) + goto retry; - record_size = le32_to_cpu( ctx->attr->length ); + record_size = le32_to_cpu(ctx->attr->length); - if ( ntfs_attr_record_move_away( ctx, 0 ) ) - { - ntfs_log_perror( "Failed to move out attribute #2" ); - break; - } - freed += record_size; + if (ntfs_attr_record_move_away(ctx, 0)) { + ntfs_log_perror("Failed to move out attribute #2"); + break; + } + freed += record_size; - /* Check whether we are done. */ - if ( size <= freed ) - { - ntfs_attr_put_search_ctx( ctx ); - return 0; - } - /* - * Reposition to first attribute after $STANDARD_INFORMATION - * and $ATTRIBUTE_LIST instead of simply skipping this attribute - * because in the case when we have got only in-memory attribute - * list then ntfs_attr_lookup will fail when it tries to find - * $ATTRIBUTE_LIST. - */ - ntfs_attr_reinit_search_ctx( ctx ); - if ( ntfs_attr_position( AT_FILE_NAME, ctx ) ) - break; - } + /* Check whether we are done. */ + if (size <= freed) { + ntfs_attr_put_search_ctx(ctx); + return 0; + } + /* + * Reposition to first attribute after $STANDARD_INFORMATION + * and $ATTRIBUTE_LIST instead of simply skipping this attribute + * because in the case when we have got only in-memory attribute + * list then ntfs_attr_lookup will fail when it tries to find + * $ATTRIBUTE_LIST. + */ + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_position(AT_FILE_NAME, ctx)) + break; + } put_err_out: - ntfs_attr_put_search_ctx( ctx ); - if ( errno == ENOSPC ) - ntfs_log_trace( "No attributes left that could be moved out.\n" ); - return -1; + ntfs_attr_put_search_ctx(ctx); + if (errno == ENOSPC) + ntfs_log_trace("No attributes left that could be moved out.\n"); + return -1; } /** * ntfs_inode_update_times - update selected time fields for ntfs inode - * @ni: ntfs inode for which update time fields - * @mask: select which time fields should be updated + * @ni: ntfs inode for which update time fields + * @mask: select which time fields should be updated * * This function updates time fields to current time. Fields to update are * selected using @mask (see enum @ntfs_time_update_flags for posssible values). */ -void ntfs_inode_update_times( ntfs_inode *ni, ntfs_time_update_flags mask ) +void ntfs_inode_update_times(ntfs_inode *ni, ntfs_time_update_flags mask) { - ntfs_time now; + ntfs_time now; - if ( !ni ) - { - ntfs_log_error( "%s(): Invalid arguments.\n", __FUNCTION__ ); - return; - } + if (!ni) { + ntfs_log_error("%s(): Invalid arguments.\n", __FUNCTION__); + return; + } - if ( ( ni->mft_no < FILE_first_user && ni->mft_no != FILE_root ) || - NVolReadOnly( ni->vol ) || !mask ) - return; + if ((ni->mft_no < FILE_first_user && ni->mft_no != FILE_root) || + NVolReadOnly(ni->vol) || !mask) + return; - now = ntfs_current_time(); - if ( mask & NTFS_UPDATE_ATIME ) - ni->last_access_time = now; - if ( mask & NTFS_UPDATE_MTIME ) - ni->last_data_change_time = now; - if ( mask & NTFS_UPDATE_CTIME ) - ni->last_mft_change_time = now; - - NInoFileNameSetDirty( ni ); - NInoSetDirty( ni ); + now = ntfs_current_time(); + if (mask & NTFS_UPDATE_ATIME) + ni->last_access_time = now; + if (mask & NTFS_UPDATE_MTIME) + ni->last_data_change_time = now; + if (mask & NTFS_UPDATE_CTIME) + ni->last_mft_change_time = now; + + NInoFileNameSetDirty(ni); + NInoSetDirty(ni); } /** * ntfs_inode_badclus_bad - check for $Badclus:$Bad data attribute - * @mft_no: mft record number where @attr is present - * @attr: attribute record used to check for the $Bad attribute + * @mft_no: mft record number where @attr is present + * @attr: attribute record used to check for the $Bad attribute * * Check if the mft record given by @mft_no and @attr contains the bad sector * list. Please note that mft record numbers describing $Badclus extent inodes * will not match the current $Badclus:$Bad check. - * + * * On success return 1 if the file is $Badclus:$Bad, otherwise return 0. * On error return -1 with errno set to the error code. */ -int ntfs_inode_badclus_bad( u64 mft_no, ATTR_RECORD *attr ) +int ntfs_inode_badclus_bad(u64 mft_no, ATTR_RECORD *attr) { - int len, ret = 0; - ntfschar *ustr; + int len, ret = 0; + ntfschar *ustr; - if ( !attr ) - { - ntfs_log_error( "Invalid argument.\n" ); - errno = EINVAL; - return -1; - } + if (!attr) { + ntfs_log_error("Invalid argument.\n"); + errno = EINVAL; + return -1; + } + + if (mft_no != FILE_BadClus) + return 0; - if ( mft_no != FILE_BadClus ) - return 0; + if (attr->type != AT_DATA) + return 0; - if ( attr->type != AT_DATA ) - return 0; + if ((ustr = ntfs_str2ucs("$Bad", &len)) == NULL) { + ntfs_log_perror("Couldn't convert '$Bad' to Unicode"); + return -1; + } - if ( ( ustr = ntfs_str2ucs( "$Bad", &len ) ) == NULL ) - { - ntfs_log_perror( "Couldn't convert '$Bad' to Unicode" ); - return -1; - } + if (ustr && ntfs_names_are_equal(ustr, len, + (ntfschar *)((u8 *)attr + le16_to_cpu(attr->name_offset)), + attr->name_length, 0, NULL, 0)) + ret = 1; - if ( ustr && ntfs_names_are_equal( ustr, len, - ( ntfschar * )( ( u8 * )attr + le16_to_cpu( attr->name_offset ) ), - attr->name_length, 0, NULL, 0 ) ) - ret = 1; + ntfs_ucsfree(ustr); - ntfs_ucsfree( ustr ); - - return ret; + return ret; } -#ifdef HAVE_SETXATTR /* extended attributes interface required */ +#ifdef HAVE_SETXATTR /* extended attributes interface required */ /* - * Get high precision NTFS times + * Get high precision NTFS times * - * They are returned in following order : create, update, access, change - * provided they fit in requested size. + * They are returned in following order : create, update, access, change + * provided they fit in requested size. * - * Returns the modified size if successfull (or 32 if buffer size is null) - * -errno if failed + * Returns the modified size if successfull (or 32 if buffer size is null) + * -errno if failed */ -int ntfs_inode_get_times( ntfs_inode *ni, char *value, size_t size ) +int ntfs_inode_get_times(ntfs_inode *ni, char *value, size_t size) { - ntfs_attr_search_ctx *ctx; - STANDARD_INFORMATION *std_info; - u64 *times; - int ret; + ntfs_attr_search_ctx *ctx; + STANDARD_INFORMATION *std_info; + u64 *times; + int ret; - ret = 0; - ctx = ntfs_attr_get_search_ctx( ni, NULL ); - if ( ctx ) - { - if ( ntfs_attr_lookup( AT_STANDARD_INFORMATION, AT_UNNAMED, - 0, CASE_SENSITIVE, 0, NULL, 0, ctx ) ) - { - ntfs_log_perror( "Failed to get standard info (inode %lld)", - ( long long )ni->mft_no ); - } - else - { - std_info = ( STANDARD_INFORMATION * )( ( u8 * )ctx->attr + - le16_to_cpu( ctx->attr->value_offset ) ); - if ( value && ( size >= 8 ) ) - { - times = ( u64* )value; - times[0] = le64_to_cpu( std_info->creation_time ); - ret = 8; - if ( size >= 16 ) - { - times[1] = le64_to_cpu( std_info->last_data_change_time ); - ret = 16; - } - if ( size >= 24 ) - { - times[2] = le64_to_cpu( std_info->last_access_time ); - ret = 24; - } - if ( size >= 32 ) - { - times[3] = le64_to_cpu( std_info->last_mft_change_time ); - ret = 32; - } - } - else if ( !size ) - ret = 32; - else - ret = -ERANGE; - } - ntfs_attr_put_search_ctx( ctx ); - } - return ( ret ? ret : -errno ); + ret = 0; + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (ctx) { + if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED, + 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { + ntfs_log_perror("Failed to get standard info (inode %lld)", + (long long)ni->mft_no); + } else { + std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + if (value && (size >= 8)) { + times = (u64*)value; + times[0] = le64_to_cpu(std_info->creation_time); + ret = 8; + if (size >= 16) { + times[1] = le64_to_cpu(std_info->last_data_change_time); + ret = 16; + } + if (size >= 24) { + times[2] = le64_to_cpu(std_info->last_access_time); + ret = 24; + } + if (size >= 32) { + times[3] = le64_to_cpu(std_info->last_mft_change_time); + ret = 32; + } + } else + if (!size) + ret = 32; + else + ret = -ERANGE; + } + ntfs_attr_put_search_ctx(ctx); + } + return (ret ? ret : -errno); } /* - * Set high precision NTFS times + * Set high precision NTFS times * - * They are expected in this order : create, update, access - * provided they are present in input. The change time is set to - * current time. + * They are expected in this order : create, update, access + * provided they are present in input. The change time is set to + * current time. * - * The times are inserted directly in the standard_information and - * file names attributes to avoid manipulating low precision times + * The times are inserted directly in the standard_information and + * file names attributes to avoid manipulating low precision times * - * Returns 0 if success - * -1 if there were an error (described by errno) + * Returns 0 if success + * -1 if there were an error (described by errno) */ -int ntfs_inode_set_times( ntfs_inode *ni, const char *value, size_t size, - int flags ) +int ntfs_inode_set_times(ntfs_inode *ni, const char *value, size_t size, + int flags) { - ntfs_attr_search_ctx *ctx; - STANDARD_INFORMATION *std_info; - FILE_NAME_ATTR *fn; - const u64 *times; - ntfs_time now; - int cnt; - int ret; + ntfs_attr_search_ctx *ctx; + STANDARD_INFORMATION *std_info; + FILE_NAME_ATTR *fn; + const u64 *times; + ntfs_time now; + int cnt; + int ret; - ret = -1; - if ( ( size >= 8 ) && !( flags & XATTR_CREATE ) ) - { - times = ( const u64* )value; - now = ntfs_current_time(); - /* update the standard information attribute */ - ctx = ntfs_attr_get_search_ctx( ni, NULL ); - if ( ctx ) - { - if ( ntfs_attr_lookup( AT_STANDARD_INFORMATION, - AT_UNNAMED, 0, CASE_SENSITIVE, - 0, NULL, 0, ctx ) ) - { - ntfs_log_perror( "Failed to get standard info (inode %lld)", - ( long long )ni->mft_no ); - } - else - { - std_info = ( STANDARD_INFORMATION * )( ( u8 * )ctx->attr + - le16_to_cpu( ctx->attr->value_offset ) ); - /* - * Mark times set to avoid overwriting - * them when the inode is closed. - * The inode structure must also be updated - * (with loss of precision) because of cacheing. - * TODO : use NTFS precision in inode, and - * return sub-second times in getattr() - */ - set_nino_flag( ni, TimesSet ); - std_info->creation_time = cpu_to_le64( times[0] ); - ni->creation_time - = std_info->creation_time; - if ( size >= 16 ) - { - std_info->last_data_change_time = cpu_to_le64( times[1] ); - ni->last_data_change_time - = std_info->last_data_change_time; - } - if ( size >= 24 ) - { - std_info->last_access_time = cpu_to_le64( times[2] ); - ni->last_access_time - = std_info->last_access_time; - } - std_info->last_mft_change_time = now; - ni->last_mft_change_time = now; - ntfs_inode_mark_dirty( ctx->ntfs_ino ); - NInoFileNameSetDirty( ni ); + ret = -1; + if ((size >= 8) && !(flags & XATTR_CREATE)) { + times = (const u64*)value; + now = ntfs_current_time(); + /* update the standard information attribute */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (ctx) { + if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, + AT_UNNAMED, 0, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + ntfs_log_perror("Failed to get standard info (inode %lld)", + (long long)ni->mft_no); + } else { + std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + /* + * Mark times set to avoid overwriting + * them when the inode is closed. + * The inode structure must also be updated + * (with loss of precision) because of cacheing. + * TODO : use NTFS precision in inode, and + * return sub-second times in getattr() + */ + set_nino_flag(ni, TimesSet); + std_info->creation_time = cpu_to_le64(times[0]); + ni->creation_time + = std_info->creation_time; + if (size >= 16) { + std_info->last_data_change_time = cpu_to_le64(times[1]); + ni->last_data_change_time + = std_info->last_data_change_time; + } + if (size >= 24) { + std_info->last_access_time = cpu_to_le64(times[2]); + ni->last_access_time + = std_info->last_access_time; + } + std_info->last_mft_change_time = now; + ni->last_mft_change_time = now; + ntfs_inode_mark_dirty(ctx->ntfs_ino); + NInoFileNameSetDirty(ni); - /* update the file names attributes */ - ntfs_attr_reinit_search_ctx( ctx ); - cnt = 0; - while ( !ntfs_attr_lookup( AT_FILE_NAME, - AT_UNNAMED, 0, CASE_SENSITIVE, - 0, NULL, 0, ctx ) ) - { - fn = ( FILE_NAME_ATTR* )( ( u8 * )ctx->attr + - le16_to_cpu( ctx->attr->value_offset ) ); - fn->creation_time - = cpu_to_le64( times[0] ); - if ( size >= 16 ) - fn->last_data_change_time - = cpu_to_le64( times[1] ); - if ( size >= 24 ) - fn->last_access_time - = cpu_to_le64( times[2] ); - fn->last_mft_change_time = now; - cnt++; - } - if ( cnt ) - ret = 0; - else - { - ntfs_log_perror( "Failed to get file names (inode %lld)", - ( long long )ni->mft_no ); - } - } - ntfs_attr_put_search_ctx( ctx ); - } - } - else if ( size < 8 ) - errno = ERANGE; - else - errno = EEXIST; - return ( ret ); + /* update the file names attributes */ + ntfs_attr_reinit_search_ctx(ctx); + cnt = 0; + while (!ntfs_attr_lookup(AT_FILE_NAME, + AT_UNNAMED, 0, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + fn = (FILE_NAME_ATTR*)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + fn->creation_time + = cpu_to_le64(times[0]); + if (size >= 16) + fn->last_data_change_time + = cpu_to_le64(times[1]); + if (size >= 24) + fn->last_access_time + = cpu_to_le64(times[2]); + fn->last_mft_change_time = now; + cnt++; + } + if (cnt) + ret = 0; + else { + ntfs_log_perror("Failed to get file names (inode %lld)", + (long long)ni->mft_no); + } + } + ntfs_attr_put_search_ctx(ctx); + } + } else + if (size < 8) + errno = ERANGE; + else + errno = EEXIST; + return (ret); } #endif /* HAVE_SETXATTR */ diff --git a/source/libntfs/inode.h b/source/libntfs/inode.h index 18a3771b..5a6f7da6 100644 --- a/source/libntfs/inode.h +++ b/source/libntfs/inode.h @@ -40,63 +40,62 @@ typedef struct _ntfs_inode ntfs_inode; * Defined bits for the state field in the ntfs_inode structure. * (f) = files only, (d) = directories only */ -typedef enum -{ - NI_Dirty, /* 1: Mft record needs to be written to disk. */ +typedef enum { + NI_Dirty, /* 1: Mft record needs to be written to disk. */ - /* The NI_AttrList* tests only make sense for base inodes. */ - NI_AttrList, /* 1: Mft record contains an attribute list. */ - NI_AttrListDirty, /* 1: Attribute list needs to be written to the - mft record and then to disk. */ - NI_FileNameDirty, /* 1: FILE_NAME attributes need to be updated - in the index. */ - NI_v3_Extensions, /* 1: JPA v3.x extensions present. */ - NI_TimesSet, /* 1: Use times which were set */ - NI_KnownSize, /* 1: Set if sizes are meaningful */ + /* The NI_AttrList* tests only make sense for base inodes. */ + NI_AttrList, /* 1: Mft record contains an attribute list. */ + NI_AttrListDirty, /* 1: Attribute list needs to be written to the + mft record and then to disk. */ + NI_FileNameDirty, /* 1: FILE_NAME attributes need to be updated + in the index. */ + NI_v3_Extensions, /* 1: JPA v3.x extensions present. */ + NI_TimesSet, /* 1: Use times which were set */ + NI_KnownSize, /* 1: Set if sizes are meaningful */ } ntfs_inode_state_bits; -#define test_nino_flag(ni, flag) test_bit(NI_##flag, (ni)->state) -#define set_nino_flag(ni, flag) set_bit(NI_##flag, (ni)->state) -#define clear_nino_flag(ni, flag) clear_bit(NI_##flag, (ni)->state) +#define test_nino_flag(ni, flag) test_bit(NI_##flag, (ni)->state) +#define set_nino_flag(ni, flag) set_bit(NI_##flag, (ni)->state) +#define clear_nino_flag(ni, flag) clear_bit(NI_##flag, (ni)->state) -#define test_and_set_nino_flag(ni, flag) \ - test_and_set_bit(NI_##flag, (ni)->state) -#define test_and_clear_nino_flag(ni, flag) \ - test_and_clear_bit(NI_##flag, (ni)->state) +#define test_and_set_nino_flag(ni, flag) \ + test_and_set_bit(NI_##flag, (ni)->state) +#define test_and_clear_nino_flag(ni, flag) \ + test_and_clear_bit(NI_##flag, (ni)->state) -#define NInoDirty(ni) test_nino_flag(ni, Dirty) -#define NInoSetDirty(ni) set_nino_flag(ni, Dirty) -#define NInoClearDirty(ni) clear_nino_flag(ni, Dirty) -#define NInoTestAndSetDirty(ni) test_and_set_nino_flag(ni, Dirty) -#define NInoTestAndClearDirty(ni) test_and_clear_nino_flag(ni, Dirty) +#define NInoDirty(ni) test_nino_flag(ni, Dirty) +#define NInoSetDirty(ni) set_nino_flag(ni, Dirty) +#define NInoClearDirty(ni) clear_nino_flag(ni, Dirty) +#define NInoTestAndSetDirty(ni) test_and_set_nino_flag(ni, Dirty) +#define NInoTestAndClearDirty(ni) test_and_clear_nino_flag(ni, Dirty) -#define NInoAttrList(ni) test_nino_flag(ni, AttrList) -#define NInoSetAttrList(ni) set_nino_flag(ni, AttrList) -#define NInoClearAttrList(ni) clear_nino_flag(ni, AttrList) +#define NInoAttrList(ni) test_nino_flag(ni, AttrList) +#define NInoSetAttrList(ni) set_nino_flag(ni, AttrList) +#define NInoClearAttrList(ni) clear_nino_flag(ni, AttrList) -#define test_nino_al_flag(ni, flag) test_nino_flag(ni, AttrList##flag) -#define set_nino_al_flag(ni, flag) set_nino_flag(ni, AttrList##flag) -#define clear_nino_al_flag(ni, flag) clear_nino_flag(ni, AttrList##flag) +#define test_nino_al_flag(ni, flag) test_nino_flag(ni, AttrList##flag) +#define set_nino_al_flag(ni, flag) set_nino_flag(ni, AttrList##flag) +#define clear_nino_al_flag(ni, flag) clear_nino_flag(ni, AttrList##flag) -#define test_and_set_nino_al_flag(ni, flag) \ - test_and_set_nino_flag(ni, AttrList##flag) -#define test_and_clear_nino_al_flag(ni, flag) \ - test_and_clear_nino_flag(ni, AttrList##flag) +#define test_and_set_nino_al_flag(ni, flag) \ + test_and_set_nino_flag(ni, AttrList##flag) +#define test_and_clear_nino_al_flag(ni, flag) \ + test_and_clear_nino_flag(ni, AttrList##flag) -#define NInoAttrListDirty(ni) test_nino_al_flag(ni, Dirty) -#define NInoAttrListSetDirty(ni) set_nino_al_flag(ni, Dirty) -#define NInoAttrListClearDirty(ni) clear_nino_al_flag(ni, Dirty) -#define NInoAttrListTestAndSetDirty(ni) test_and_set_nino_al_flag(ni, Dirty) +#define NInoAttrListDirty(ni) test_nino_al_flag(ni, Dirty) +#define NInoAttrListSetDirty(ni) set_nino_al_flag(ni, Dirty) +#define NInoAttrListClearDirty(ni) clear_nino_al_flag(ni, Dirty) +#define NInoAttrListTestAndSetDirty(ni) test_and_set_nino_al_flag(ni, Dirty) #define NInoAttrListTestAndClearDirty(ni) test_and_clear_nino_al_flag(ni, Dirty) #define NInoFileNameDirty(ni) test_nino_flag(ni, FileNameDirty) #define NInoFileNameSetDirty(ni) set_nino_flag(ni, FileNameDirty) #define NInoFileNameClearDirty(ni) clear_nino_flag(ni, FileNameDirty) -#define NInoFileNameTestAndSetDirty(ni) \ - test_and_set_nino_flag(ni, FileNameDirty) -#define NInoFileNameTestAndClearDirty(ni) \ - test_and_clear_nino_flag(ni, FileNameDirty) +#define NInoFileNameTestAndSetDirty(ni) \ + test_and_set_nino_flag(ni, FileNameDirty) +#define NInoFileNameTestAndClearDirty(ni) \ + test_and_clear_nino_flag(ni, FileNameDirty) /** * struct _ntfs_inode - The NTFS in-memory inode structure. @@ -104,123 +103,120 @@ typedef enum * It is just used as an extension to the fields already provided in the VFS * inode. */ -struct _ntfs_inode -{ - u64 mft_no; /* Inode / mft record number. */ - MFT_RECORD *mrec; /* The actual mft record of the inode. */ - ntfs_volume *vol; /* Pointer to the ntfs volume of this inode. */ - unsigned long state; /* NTFS specific flags describing this inode. - See ntfs_inode_state_bits above. */ - FILE_ATTR_FLAGS flags; /* Flags describing the file. - (Copy from STANDARD_INFORMATION) */ - /* - * Attribute list support (for use by the attribute lookup functions). - * Setup during ntfs_open_inode() for all inodes with attribute lists. - * Only valid if NI_AttrList is set in state. - */ - u32 attr_list_size; /* Length of attribute list value in bytes. */ - u8 *attr_list; /* Attribute list value itself. */ - /* Below fields are always valid. */ - s32 nr_extents; /* For a base mft record, the number of - attached extent inodes (0 if none), for - extent records this is -1. */ - union /* This union is only used if nr_extents != 0. */ - { - ntfs_inode **extent_nis;/* For nr_extents > 0, array of the - ntfs inodes of the extent mft - records belonging to this base - inode which have been loaded. */ - ntfs_inode *base_ni; /* For nr_extents == -1, the ntfs - inode of the base mft record. */ - }; +struct _ntfs_inode { + u64 mft_no; /* Inode / mft record number. */ + MFT_RECORD *mrec; /* The actual mft record of the inode. */ + ntfs_volume *vol; /* Pointer to the ntfs volume of this inode. */ + unsigned long state; /* NTFS specific flags describing this inode. + See ntfs_inode_state_bits above. */ + FILE_ATTR_FLAGS flags; /* Flags describing the file. + (Copy from STANDARD_INFORMATION) */ + /* + * Attribute list support (for use by the attribute lookup functions). + * Setup during ntfs_open_inode() for all inodes with attribute lists. + * Only valid if NI_AttrList is set in state. + */ + u32 attr_list_size; /* Length of attribute list value in bytes. */ + u8 *attr_list; /* Attribute list value itself. */ + /* Below fields are always valid. */ + s32 nr_extents; /* For a base mft record, the number of + attached extent inodes (0 if none), for + extent records this is -1. */ + union { /* This union is only used if nr_extents != 0. */ + ntfs_inode **extent_nis;/* For nr_extents > 0, array of the + ntfs inodes of the extent mft + records belonging to this base + inode which have been loaded. */ + ntfs_inode *base_ni; /* For nr_extents == -1, the ntfs + inode of the base mft record. */ + }; - /* Below fields are valid only for base inode. */ + /* Below fields are valid only for base inode. */ - /* - * These two fields are used to sync filename index and guaranteed to be - * correct, however value in index itself maybe wrong (windows itself - * do not update them properly). - * For directories, they hold the index size, provided the - * flag KnownSize is set. - */ - s64 data_size; /* Data size of unnamed DATA attribute - (or INDEX_ROOT for directories) */ - s64 allocated_size; /* Allocated size stored in the filename - index. (NOTE: Equal to allocated size of - the unnamed data attribute for normal or - encrypted files and to compressed size - of the unnamed data attribute for sparse or - compressed files.) */ + /* + * These two fields are used to sync filename index and guaranteed to be + * correct, however value in index itself maybe wrong (windows itself + * do not update them properly). + * For directories, they hold the index size, provided the + * flag KnownSize is set. + */ + s64 data_size; /* Data size of unnamed DATA attribute + (or INDEX_ROOT for directories) */ + s64 allocated_size; /* Allocated size stored in the filename + index. (NOTE: Equal to allocated size of + the unnamed data attribute for normal or + encrypted files and to compressed size + of the unnamed data attribute for sparse or + compressed files.) */ - /* - * These four fields are copy of relevant fields from - * STANDARD_INFORMATION attribute and used to sync it and FILE_NAME - * attribute in the index. - */ - ntfs_time creation_time; - ntfs_time last_data_change_time; - ntfs_time last_mft_change_time; - ntfs_time last_access_time; - /* NTFS 3.x extensions added by JPA */ - /* only if NI_v3_Extensions is set in state */ - le32 owner_id; - le32 security_id; - le64 quota_charged; - le64 usn; + /* + * These four fields are copy of relevant fields from + * STANDARD_INFORMATION attribute and used to sync it and FILE_NAME + * attribute in the index. + */ + ntfs_time creation_time; + ntfs_time last_data_change_time; + ntfs_time last_mft_change_time; + ntfs_time last_access_time; + /* NTFS 3.x extensions added by JPA */ + /* only if NI_v3_Extensions is set in state */ + le32 owner_id; + le32 security_id; + le64 quota_charged; + le64 usn; }; -typedef enum -{ - NTFS_UPDATE_ATIME = 1 << 0, - NTFS_UPDATE_MTIME = 1 << 1, - NTFS_UPDATE_CTIME = 1 << 2, +typedef enum { + NTFS_UPDATE_ATIME = 1 << 0, + NTFS_UPDATE_MTIME = 1 << 1, + NTFS_UPDATE_CTIME = 1 << 2, } ntfs_time_update_flags; #define NTFS_UPDATE_MCTIME (NTFS_UPDATE_MTIME | NTFS_UPDATE_CTIME) #define NTFS_UPDATE_AMCTIME (NTFS_UPDATE_ATIME | NTFS_UPDATE_MCTIME) -extern ntfs_inode *ntfs_inode_base( ntfs_inode *ni ); +extern ntfs_inode *ntfs_inode_base(ntfs_inode *ni); -extern ntfs_inode *ntfs_inode_allocate( ntfs_volume *vol ); +extern ntfs_inode *ntfs_inode_allocate(ntfs_volume *vol); -extern ntfs_inode *ntfs_inode_open( ntfs_volume *vol, const MFT_REF mref ); +extern ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref); -extern int ntfs_inode_close( ntfs_inode *ni ); -extern int ntfs_inode_close_in_dir( ntfs_inode *ni, ntfs_inode *dir_ni ); +extern int ntfs_inode_close(ntfs_inode *ni); +extern int ntfs_inode_close_in_dir(ntfs_inode *ni, ntfs_inode *dir_ni); #if CACHE_NIDATA_SIZE struct CACHED_GENERIC; -extern int ntfs_inode_real_close( ntfs_inode *ni ); -extern void ntfs_inode_invalidate( ntfs_volume *vol, const MFT_REF mref ); -extern void ntfs_inode_nidata_free( const struct CACHED_GENERIC *cached ); -extern int ntfs_inode_nidata_hash( const struct CACHED_GENERIC *item ); +extern int ntfs_inode_real_close(ntfs_inode *ni); +extern void ntfs_inode_invalidate(ntfs_volume *vol, const MFT_REF mref); +extern void ntfs_inode_nidata_free(const struct CACHED_GENERIC *cached); +extern int ntfs_inode_nidata_hash(const struct CACHED_GENERIC *item); #endif -extern ntfs_inode *ntfs_extent_inode_open( ntfs_inode *base_ni, - const MFT_REF mref ); +extern ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni, + const MFT_REF mref); -extern int ntfs_inode_attach_all_extents( ntfs_inode *ni ); +extern int ntfs_inode_attach_all_extents(ntfs_inode *ni); -extern void ntfs_inode_mark_dirty( ntfs_inode *ni ); +extern void ntfs_inode_mark_dirty(ntfs_inode *ni); -extern void ntfs_inode_update_times( ntfs_inode *ni, ntfs_time_update_flags mask ); +extern void ntfs_inode_update_times(ntfs_inode *ni, ntfs_time_update_flags mask); -extern int ntfs_inode_sync( ntfs_inode *ni ); +extern int ntfs_inode_sync(ntfs_inode *ni); -extern int ntfs_inode_add_attrlist( ntfs_inode *ni ); +extern int ntfs_inode_add_attrlist(ntfs_inode *ni); -extern int ntfs_inode_free_space( ntfs_inode *ni, int size ); +extern int ntfs_inode_free_space(ntfs_inode *ni, int size); -extern int ntfs_inode_badclus_bad( u64 mft_no, ATTR_RECORD *a ); +extern int ntfs_inode_badclus_bad(u64 mft_no, ATTR_RECORD *a); -extern int ntfs_inode_get_times( ntfs_inode *ni, char *value, size_t size ); +extern int ntfs_inode_get_times(ntfs_inode *ni, char *value, size_t size); -extern int ntfs_inode_set_times( ntfs_inode *ni, const char *value, - size_t size, int flags ); +extern int ntfs_inode_set_times(ntfs_inode *ni, const char *value, + size_t size, int flags); /* debugging */ #define debug_double_inode(num, type) diff --git a/source/libntfs/layout.h b/source/libntfs/layout.h index b8e1747e..8670557e 100644 --- a/source/libntfs/layout.h +++ b/source/libntfs/layout.h @@ -29,72 +29,70 @@ #include "support.h" /* The NTFS oem_id */ -#define magicNTFS const_cpu_to_le64(0x202020205346544e) /* "NTFS " */ -#define NTFS_SB_MAGIC 0x5346544e /* 'NTFS' */ +#define magicNTFS const_cpu_to_le64(0x202020205346544e) /* "NTFS " */ +#define NTFS_SB_MAGIC 0x5346544e /* 'NTFS' */ /* * Location of bootsector on partition: - * The standard NTFS_BOOT_SECTOR is on sector 0 of the partition. - * On NT4 and above there is one backup copy of the boot sector to - * be found on the last sector of the partition (not normally accessible - * from within Windows as the bootsector contained number of sectors - * value is one less than the actual value!). - * On versions of NT 3.51 and earlier, the backup copy was located at - * number of sectors/2 (integer divide), i.e. in the middle of the volume. + * The standard NTFS_BOOT_SECTOR is on sector 0 of the partition. + * On NT4 and above there is one backup copy of the boot sector to + * be found on the last sector of the partition (not normally accessible + * from within Windows as the bootsector contained number of sectors + * value is one less than the actual value!). + * On versions of NT 3.51 and earlier, the backup copy was located at + * number of sectors/2 (integer divide), i.e. in the middle of the volume. */ /** * struct BIOS_PARAMETER_BLOCK - BIOS parameter block (bpb) structure. */ -typedef struct -{ - u16 bytes_per_sector; /* Size of a sector in bytes. */ - u8 sectors_per_cluster; /* Size of a cluster in sectors. */ - u16 reserved_sectors; /* zero */ - u8 fats; /* zero */ - u16 root_entries; /* zero */ - u16 sectors; /* zero */ - u8 media_type; /* 0xf8 = hard disk */ - u16 sectors_per_fat; /* zero */ - /*0x0d*/u16 sectors_per_track; /* Required to boot Windows. */ - /*0x0f*/u16 heads; /* Required to boot Windows. */ - /*0x11*/u32 hidden_sectors; /* Offset to the start of the partition - relative to the disk in sectors. - Required to boot Windows. */ - /*0x15*/u32 large_sectors; /* zero */ - /* sizeof() = 25 (0x19) bytes */ -} __attribute__( ( __packed__ ) ) BIOS_PARAMETER_BLOCK; +typedef struct { + u16 bytes_per_sector; /* Size of a sector in bytes. */ + u8 sectors_per_cluster; /* Size of a cluster in sectors. */ + u16 reserved_sectors; /* zero */ + u8 fats; /* zero */ + u16 root_entries; /* zero */ + u16 sectors; /* zero */ + u8 media_type; /* 0xf8 = hard disk */ + u16 sectors_per_fat; /* zero */ +/*0x0d*/u16 sectors_per_track; /* Required to boot Windows. */ +/*0x0f*/u16 heads; /* Required to boot Windows. */ +/*0x11*/u32 hidden_sectors; /* Offset to the start of the partition + relative to the disk in sectors. + Required to boot Windows. */ +/*0x15*/u32 large_sectors; /* zero */ +/* sizeof() = 25 (0x19) bytes */ +} __attribute__((__packed__)) BIOS_PARAMETER_BLOCK; /** * struct NTFS_BOOT_SECTOR - NTFS boot sector structure. */ -typedef struct -{ - u8 jump[3]; /* Irrelevant (jump to boot up code).*/ - u64 oem_id; /* Magic "NTFS ". */ - /*0x0b*/BIOS_PARAMETER_BLOCK bpb; /* See BIOS_PARAMETER_BLOCK. */ - u8 physical_drive; /* 0x00 floppy, 0x80 hard disk */ - u8 current_head; /* zero */ - u8 extended_boot_signature; /* 0x80 */ - u8 reserved2; /* zero */ - /*0x28*/s64 number_of_sectors; /* Number of sectors in volume. Gives - maximum volume size of 2^63 sectors. - Assuming standard sector size of 512 - bytes, the maximum byte size is - approx. 4.7x10^21 bytes. (-; */ - s64 mft_lcn; /* Cluster location of mft data. */ - s64 mftmirr_lcn; /* Cluster location of copy of mft. */ - s8 clusters_per_mft_record; /* Mft record size in clusters. */ - u8 reserved0[3]; /* zero */ - s8 clusters_per_index_record; /* Index block size in clusters. */ - u8 reserved1[3]; /* zero */ - u64 volume_serial_number; /* Irrelevant (serial number). */ - u32 checksum; /* Boot sector checksum. */ - /*0x54*/u8 bootstrap[426]; /* Irrelevant (boot up code). */ - u16 end_of_sector_marker; /* End of bootsector magic. Always is - 0xaa55 in little endian. */ - /* sizeof() = 512 (0x200) bytes */ -} __attribute__( ( __packed__ ) ) NTFS_BOOT_SECTOR; +typedef struct { + u8 jump[3]; /* Irrelevant (jump to boot up code).*/ + u64 oem_id; /* Magic "NTFS ". */ +/*0x0b*/BIOS_PARAMETER_BLOCK bpb; /* See BIOS_PARAMETER_BLOCK. */ + u8 physical_drive; /* 0x00 floppy, 0x80 hard disk */ + u8 current_head; /* zero */ + u8 extended_boot_signature; /* 0x80 */ + u8 reserved2; /* zero */ +/*0x28*/s64 number_of_sectors; /* Number of sectors in volume. Gives + maximum volume size of 2^63 sectors. + Assuming standard sector size of 512 + bytes, the maximum byte size is + approx. 4.7x10^21 bytes. (-; */ + s64 mft_lcn; /* Cluster location of mft data. */ + s64 mftmirr_lcn; /* Cluster location of copy of mft. */ + s8 clusters_per_mft_record; /* Mft record size in clusters. */ + u8 reserved0[3]; /* zero */ + s8 clusters_per_index_record; /* Index block size in clusters. */ + u8 reserved1[3]; /* zero */ + u64 volume_serial_number; /* Irrelevant (serial number). */ + u32 checksum; /* Boot sector checksum. */ +/*0x54*/u8 bootstrap[426]; /* Irrelevant (boot up code). */ + u16 end_of_sector_marker; /* End of bootsector magic. Always is + 0xaa55 in little endian. */ +/* sizeof() = 512 (0x200) bytes */ +} __attribute__((__packed__)) NTFS_BOOT_SECTOR; /** * enum NTFS_RECORD_TYPES - @@ -102,70 +100,69 @@ typedef struct * Magic identifiers present at the beginning of all ntfs record containing * records (like mft records for example). */ -typedef enum -{ - /* Found in $MFT/$DATA. */ - magic_FILE = const_cpu_to_le32( 0x454c4946 ), /* Mft entry. */ - magic_INDX = const_cpu_to_le32( 0x58444e49 ), /* Index buffer. */ - magic_HOLE = const_cpu_to_le32( 0x454c4f48 ), /* ? (NTFS 3.0+?) */ +typedef enum { + /* Found in $MFT/$DATA. */ + magic_FILE = const_cpu_to_le32(0x454c4946), /* Mft entry. */ + magic_INDX = const_cpu_to_le32(0x58444e49), /* Index buffer. */ + magic_HOLE = const_cpu_to_le32(0x454c4f48), /* ? (NTFS 3.0+?) */ - /* Found in $LogFile/$DATA. */ - magic_RSTR = const_cpu_to_le32( 0x52545352 ), /* Restart page. */ - magic_RCRD = const_cpu_to_le32( 0x44524352 ), /* Log record page. */ + /* Found in $LogFile/$DATA. */ + magic_RSTR = const_cpu_to_le32(0x52545352), /* Restart page. */ + magic_RCRD = const_cpu_to_le32(0x44524352), /* Log record page. */ - /* Found in $LogFile/$DATA. (May be found in $MFT/$DATA, also?) */ - magic_CHKD = const_cpu_to_le32( 0x444b4843 ), /* Modified by chkdsk. */ + /* Found in $LogFile/$DATA. (May be found in $MFT/$DATA, also?) */ + magic_CHKD = const_cpu_to_le32(0x444b4843), /* Modified by chkdsk. */ - /* Found in all ntfs record containing records. */ - magic_BAAD = const_cpu_to_le32( 0x44414142 ), /* Failed multi sector - transfer was detected. */ + /* Found in all ntfs record containing records. */ + magic_BAAD = const_cpu_to_le32(0x44414142), /* Failed multi sector + transfer was detected. */ - /* - * Found in $LogFile/$DATA when a page is full or 0xff bytes and is - * thus not initialized. User has to initialize the page before using - * it. - */ - magic_empty = const_cpu_to_le32( 0xffffffff ),/* Record is empty and has - to be initialized before - it can be used. */ + /* + * Found in $LogFile/$DATA when a page is full or 0xff bytes and is + * thus not initialized. User has to initialize the page before using + * it. + */ + magic_empty = const_cpu_to_le32(0xffffffff),/* Record is empty and has + to be initialized before + it can be used. */ } NTFS_RECORD_TYPES; /* * Generic magic comparison macros. Finally found a use for the ## preprocessor * operator! (-8 */ -#define ntfs_is_magic(x, m) ( (u32)(x) == (u32)magic_##m ) -#define ntfs_is_magicp(p, m) ( *(u32*)(p) == (u32)magic_##m ) +#define ntfs_is_magic(x, m) ( (u32)(x) == (u32)magic_##m ) +#define ntfs_is_magicp(p, m) ( *(u32*)(p) == (u32)magic_##m ) /* * Specialised magic comparison macros for the NTFS_RECORD_TYPES defined above. */ -#define ntfs_is_file_record(x) ( ntfs_is_magic (x, FILE) ) -#define ntfs_is_file_recordp(p) ( ntfs_is_magicp(p, FILE) ) -#define ntfs_is_mft_record(x) ( ntfs_is_file_record(x) ) -#define ntfs_is_mft_recordp(p) ( ntfs_is_file_recordp(p) ) -#define ntfs_is_indx_record(x) ( ntfs_is_magic (x, INDX) ) -#define ntfs_is_indx_recordp(p) ( ntfs_is_magicp(p, INDX) ) -#define ntfs_is_hole_record(x) ( ntfs_is_magic (x, HOLE) ) -#define ntfs_is_hole_recordp(p) ( ntfs_is_magicp(p, HOLE) ) +#define ntfs_is_file_record(x) ( ntfs_is_magic (x, FILE) ) +#define ntfs_is_file_recordp(p) ( ntfs_is_magicp(p, FILE) ) +#define ntfs_is_mft_record(x) ( ntfs_is_file_record(x) ) +#define ntfs_is_mft_recordp(p) ( ntfs_is_file_recordp(p) ) +#define ntfs_is_indx_record(x) ( ntfs_is_magic (x, INDX) ) +#define ntfs_is_indx_recordp(p) ( ntfs_is_magicp(p, INDX) ) +#define ntfs_is_hole_record(x) ( ntfs_is_magic (x, HOLE) ) +#define ntfs_is_hole_recordp(p) ( ntfs_is_magicp(p, HOLE) ) -#define ntfs_is_rstr_record(x) ( ntfs_is_magic (x, RSTR) ) -#define ntfs_is_rstr_recordp(p) ( ntfs_is_magicp(p, RSTR) ) -#define ntfs_is_rcrd_record(x) ( ntfs_is_magic (x, RCRD) ) -#define ntfs_is_rcrd_recordp(p) ( ntfs_is_magicp(p, RCRD) ) +#define ntfs_is_rstr_record(x) ( ntfs_is_magic (x, RSTR) ) +#define ntfs_is_rstr_recordp(p) ( ntfs_is_magicp(p, RSTR) ) +#define ntfs_is_rcrd_record(x) ( ntfs_is_magic (x, RCRD) ) +#define ntfs_is_rcrd_recordp(p) ( ntfs_is_magicp(p, RCRD) ) -#define ntfs_is_chkd_record(x) ( ntfs_is_magic (x, CHKD) ) -#define ntfs_is_chkd_recordp(p) ( ntfs_is_magicp(p, CHKD) ) +#define ntfs_is_chkd_record(x) ( ntfs_is_magic (x, CHKD) ) +#define ntfs_is_chkd_recordp(p) ( ntfs_is_magicp(p, CHKD) ) -#define ntfs_is_baad_record(x) ( ntfs_is_magic (x, BAAD) ) -#define ntfs_is_baad_recordp(p) ( ntfs_is_magicp(p, BAAD) ) +#define ntfs_is_baad_record(x) ( ntfs_is_magic (x, BAAD) ) +#define ntfs_is_baad_recordp(p) ( ntfs_is_magicp(p, BAAD) ) -#define ntfs_is_empty_record(x) ( ntfs_is_magic (x, empty) ) -#define ntfs_is_empty_recordp(p) ( ntfs_is_magicp(p, empty) ) +#define ntfs_is_empty_record(x) ( ntfs_is_magic (x, empty) ) +#define ntfs_is_empty_recordp(p) ( ntfs_is_magicp(p, empty) ) -#define NTFS_BLOCK_SIZE 512 -#define NTFS_BLOCK_SIZE_BITS 9 +#define NTFS_BLOCK_SIZE 512 +#define NTFS_BLOCK_SIZE_BITS 9 /** * struct NTFS_RECORD - @@ -179,23 +176,22 @@ typedef enum * are set to it (during writing). If they are not, an incomplete multi sector * transfer has occurred when the data was written. * The maximum size for the update sequence array is fixed to: - * maximum size = usa_ofs + (usa_count * 2) = 510 bytes + * maximum size = usa_ofs + (usa_count * 2) = 510 bytes * The 510 bytes comes from the fact that the last u16 in the array has to * (obviously) finish before the last u16 of the first 512-byte sector. * This formula can be used as a consistency check in that usa_ofs + * (usa_count * 2) has to be less than or equal to 510. */ -typedef struct -{ - NTFS_RECORD_TYPES magic;/* A four-byte magic identifying the - record type and/or status. */ - u16 usa_ofs; /* Offset to the Update Sequence Array (usa) - from the start of the ntfs record. */ - u16 usa_count; /* Number of u16 sized entries in the usa - including the Update Sequence Number (usn), - thus the number of fixups is the usa_count - minus 1. */ -} __attribute__( ( __packed__ ) ) NTFS_RECORD; +typedef struct { + NTFS_RECORD_TYPES magic;/* A four-byte magic identifying the + record type and/or status. */ + u16 usa_ofs; /* Offset to the Update Sequence Array (usa) + from the start of the ntfs record. */ + u16 usa_count; /* Number of u16 sized entries in the usa + including the Update Sequence Number (usn), + thus the number of fixups is the usa_count + minus 1. */ +} __attribute__((__packed__)) NTFS_RECORD; /** * enum NTFS_SYSTEM_FILES - System files mft record numbers. @@ -205,44 +201,43 @@ typedef struct * mft records. Also, the sequence number for each of the system files is * always equal to their mft record number and it is never modified. */ -typedef enum -{ - FILE_MFT = 0, /* Master file table (mft). Data attribute - contains the entries and bitmap attribute - records which ones are in use (bit==1). */ - FILE_MFTMirr = 1, /* Mft mirror: copy of first four mft records - in data attribute. If cluster size > 4kiB, - copy of first N mft records, with - N = cluster_size / mft_record_size. */ - FILE_LogFile = 2, /* Journalling log in data attribute. */ - FILE_Volume = 3, /* Volume name attribute and volume information - attribute (flags and ntfs version). Windows - refers to this file as volume DASD (Direct - Access Storage Device). */ - FILE_AttrDef = 4, /* Array of attribute definitions in data - attribute. */ - FILE_root = 5, /* Root directory. */ - FILE_Bitmap = 6, /* Allocation bitmap of all clusters (lcns) in - data attribute. */ - FILE_Boot = 7, /* Boot sector (always at cluster 0) in data - attribute. */ - FILE_BadClus = 8, /* Contains all bad clusters in the non-resident - data attribute. */ - FILE_Secure = 9, /* Shared security descriptors in data attribute - and two indexes into the descriptors. - Appeared in Windows 2000. Before that, this - file was named $Quota but was unused. */ - FILE_UpCase = 10, /* Uppercase equivalents of all 65536 Unicode - characters in data attribute. */ - FILE_Extend = 11, /* Directory containing other system files (eg. - $ObjId, $Quota, $Reparse and $UsnJrnl). This - is new to NTFS3.0. */ - FILE_reserved12 = 12, /* Reserved for future use (records 12-15). */ - FILE_reserved13 = 13, - FILE_reserved14 = 14, - FILE_reserved15 = 15, - FILE_first_user = 16, /* First user file, used as test limit for - whether to allow opening a file or not. */ +typedef enum { + FILE_MFT = 0, /* Master file table (mft). Data attribute + contains the entries and bitmap attribute + records which ones are in use (bit==1). */ + FILE_MFTMirr = 1, /* Mft mirror: copy of first four mft records + in data attribute. If cluster size > 4kiB, + copy of first N mft records, with + N = cluster_size / mft_record_size. */ + FILE_LogFile = 2, /* Journalling log in data attribute. */ + FILE_Volume = 3, /* Volume name attribute and volume information + attribute (flags and ntfs version). Windows + refers to this file as volume DASD (Direct + Access Storage Device). */ + FILE_AttrDef = 4, /* Array of attribute definitions in data + attribute. */ + FILE_root = 5, /* Root directory. */ + FILE_Bitmap = 6, /* Allocation bitmap of all clusters (lcns) in + data attribute. */ + FILE_Boot = 7, /* Boot sector (always at cluster 0) in data + attribute. */ + FILE_BadClus = 8, /* Contains all bad clusters in the non-resident + data attribute. */ + FILE_Secure = 9, /* Shared security descriptors in data attribute + and two indexes into the descriptors. + Appeared in Windows 2000. Before that, this + file was named $Quota but was unused. */ + FILE_UpCase = 10, /* Uppercase equivalents of all 65536 Unicode + characters in data attribute. */ + FILE_Extend = 11, /* Directory containing other system files (eg. + $ObjId, $Quota, $Reparse and $UsnJrnl). This + is new to NTFS3.0. */ + FILE_reserved12 = 12, /* Reserved for future use (records 12-15). */ + FILE_reserved13 = 13, + FILE_reserved14 = 14, + FILE_reserved15 = 15, + FILE_first_user = 16, /* First user file, used as test limit for + whether to allow opening a file or not. */ } NTFS_SYSTEM_FILES; /** @@ -250,7 +245,7 @@ typedef enum * * These are the so far known MFT_RECORD_* flags (16-bit) which contain * information about the mft record in which they are present. - * + * * MFT_RECORD_IS_4 exists on all $Extend sub-files. * It seems that it marks it is a metadata file with MFT record >24, however, * it is unknown if it is limited to metadata files only. @@ -259,15 +254,14 @@ typedef enum * index, that means an INDEX_ROOT and an INDEX_ALLOCATION with a name other * than "$I30". It is unknown if it is limited to metadata files only. */ -typedef enum -{ - MFT_RECORD_IN_USE = const_cpu_to_le16( 0x0001 ), - MFT_RECORD_IS_DIRECTORY = const_cpu_to_le16( 0x0002 ), - MFT_RECORD_IS_4 = const_cpu_to_le16( 0x0004 ), - MFT_RECORD_IS_VIEW_INDEX = const_cpu_to_le16( 0x0008 ), - MFT_REC_SPACE_FILLER = 0xffff, /* Just to make flags - 16-bit. */ -} __attribute__( ( __packed__ ) ) MFT_RECORD_FLAGS; +typedef enum { + MFT_RECORD_IN_USE = const_cpu_to_le16(0x0001), + MFT_RECORD_IS_DIRECTORY = const_cpu_to_le16(0x0002), + MFT_RECORD_IS_4 = const_cpu_to_le16(0x0004), + MFT_RECORD_IS_VIEW_INDEX = const_cpu_to_le16(0x0008), + MFT_REC_SPACE_FILLER = 0xffff, /* Just to make flags + 16-bit. */ +} __attribute__((__packed__)) MFT_RECORD_FLAGS; /* * mft references (aka file references or file record segment references) are @@ -318,19 +312,19 @@ typedef enum typedef u64 MFT_REF; -#define MK_MREF(m, s) ((MFT_REF)(((MFT_REF)(s) << 48) | \ - ((MFT_REF)(m) & MFT_REF_MASK_CPU))) +#define MK_MREF(m, s) ((MFT_REF)(((MFT_REF)(s) << 48) | \ + ((MFT_REF)(m) & MFT_REF_MASK_CPU))) #define MK_LE_MREF(m, s) const_cpu_to_le64(((MFT_REF)(((MFT_REF)(s) << 48) | \ - ((MFT_REF)(m) & MFT_REF_MASK_CPU)))) + ((MFT_REF)(m) & MFT_REF_MASK_CPU)))) -#define MREF(x) ((u64)((x) & MFT_REF_MASK_CPU)) -#define MSEQNO(x) ((u16)(((x) >> 48) & 0xffff)) -#define MREF_LE(x) ((u64)(const_le64_to_cpu(x) & MFT_REF_MASK_CPU)) -#define MSEQNO_LE(x) ((u16)((const_le64_to_cpu(x) >> 48) & 0xffff)) +#define MREF(x) ((u64)((x) & MFT_REF_MASK_CPU)) +#define MSEQNO(x) ((u16)(((x) >> 48) & 0xffff)) +#define MREF_LE(x) ((u64)(const_le64_to_cpu(x) & MFT_REF_MASK_CPU)) +#define MSEQNO_LE(x) ((u16)((const_le64_to_cpu(x) >> 48) & 0xffff)) -#define IS_ERR_MREF(x) (((x) & 0x0000800000000000ULL) ? 1 : 0) -#define ERR_MREF(x) ((u64)((s64)(x))) -#define MREF_ERR(x) ((int)((s64)(x))) +#define IS_ERR_MREF(x) (((x) & 0x0000800000000000ULL) ? 1 : 0) +#define ERR_MREF(x) ((u64)((s64)(x))) +#define MREF_ERR(x) ((int)((s64)(x))) /** * struct MFT_RECORD - An MFT record layout (NTFS 3.1+) @@ -341,147 +335,145 @@ typedef u64 MFT_REF; * in that it only consists of the attribute type code AT_END and none of the * other members of the attribute structure are present. */ -typedef struct -{ - /*Ofs*/ - /* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */ - NTFS_RECORD_TYPES magic;/* Usually the magic is "FILE". */ - u16 usa_ofs; /* See NTFS_RECORD definition above. */ - u16 usa_count; /* See NTFS_RECORD definition above. */ +typedef struct { +/*Ofs*/ +/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */ + NTFS_RECORD_TYPES magic;/* Usually the magic is "FILE". */ + u16 usa_ofs; /* See NTFS_RECORD definition above. */ + u16 usa_count; /* See NTFS_RECORD definition above. */ - /* 8*/ LSN lsn; /* $LogFile sequence number for this record. - Changed every time the record is modified. */ - /* 16*/ u16 sequence_number; /* Number of times this mft record has been - reused. (See description for MFT_REF - above.) NOTE: The increment (skipping zero) - is done when the file is deleted. NOTE: If - this is zero it is left zero. */ - /* 18*/ u16 link_count; /* Number of hard links, i.e. the number of - directory entries referencing this record. - NOTE: Only used in mft base records. - NOTE: When deleting a directory entry we - check the link_count and if it is 1 we - delete the file. Otherwise we delete the - FILE_NAME_ATTR being referenced by the - directory entry from the mft record and - decrement the link_count. - FIXME: Careful with Win32 + DOS names! */ - /* 20*/ u16 attrs_offset; /* Byte offset to the first attribute in this - mft record from the start of the mft record. - NOTE: Must be aligned to 8-byte boundary. */ - /* 22*/ MFT_RECORD_FLAGS flags; /* Bit array of MFT_RECORD_FLAGS. When a file - is deleted, the MFT_RECORD_IN_USE flag is - set to zero. */ - /* 24*/ u32 bytes_in_use; /* Number of bytes used in this mft record. - NOTE: Must be aligned to 8-byte boundary. */ - /* 28*/ u32 bytes_allocated; /* Number of bytes allocated for this mft - record. This should be equal to the mft - record size. */ - /* 32*/ MFT_REF base_mft_record; /* This is zero for base mft records. - When it is not zero it is a mft reference - pointing to the base mft record to which - this record belongs (this is then used to - locate the attribute list attribute present - in the base record which describes this - extension record and hence might need - modification when the extension record - itself is modified, also locating the - attribute list also means finding the other - potential extents, belonging to the non-base - mft record). */ - /* 40*/ u16 next_attr_instance; /* The instance number that will be - assigned to the next attribute added to this - mft record. NOTE: Incremented each time - after it is used. NOTE: Every time the mft - record is reused this number is set to zero. - NOTE: The first instance number is always 0. - */ - /* The below fields are specific to NTFS 3.1+ (Windows XP and above): */ - /* 42*/ u16 reserved; /* Reserved/alignment. */ - /* 44*/ u32 mft_record_number; /* Number of this mft record. */ - /* sizeof() = 48 bytes */ - /* - * When (re)using the mft record, we place the update sequence array at this - * offset, i.e. before we start with the attributes. This also makes sense, - * otherwise we could run into problems with the update sequence array - * containing in itself the last two bytes of a sector which would mean that - * multi sector transfer protection wouldn't work. As you can't protect data - * by overwriting it since you then can't get it back... - * When reading we obviously use the data from the ntfs record header. - */ -} __attribute__( ( __packed__ ) ) MFT_RECORD; +/* 8*/ LSN lsn; /* $LogFile sequence number for this record. + Changed every time the record is modified. */ +/* 16*/ u16 sequence_number; /* Number of times this mft record has been + reused. (See description for MFT_REF + above.) NOTE: The increment (skipping zero) + is done when the file is deleted. NOTE: If + this is zero it is left zero. */ +/* 18*/ u16 link_count; /* Number of hard links, i.e. the number of + directory entries referencing this record. + NOTE: Only used in mft base records. + NOTE: When deleting a directory entry we + check the link_count and if it is 1 we + delete the file. Otherwise we delete the + FILE_NAME_ATTR being referenced by the + directory entry from the mft record and + decrement the link_count. + FIXME: Careful with Win32 + DOS names! */ +/* 20*/ u16 attrs_offset; /* Byte offset to the first attribute in this + mft record from the start of the mft record. + NOTE: Must be aligned to 8-byte boundary. */ +/* 22*/ MFT_RECORD_FLAGS flags; /* Bit array of MFT_RECORD_FLAGS. When a file + is deleted, the MFT_RECORD_IN_USE flag is + set to zero. */ +/* 24*/ u32 bytes_in_use; /* Number of bytes used in this mft record. + NOTE: Must be aligned to 8-byte boundary. */ +/* 28*/ u32 bytes_allocated; /* Number of bytes allocated for this mft + record. This should be equal to the mft + record size. */ +/* 32*/ MFT_REF base_mft_record; /* This is zero for base mft records. + When it is not zero it is a mft reference + pointing to the base mft record to which + this record belongs (this is then used to + locate the attribute list attribute present + in the base record which describes this + extension record and hence might need + modification when the extension record + itself is modified, also locating the + attribute list also means finding the other + potential extents, belonging to the non-base + mft record). */ +/* 40*/ u16 next_attr_instance; /* The instance number that will be + assigned to the next attribute added to this + mft record. NOTE: Incremented each time + after it is used. NOTE: Every time the mft + record is reused this number is set to zero. + NOTE: The first instance number is always 0. + */ +/* The below fields are specific to NTFS 3.1+ (Windows XP and above): */ +/* 42*/ u16 reserved; /* Reserved/alignment. */ +/* 44*/ u32 mft_record_number; /* Number of this mft record. */ +/* sizeof() = 48 bytes */ +/* + * When (re)using the mft record, we place the update sequence array at this + * offset, i.e. before we start with the attributes. This also makes sense, + * otherwise we could run into problems with the update sequence array + * containing in itself the last two bytes of a sector which would mean that + * multi sector transfer protection wouldn't work. As you can't protect data + * by overwriting it since you then can't get it back... + * When reading we obviously use the data from the ntfs record header. + */ +} __attribute__((__packed__)) MFT_RECORD; /** * struct MFT_RECORD_OLD - An MFT record layout (NTFS <=3.0) * * This is the version without the NTFS 3.1+ specific fields. */ -typedef struct -{ - /*Ofs*/ - /* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */ - NTFS_RECORD_TYPES magic;/* Usually the magic is "FILE". */ - u16 usa_ofs; /* See NTFS_RECORD definition above. */ - u16 usa_count; /* See NTFS_RECORD definition above. */ +typedef struct { +/*Ofs*/ +/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */ + NTFS_RECORD_TYPES magic;/* Usually the magic is "FILE". */ + u16 usa_ofs; /* See NTFS_RECORD definition above. */ + u16 usa_count; /* See NTFS_RECORD definition above. */ - /* 8*/ LSN lsn; /* $LogFile sequence number for this record. - Changed every time the record is modified. */ - /* 16*/ u16 sequence_number; /* Number of times this mft record has been - reused. (See description for MFT_REF - above.) NOTE: The increment (skipping zero) - is done when the file is deleted. NOTE: If - this is zero it is left zero. */ - /* 18*/ u16 link_count; /* Number of hard links, i.e. the number of - directory entries referencing this record. - NOTE: Only used in mft base records. - NOTE: When deleting a directory entry we - check the link_count and if it is 1 we - delete the file. Otherwise we delete the - FILE_NAME_ATTR being referenced by the - directory entry from the mft record and - decrement the link_count. - FIXME: Careful with Win32 + DOS names! */ - /* 20*/ u16 attrs_offset; /* Byte offset to the first attribute in this - mft record from the start of the mft record. - NOTE: Must be aligned to 8-byte boundary. */ - /* 22*/ MFT_RECORD_FLAGS flags; /* Bit array of MFT_RECORD_FLAGS. When a file - is deleted, the MFT_RECORD_IN_USE flag is - set to zero. */ - /* 24*/ u32 bytes_in_use; /* Number of bytes used in this mft record. - NOTE: Must be aligned to 8-byte boundary. */ - /* 28*/ u32 bytes_allocated; /* Number of bytes allocated for this mft - record. This should be equal to the mft - record size. */ - /* 32*/ MFT_REF base_mft_record; /* This is zero for base mft records. - When it is not zero it is a mft reference - pointing to the base mft record to which - this record belongs (this is then used to - locate the attribute list attribute present - in the base record which describes this - extension record and hence might need - modification when the extension record - itself is modified, also locating the - attribute list also means finding the other - potential extents, belonging to the non-base - mft record). */ - /* 40*/ u16 next_attr_instance; /* The instance number that will be - assigned to the next attribute added to this - mft record. NOTE: Incremented each time - after it is used. NOTE: Every time the mft - record is reused this number is set to zero. - NOTE: The first instance number is always 0. - */ - /* sizeof() = 42 bytes */ - /* - * When (re)using the mft record, we place the update sequence array at this - * offset, i.e. before we start with the attributes. This also makes sense, - * otherwise we could run into problems with the update sequence array - * containing in itself the last two bytes of a sector which would mean that - * multi sector transfer protection wouldn't work. As you can't protect data - * by overwriting it since you then can't get it back... - * When reading we obviously use the data from the ntfs record header. - */ -} __attribute__( ( __packed__ ) ) MFT_RECORD_OLD; +/* 8*/ LSN lsn; /* $LogFile sequence number for this record. + Changed every time the record is modified. */ +/* 16*/ u16 sequence_number; /* Number of times this mft record has been + reused. (See description for MFT_REF + above.) NOTE: The increment (skipping zero) + is done when the file is deleted. NOTE: If + this is zero it is left zero. */ +/* 18*/ u16 link_count; /* Number of hard links, i.e. the number of + directory entries referencing this record. + NOTE: Only used in mft base records. + NOTE: When deleting a directory entry we + check the link_count and if it is 1 we + delete the file. Otherwise we delete the + FILE_NAME_ATTR being referenced by the + directory entry from the mft record and + decrement the link_count. + FIXME: Careful with Win32 + DOS names! */ +/* 20*/ u16 attrs_offset; /* Byte offset to the first attribute in this + mft record from the start of the mft record. + NOTE: Must be aligned to 8-byte boundary. */ +/* 22*/ MFT_RECORD_FLAGS flags; /* Bit array of MFT_RECORD_FLAGS. When a file + is deleted, the MFT_RECORD_IN_USE flag is + set to zero. */ +/* 24*/ u32 bytes_in_use; /* Number of bytes used in this mft record. + NOTE: Must be aligned to 8-byte boundary. */ +/* 28*/ u32 bytes_allocated; /* Number of bytes allocated for this mft + record. This should be equal to the mft + record size. */ +/* 32*/ MFT_REF base_mft_record; /* This is zero for base mft records. + When it is not zero it is a mft reference + pointing to the base mft record to which + this record belongs (this is then used to + locate the attribute list attribute present + in the base record which describes this + extension record and hence might need + modification when the extension record + itself is modified, also locating the + attribute list also means finding the other + potential extents, belonging to the non-base + mft record). */ +/* 40*/ u16 next_attr_instance; /* The instance number that will be + assigned to the next attribute added to this + mft record. NOTE: Incremented each time + after it is used. NOTE: Every time the mft + record is reused this number is set to zero. + NOTE: The first instance number is always 0. + */ +/* sizeof() = 42 bytes */ +/* + * When (re)using the mft record, we place the update sequence array at this + * offset, i.e. before we start with the attributes. This also makes sense, + * otherwise we could run into problems with the update sequence array + * containing in itself the last two bytes of a sector which would mean that + * multi sector transfer protection wouldn't work. As you can't protect data + * by overwriting it since you then can't get it back... + * When reading we obviously use the data from the ntfs record header. + */ +} __attribute__((__packed__)) MFT_RECORD_OLD; /** * enum ATTR_TYPES - System defined attributes (32-bit). @@ -489,32 +481,31 @@ typedef struct * Each attribute type has a corresponding attribute name (Unicode string of * maximum 64 character length) as described by the attribute definitions * present in the data attribute of the $AttrDef system file. - * + * * On NTFS 3.0 volumes the names are just as the types are named in the below * enum exchanging AT_ for the dollar sign ($). If that isn't a revealing * choice of symbol... (-; */ -typedef enum -{ - AT_UNUSED = const_cpu_to_le32( 0 ), - AT_STANDARD_INFORMATION = const_cpu_to_le32( 0x10 ), - AT_ATTRIBUTE_LIST = const_cpu_to_le32( 0x20 ), - AT_FILE_NAME = const_cpu_to_le32( 0x30 ), - AT_OBJECT_ID = const_cpu_to_le32( 0x40 ), - AT_SECURITY_DESCRIPTOR = const_cpu_to_le32( 0x50 ), - AT_VOLUME_NAME = const_cpu_to_le32( 0x60 ), - AT_VOLUME_INFORMATION = const_cpu_to_le32( 0x70 ), - AT_DATA = const_cpu_to_le32( 0x80 ), - AT_INDEX_ROOT = const_cpu_to_le32( 0x90 ), - AT_INDEX_ALLOCATION = const_cpu_to_le32( 0xa0 ), - AT_BITMAP = const_cpu_to_le32( 0xb0 ), - AT_REPARSE_POINT = const_cpu_to_le32( 0xc0 ), - AT_EA_INFORMATION = const_cpu_to_le32( 0xd0 ), - AT_EA = const_cpu_to_le32( 0xe0 ), - AT_PROPERTY_SET = const_cpu_to_le32( 0xf0 ), - AT_LOGGED_UTILITY_STREAM = const_cpu_to_le32( 0x100 ), - AT_FIRST_USER_DEFINED_ATTRIBUTE = const_cpu_to_le32( 0x1000 ), - AT_END = const_cpu_to_le32( 0xffffffff ), +typedef enum { + AT_UNUSED = const_cpu_to_le32( 0), + AT_STANDARD_INFORMATION = const_cpu_to_le32( 0x10), + AT_ATTRIBUTE_LIST = const_cpu_to_le32( 0x20), + AT_FILE_NAME = const_cpu_to_le32( 0x30), + AT_OBJECT_ID = const_cpu_to_le32( 0x40), + AT_SECURITY_DESCRIPTOR = const_cpu_to_le32( 0x50), + AT_VOLUME_NAME = const_cpu_to_le32( 0x60), + AT_VOLUME_INFORMATION = const_cpu_to_le32( 0x70), + AT_DATA = const_cpu_to_le32( 0x80), + AT_INDEX_ROOT = const_cpu_to_le32( 0x90), + AT_INDEX_ALLOCATION = const_cpu_to_le32( 0xa0), + AT_BITMAP = const_cpu_to_le32( 0xb0), + AT_REPARSE_POINT = const_cpu_to_le32( 0xc0), + AT_EA_INFORMATION = const_cpu_to_le32( 0xd0), + AT_EA = const_cpu_to_le32( 0xe0), + AT_PROPERTY_SET = const_cpu_to_le32( 0xf0), + AT_LOGGED_UTILITY_STREAM = const_cpu_to_le32( 0x100), + AT_FIRST_USER_DEFINED_ATTRIBUTE = const_cpu_to_le32( 0x1000), + AT_END = const_cpu_to_le32(0xffffffff), } ATTR_TYPES; /** @@ -522,55 +513,54 @@ typedef enum * (32-bit). * * COLLATION_UNICODE_STRING - Collate Unicode strings by comparing their binary - * Unicode values, except that when a character can be uppercased, the - * upper case value collates before the lower case one. + * Unicode values, except that when a character can be uppercased, the + * upper case value collates before the lower case one. * COLLATION_FILE_NAME - Collate file names as Unicode strings. The collation - * is done very much like COLLATION_UNICODE_STRING. In fact I have no idea - * what the difference is. Perhaps the difference is that file names - * would treat some special characters in an odd way (see - * unistr.c::ntfs_collate_names() and unistr.c::legal_ansi_char_array[] - * for what I mean but COLLATION_UNICODE_STRING would not give any special - * treatment to any characters at all, but this is speculation. + * is done very much like COLLATION_UNICODE_STRING. In fact I have no idea + * what the difference is. Perhaps the difference is that file names + * would treat some special characters in an odd way (see + * unistr.c::ntfs_collate_names() and unistr.c::legal_ansi_char_array[] + * for what I mean but COLLATION_UNICODE_STRING would not give any special + * treatment to any characters at all, but this is speculation. * COLLATION_NTOFS_ULONG - Sorting is done according to ascending u32 key - * values. E.g. used for $SII index in FILE_Secure, which sorts by - * security_id (u32). + * values. E.g. used for $SII index in FILE_Secure, which sorts by + * security_id (u32). * COLLATION_NTOFS_SID - Sorting is done according to ascending SID values. - * E.g. used for $O index in FILE_Extend/$Quota. + * E.g. used for $O index in FILE_Extend/$Quota. * COLLATION_NTOFS_SECURITY_HASH - Sorting is done first by ascending hash - * values and second by ascending security_id values. E.g. used for $SDH - * index in FILE_Secure. + * values and second by ascending security_id values. E.g. used for $SDH + * index in FILE_Secure. * COLLATION_NTOFS_ULONGS - Sorting is done according to a sequence of ascending - * u32 key values. E.g. used for $O index in FILE_Extend/$ObjId, which - * sorts by object_id (16-byte), by splitting up the object_id in four - * u32 values and using them as individual keys. E.g. take the following - * two security_ids, stored as follows on disk: - * 1st: a1 61 65 b7 65 7b d4 11 9e 3d 00 e0 81 10 42 59 - * 2nd: 38 14 37 d2 d2 f3 d4 11 a5 21 c8 6b 79 b1 97 45 - * To compare them, they are split into four u32 values each, like so: - * 1st: 0xb76561a1 0x11d47b65 0xe0003d9e 0x59421081 - * 2nd: 0xd2371438 0x11d4f3d2 0x6bc821a5 0x4597b179 - * Now, it is apparent why the 2nd object_id collates after the 1st: the - * first u32 value of the 1st object_id is less than the first u32 of - * the 2nd object_id. If the first u32 values of both object_ids were - * equal then the second u32 values would be compared, etc. + * u32 key values. E.g. used for $O index in FILE_Extend/$ObjId, which + * sorts by object_id (16-byte), by splitting up the object_id in four + * u32 values and using them as individual keys. E.g. take the following + * two security_ids, stored as follows on disk: + * 1st: a1 61 65 b7 65 7b d4 11 9e 3d 00 e0 81 10 42 59 + * 2nd: 38 14 37 d2 d2 f3 d4 11 a5 21 c8 6b 79 b1 97 45 + * To compare them, they are split into four u32 values each, like so: + * 1st: 0xb76561a1 0x11d47b65 0xe0003d9e 0x59421081 + * 2nd: 0xd2371438 0x11d4f3d2 0x6bc821a5 0x4597b179 + * Now, it is apparent why the 2nd object_id collates after the 1st: the + * first u32 value of the 1st object_id is less than the first u32 of + * the 2nd object_id. If the first u32 values of both object_ids were + * equal then the second u32 values would be compared, etc. */ -typedef enum -{ - COLLATION_BINARY = const_cpu_to_le32( 0 ), /* Collate by binary - compare where the first byte is most - significant. */ - COLLATION_FILE_NAME = const_cpu_to_le32( 1 ), /* Collate file names - as Unicode strings. */ - COLLATION_UNICODE_STRING = const_cpu_to_le32( 2 ), /* Collate Unicode - strings by comparing their binary - Unicode values, except that when a - character can be uppercased, the upper - case value collates before the lower - case one. */ - COLLATION_NTOFS_ULONG = const_cpu_to_le32( 16 ), - COLLATION_NTOFS_SID = const_cpu_to_le32( 17 ), - COLLATION_NTOFS_SECURITY_HASH = const_cpu_to_le32( 18 ), - COLLATION_NTOFS_ULONGS = const_cpu_to_le32( 19 ), +typedef enum { + COLLATION_BINARY = const_cpu_to_le32(0), /* Collate by binary + compare where the first byte is most + significant. */ + COLLATION_FILE_NAME = const_cpu_to_le32(1), /* Collate file names + as Unicode strings. */ + COLLATION_UNICODE_STRING = const_cpu_to_le32(2), /* Collate Unicode + strings by comparing their binary + Unicode values, except that when a + character can be uppercased, the upper + case value collates before the lower + case one. */ + COLLATION_NTOFS_ULONG = const_cpu_to_le32(16), + COLLATION_NTOFS_SID = const_cpu_to_le32(17), + COLLATION_NTOFS_SECURITY_HASH = const_cpu_to_le32(18), + COLLATION_NTOFS_ULONGS = const_cpu_to_le32(19), } COLLATION_RULES; /** @@ -583,32 +573,31 @@ typedef enum * name attribute has this flag set and this is the only attribute indexed in * NT4. */ -typedef enum -{ - ATTR_DEF_INDEXABLE = const_cpu_to_le32( 0x02 ), /* Attribute can be - indexed. */ - ATTR_DEF_MULTIPLE = const_cpu_to_le32( 0x04 ), /* Attribute type - can be present multiple times in the - mft records of an inode. */ - ATTR_DEF_NOT_ZERO = const_cpu_to_le32( 0x08 ), /* Attribute value - must contain at least one non-zero - byte. */ - ATTR_DEF_INDEXED_UNIQUE = const_cpu_to_le32( 0x10 ), /* Attribute must be - indexed and the attribute value must be - unique for the attribute type in all of - the mft records of an inode. */ - ATTR_DEF_NAMED_UNIQUE = const_cpu_to_le32( 0x20 ), /* Attribute must be - named and the name must be unique for - the attribute type in all of the mft - records of an inode. */ - ATTR_DEF_RESIDENT = const_cpu_to_le32( 0x40 ), /* Attribute must be - resident. */ - ATTR_DEF_ALWAYS_LOG = const_cpu_to_le32( 0x80 ), /* Always log - modifications to this attribute, - regardless of whether it is resident or - non-resident. Without this, only log - modifications if the attribute is - resident. */ +typedef enum { + ATTR_DEF_INDEXABLE = const_cpu_to_le32(0x02), /* Attribute can be + indexed. */ + ATTR_DEF_MULTIPLE = const_cpu_to_le32(0x04), /* Attribute type + can be present multiple times in the + mft records of an inode. */ + ATTR_DEF_NOT_ZERO = const_cpu_to_le32(0x08), /* Attribute value + must contain at least one non-zero + byte. */ + ATTR_DEF_INDEXED_UNIQUE = const_cpu_to_le32(0x10), /* Attribute must be + indexed and the attribute value must be + unique for the attribute type in all of + the mft records of an inode. */ + ATTR_DEF_NAMED_UNIQUE = const_cpu_to_le32(0x20), /* Attribute must be + named and the name must be unique for + the attribute type in all of the mft + records of an inode. */ + ATTR_DEF_RESIDENT = const_cpu_to_le32(0x40), /* Attribute must be + resident. */ + ATTR_DEF_ALWAYS_LOG = const_cpu_to_le32(0x80), /* Always log + modifications to this attribute, + regardless of whether it is resident or + non-resident. Without this, only log + modifications if the attribute is + resident. */ } ATTR_DEF_FLAGS; /** @@ -622,34 +611,31 @@ typedef enum * attribute can be resident/non-resident and possibly other things, but the * actual bits are unknown. */ -typedef struct -{ - /*hex ofs*/ - /* 0*/ - ntfschar name[0x40]; /* Unicode name of the attribute. Zero - terminated. */ - /* 80*/ ATTR_TYPES type; /* Type of the attribute. */ - /* 84*/ u32 display_rule; /* Default display rule. - FIXME: What does it mean? (AIA) */ - /* 88*/ COLLATION_RULES collation_rule; /* Default collation rule. */ - /* 8c*/ ATTR_DEF_FLAGS flags; /* Flags describing the attribute. */ - /* 90*/ s64 min_size; /* Optional minimum attribute size. */ - /* 98*/ s64 max_size; /* Maximum size of attribute. */ - /* sizeof() = 0xa0 or 160 bytes */ -} __attribute__( ( __packed__ ) ) ATTR_DEF; +typedef struct { +/*hex ofs*/ +/* 0*/ ntfschar name[0x40]; /* Unicode name of the attribute. Zero + terminated. */ +/* 80*/ ATTR_TYPES type; /* Type of the attribute. */ +/* 84*/ u32 display_rule; /* Default display rule. + FIXME: What does it mean? (AIA) */ +/* 88*/ COLLATION_RULES collation_rule; /* Default collation rule. */ +/* 8c*/ ATTR_DEF_FLAGS flags; /* Flags describing the attribute. */ +/* 90*/ s64 min_size; /* Optional minimum attribute size. */ +/* 98*/ s64 max_size; /* Maximum size of attribute. */ +/* sizeof() = 0xa0 or 160 bytes */ +} __attribute__((__packed__)) ATTR_DEF; /** * enum ATTR_FLAGS - Attribute flags (16-bit). */ -typedef enum -{ - ATTR_IS_COMPRESSED = const_cpu_to_le16( 0x0001 ), - ATTR_COMPRESSION_MASK = const_cpu_to_le16( 0x00ff ), /* Compression - method mask. Also, first - illegal value. */ - ATTR_IS_ENCRYPTED = const_cpu_to_le16( 0x4000 ), - ATTR_IS_SPARSE = const_cpu_to_le16( 0x8000 ), -} __attribute__( ( __packed__ ) ) ATTR_FLAGS; +typedef enum { + ATTR_IS_COMPRESSED = const_cpu_to_le16(0x0001), + ATTR_COMPRESSION_MASK = const_cpu_to_le16(0x00ff), /* Compression + method mask. Also, first + illegal value. */ + ATTR_IS_ENCRYPTED = const_cpu_to_le16(0x4000), + ATTR_IS_SPARSE = const_cpu_to_le16(0x8000), +} __attribute__((__packed__)) ATTR_FLAGS; /* * Attribute compression. @@ -668,32 +654,32 @@ typedef enum * can be stored: * * 1) The data in the block is all zero (a sparse block): - * This is stored as a sparse block in the runlist, i.e. the runlist - * entry has length = X and lcn = -1. The mapping pairs array actually - * uses a delta_lcn value length of 0, i.e. delta_lcn is not present at - * all, which is then interpreted by the driver as lcn = -1. - * NOTE: Even uncompressed files can be sparse on NTFS 3.0 volumes, then - * the same principles apply as above, except that the length is not - * restricted to being any particular value. + * This is stored as a sparse block in the runlist, i.e. the runlist + * entry has length = X and lcn = -1. The mapping pairs array actually + * uses a delta_lcn value length of 0, i.e. delta_lcn is not present at + * all, which is then interpreted by the driver as lcn = -1. + * NOTE: Even uncompressed files can be sparse on NTFS 3.0 volumes, then + * the same principles apply as above, except that the length is not + * restricted to being any particular value. * * 2) The data in the block is not compressed: - * This happens when compression doesn't reduce the size of the block - * in clusters. I.e. if compression has a small effect so that the - * compressed data still occupies X clusters, then the uncompressed data - * is stored in the block. - * This case is recognised by the fact that the runlist entry has - * length = X and lcn >= 0. The mapping pairs array stores this as - * normal with a run length of X and some specific delta_lcn, i.e. - * delta_lcn has to be present. + * This happens when compression doesn't reduce the size of the block + * in clusters. I.e. if compression has a small effect so that the + * compressed data still occupies X clusters, then the uncompressed data + * is stored in the block. + * This case is recognised by the fact that the runlist entry has + * length = X and lcn >= 0. The mapping pairs array stores this as + * normal with a run length of X and some specific delta_lcn, i.e. + * delta_lcn has to be present. * * 3) The data in the block is compressed: - * The common case. This case is recognised by the fact that the run - * list entry has length L < X and lcn >= 0. The mapping pairs array - * stores this as normal with a run length of X and some specific - * delta_lcn, i.e. delta_lcn has to be present. This runlist entry is - * immediately followed by a sparse entry with length = X - L and - * lcn = -1. The latter entry is to make up the vcn counting to the - * full compression block size X. + * The common case. This case is recognised by the fact that the run + * list entry has length L < X and lcn >= 0. The mapping pairs array + * stores this as normal with a run length of X and some specific + * delta_lcn, i.e. delta_lcn has to be present. This runlist entry is + * immediately followed by a sparse entry with length = X - L and + * lcn = -1. The latter entry is to make up the vcn counting to the + * full compression block size X. * * In fact, life is more complicated because adjacent entries of the same type * can be coalesced. This means that one has to keep track of the number of @@ -721,195 +707,186 @@ typedef enum /** * enum RESIDENT_ATTR_FLAGS - Flags of resident attributes (8-bit). */ -typedef enum -{ - RESIDENT_ATTR_IS_INDEXED = 0x01, /* Attribute is referenced in an index - (has implications for deleting and - modifying the attribute). */ -} __attribute__( ( __packed__ ) ) RESIDENT_ATTR_FLAGS; +typedef enum { + RESIDENT_ATTR_IS_INDEXED = 0x01, /* Attribute is referenced in an index + (has implications for deleting and + modifying the attribute). */ +} __attribute__((__packed__)) RESIDENT_ATTR_FLAGS; /** * struct ATTR_RECORD - Attribute record header. * * Always aligned to 8-byte boundary. */ -typedef struct -{ - /*Ofs*/ - /* 0*/ - ATTR_TYPES type; /* The (32-bit) type of the attribute. */ - /* 4*/ u32 length; /* Byte size of the resident part of the - attribute (aligned to 8-byte boundary). - Used to get to the next attribute. */ - /* 8*/ u8 non_resident; /* If 0, attribute is resident. - If 1, attribute is non-resident. */ - /* 9*/ u8 name_length; /* Unicode character size of name of attribute. - 0 if unnamed. */ - /* 10*/ u16 name_offset; /* If name_length != 0, the byte offset to the - beginning of the name from the attribute - record. Note that the name is stored as a - Unicode string. When creating, place offset - just at the end of the record header. Then, - follow with attribute value or mapping pairs - array, resident and non-resident attributes - respectively, aligning to an 8-byte - boundary. */ - /* 12*/ ATTR_FLAGS flags; /* Flags describing the attribute. */ - /* 14*/ u16 instance; /* The instance of this attribute record. This - number is unique within this mft record (see - MFT_RECORD/next_attribute_instance notes - above for more details). */ - /* 16*/ union - { - /* Resident attributes. */ - struct - { - /* 16 */ - u32 value_length; /* Byte size of attribute value. */ - /* 20 */ u16 value_offset; /* Byte offset of the attribute - value from the start of the - attribute record. When creating, - align to 8-byte boundary if we - have a name present as this might - not have a length of a multiple - of 8-bytes. */ - /* 22 */ RESIDENT_ATTR_FLAGS resident_flags; /* See above. */ - /* 23 */ s8 reservedR; /* Reserved/alignment to 8-byte - boundary. */ - /* 24 */ void *resident_end[0]; /* Use offsetof(ATTR_RECORD, - resident_end) to get size of - a resident attribute. */ - } __attribute__( ( __packed__ ) ); - /* Non-resident attributes. */ - struct - { - /* 16*/ - VCN lowest_vcn; /* Lowest valid virtual cluster number - for this portion of the attribute value or - 0 if this is the only extent (usually the - case). - Only when an attribute list is used - does lowest_vcn != 0 ever occur. */ - /* 24*/ VCN highest_vcn; /* Highest valid vcn of this extent of - the attribute value. - Usually there is only one - portion, so this usually equals the attribute - value size in clusters minus 1. Can be -1 for - zero length files. Can be 0 for "single extent" - attributes. */ - /* 32*/ u16 mapping_pairs_offset; /* Byte offset from the - beginning of the structure to the mapping pairs - array which contains the mappings between the - vcns and the logical cluster numbers (lcns). - When creating, place this at the end of this - record header aligned to 8-byte boundary. */ - /* 34*/ u8 compression_unit; /* The compression unit expressed - as the log to the base 2 of the number of - clusters in a compression unit. 0 means not - compressed. (This effectively limits the - compression unit size to be a power of two - clusters.) WinNT4 only uses a value of 4. */ - /* 35*/ u8 reserved1[5]; /* Align to 8-byte boundary. */ - /* The sizes below are only used when lowest_vcn is zero, as otherwise it would - be difficult to keep them up-to-date.*/ - /* 40*/ s64 allocated_size; /* Byte size of disk space - allocated to hold the attribute value. Always - is a multiple of the cluster size. When a file - is compressed, this field is a multiple of the - compression block size (2^compression_unit) and - it represents the logically allocated space - rather than the actual on disk usage. For this - use the compressed_size (see below). */ - /* 48*/ s64 data_size; /* Byte size of the attribute - value. Can be larger than allocated_size if - attribute value is compressed or sparse. */ - /* 56*/ s64 initialized_size; /* Byte size of initialized - portion of the attribute value. Usually equals - data_size. */ - /* 64 */ void *non_resident_end[0]; /* Use offsetof(ATTR_RECORD, - non_resident_end) to get - size of a non resident - attribute. */ - /* sizeof(uncompressed attr) = 64*/ - /* 64*/ s64 compressed_size; /* Byte size of the attribute - value after compression. Only present when - compressed. Always is a multiple of the - cluster size. Represents the actual amount of - disk space being used on the disk. */ - /* 72 */ void *compressed_end[0]; - /* Use offsetof(ATTR_RECORD, compressed_end) to - get size of a compressed attribute. */ - /* sizeof(compressed attr) = 72*/ - } __attribute__( ( __packed__ ) ); - } __attribute__( ( __packed__ ) ); -} __attribute__( ( __packed__ ) ) ATTR_RECORD; +typedef struct { +/*Ofs*/ +/* 0*/ ATTR_TYPES type; /* The (32-bit) type of the attribute. */ +/* 4*/ u32 length; /* Byte size of the resident part of the + attribute (aligned to 8-byte boundary). + Used to get to the next attribute. */ +/* 8*/ u8 non_resident; /* If 0, attribute is resident. + If 1, attribute is non-resident. */ +/* 9*/ u8 name_length; /* Unicode character size of name of attribute. + 0 if unnamed. */ +/* 10*/ u16 name_offset; /* If name_length != 0, the byte offset to the + beginning of the name from the attribute + record. Note that the name is stored as a + Unicode string. When creating, place offset + just at the end of the record header. Then, + follow with attribute value or mapping pairs + array, resident and non-resident attributes + respectively, aligning to an 8-byte + boundary. */ +/* 12*/ ATTR_FLAGS flags; /* Flags describing the attribute. */ +/* 14*/ u16 instance; /* The instance of this attribute record. This + number is unique within this mft record (see + MFT_RECORD/next_attribute_instance notes + above for more details). */ +/* 16*/ union { + /* Resident attributes. */ + struct { +/* 16 */ u32 value_length; /* Byte size of attribute value. */ +/* 20 */ u16 value_offset; /* Byte offset of the attribute + value from the start of the + attribute record. When creating, + align to 8-byte boundary if we + have a name present as this might + not have a length of a multiple + of 8-bytes. */ +/* 22 */ RESIDENT_ATTR_FLAGS resident_flags; /* See above. */ +/* 23 */ s8 reservedR; /* Reserved/alignment to 8-byte + boundary. */ +/* 24 */ void *resident_end[0]; /* Use offsetof(ATTR_RECORD, + resident_end) to get size of + a resident attribute. */ + } __attribute__((__packed__)); + /* Non-resident attributes. */ + struct { +/* 16*/ VCN lowest_vcn; /* Lowest valid virtual cluster number + for this portion of the attribute value or + 0 if this is the only extent (usually the + case). - Only when an attribute list is used + does lowest_vcn != 0 ever occur. */ +/* 24*/ VCN highest_vcn; /* Highest valid vcn of this extent of + the attribute value. - Usually there is only one + portion, so this usually equals the attribute + value size in clusters minus 1. Can be -1 for + zero length files. Can be 0 for "single extent" + attributes. */ +/* 32*/ u16 mapping_pairs_offset; /* Byte offset from the + beginning of the structure to the mapping pairs + array which contains the mappings between the + vcns and the logical cluster numbers (lcns). + When creating, place this at the end of this + record header aligned to 8-byte boundary. */ +/* 34*/ u8 compression_unit; /* The compression unit expressed + as the log to the base 2 of the number of + clusters in a compression unit. 0 means not + compressed. (This effectively limits the + compression unit size to be a power of two + clusters.) WinNT4 only uses a value of 4. */ +/* 35*/ u8 reserved1[5]; /* Align to 8-byte boundary. */ +/* The sizes below are only used when lowest_vcn is zero, as otherwise it would + be difficult to keep them up-to-date.*/ +/* 40*/ s64 allocated_size; /* Byte size of disk space + allocated to hold the attribute value. Always + is a multiple of the cluster size. When a file + is compressed, this field is a multiple of the + compression block size (2^compression_unit) and + it represents the logically allocated space + rather than the actual on disk usage. For this + use the compressed_size (see below). */ +/* 48*/ s64 data_size; /* Byte size of the attribute + value. Can be larger than allocated_size if + attribute value is compressed or sparse. */ +/* 56*/ s64 initialized_size; /* Byte size of initialized + portion of the attribute value. Usually equals + data_size. */ +/* 64 */ void *non_resident_end[0]; /* Use offsetof(ATTR_RECORD, + non_resident_end) to get + size of a non resident + attribute. */ +/* sizeof(uncompressed attr) = 64*/ +/* 64*/ s64 compressed_size; /* Byte size of the attribute + value after compression. Only present when + compressed. Always is a multiple of the + cluster size. Represents the actual amount of + disk space being used on the disk. */ +/* 72 */ void *compressed_end[0]; + /* Use offsetof(ATTR_RECORD, compressed_end) to + get size of a compressed attribute. */ +/* sizeof(compressed attr) = 72*/ + } __attribute__((__packed__)); + } __attribute__((__packed__)); +} __attribute__((__packed__)) ATTR_RECORD; typedef ATTR_RECORD ATTR_REC; /** * enum FILE_ATTR_FLAGS - File attribute flags (32-bit). */ -typedef enum -{ - /* - * These flags are only present in the STANDARD_INFORMATION attribute - * (in the field file_attributes). - */ - FILE_ATTR_READONLY = const_cpu_to_le32( 0x00000001 ), - FILE_ATTR_HIDDEN = const_cpu_to_le32( 0x00000002 ), - FILE_ATTR_SYSTEM = const_cpu_to_le32( 0x00000004 ), - /* Old DOS volid. Unused in NT. = cpu_to_le32(0x00000008), */ +typedef enum { + /* + * These flags are only present in the STANDARD_INFORMATION attribute + * (in the field file_attributes). + */ + FILE_ATTR_READONLY = const_cpu_to_le32(0x00000001), + FILE_ATTR_HIDDEN = const_cpu_to_le32(0x00000002), + FILE_ATTR_SYSTEM = const_cpu_to_le32(0x00000004), + /* Old DOS volid. Unused in NT. = cpu_to_le32(0x00000008), */ - FILE_ATTR_DIRECTORY = const_cpu_to_le32( 0x00000010 ), - /* FILE_ATTR_DIRECTORY is not considered valid in NT. It is reserved - for the DOS SUBDIRECTORY flag. */ - FILE_ATTR_ARCHIVE = const_cpu_to_le32( 0x00000020 ), - FILE_ATTR_DEVICE = const_cpu_to_le32( 0x00000040 ), - FILE_ATTR_NORMAL = const_cpu_to_le32( 0x00000080 ), + FILE_ATTR_DIRECTORY = const_cpu_to_le32(0x00000010), + /* FILE_ATTR_DIRECTORY is not considered valid in NT. It is reserved + for the DOS SUBDIRECTORY flag. */ + FILE_ATTR_ARCHIVE = const_cpu_to_le32(0x00000020), + FILE_ATTR_DEVICE = const_cpu_to_le32(0x00000040), + FILE_ATTR_NORMAL = const_cpu_to_le32(0x00000080), - FILE_ATTR_TEMPORARY = const_cpu_to_le32( 0x00000100 ), - FILE_ATTR_SPARSE_FILE = const_cpu_to_le32( 0x00000200 ), - FILE_ATTR_REPARSE_POINT = const_cpu_to_le32( 0x00000400 ), - FILE_ATTR_COMPRESSED = const_cpu_to_le32( 0x00000800 ), + FILE_ATTR_TEMPORARY = const_cpu_to_le32(0x00000100), + FILE_ATTR_SPARSE_FILE = const_cpu_to_le32(0x00000200), + FILE_ATTR_REPARSE_POINT = const_cpu_to_le32(0x00000400), + FILE_ATTR_COMPRESSED = const_cpu_to_le32(0x00000800), - FILE_ATTR_OFFLINE = const_cpu_to_le32( 0x00001000 ), - FILE_ATTR_NOT_CONTENT_INDEXED = const_cpu_to_le32( 0x00002000 ), - FILE_ATTR_ENCRYPTED = const_cpu_to_le32( 0x00004000 ), + FILE_ATTR_OFFLINE = const_cpu_to_le32(0x00001000), + FILE_ATTR_NOT_CONTENT_INDEXED = const_cpu_to_le32(0x00002000), + FILE_ATTR_ENCRYPTED = const_cpu_to_le32(0x00004000), - FILE_ATTR_VALID_FLAGS = const_cpu_to_le32( 0x00007fb7 ), - /* FILE_ATTR_VALID_FLAGS masks out the old DOS VolId and the - FILE_ATTR_DEVICE and preserves everything else. This mask - is used to obtain all flags that are valid for reading. */ - FILE_ATTR_VALID_SET_FLAGS = const_cpu_to_le32( 0x000031a7 ), - /* FILE_ATTR_VALID_SET_FLAGS masks out the old DOS VolId, the - FILE_ATTR_DEVICE, FILE_ATTR_DIRECTORY, FILE_ATTR_SPARSE_FILE, - FILE_ATTR_REPARSE_POINT, FILE_ATRE_COMPRESSED and FILE_ATTR_ENCRYPTED - and preserves the rest. This mask is used to to obtain all flags that - are valid for setting. */ + FILE_ATTR_VALID_FLAGS = const_cpu_to_le32(0x00007fb7), + /* FILE_ATTR_VALID_FLAGS masks out the old DOS VolId and the + FILE_ATTR_DEVICE and preserves everything else. This mask + is used to obtain all flags that are valid for reading. */ + FILE_ATTR_VALID_SET_FLAGS = const_cpu_to_le32(0x000031a7), + /* FILE_ATTR_VALID_SET_FLAGS masks out the old DOS VolId, the + FILE_ATTR_DEVICE, FILE_ATTR_DIRECTORY, FILE_ATTR_SPARSE_FILE, + FILE_ATTR_REPARSE_POINT, FILE_ATRE_COMPRESSED and FILE_ATTR_ENCRYPTED + and preserves the rest. This mask is used to to obtain all flags that + are valid for setting. */ - /** - * FILE_ATTR_I30_INDEX_PRESENT - Is it a directory? - * - * This is a copy of the MFT_RECORD_IS_DIRECTORY bit from the mft - * record, telling us whether this is a directory or not, i.e. whether - * it has an index root attribute named "$I30" or not. - * - * This flag is only present in the FILE_NAME attribute (in the - * file_attributes field). - */ - FILE_ATTR_I30_INDEX_PRESENT = const_cpu_to_le32( 0x10000000 ), - - /** - * FILE_ATTR_VIEW_INDEX_PRESENT - Does have a non-directory index? - * - * This is a copy of the MFT_RECORD_IS_VIEW_INDEX bit from the mft - * record, telling us whether this file has a view index present (eg. - * object id index, quota index, one of the security indexes and the - * reparse points index). - * - * This flag is only present in the $STANDARD_INFORMATION and - * $FILE_NAME attributes. - */ - FILE_ATTR_VIEW_INDEX_PRESENT = const_cpu_to_le32( 0x20000000 ), -} __attribute__( ( __packed__ ) ) FILE_ATTR_FLAGS; + /** + * FILE_ATTR_I30_INDEX_PRESENT - Is it a directory? + * + * This is a copy of the MFT_RECORD_IS_DIRECTORY bit from the mft + * record, telling us whether this is a directory or not, i.e. whether + * it has an index root attribute named "$I30" or not. + * + * This flag is only present in the FILE_NAME attribute (in the + * file_attributes field). + */ + FILE_ATTR_I30_INDEX_PRESENT = const_cpu_to_le32(0x10000000), + + /** + * FILE_ATTR_VIEW_INDEX_PRESENT - Does have a non-directory index? + * + * This is a copy of the MFT_RECORD_IS_VIEW_INDEX bit from the mft + * record, telling us whether this file has a view index present (eg. + * object id index, quota index, one of the security indexes and the + * reparse points index). + * + * This flag is only present in the $STANDARD_INFORMATION and + * $FILE_NAME attributes. + */ + FILE_ATTR_VIEW_INDEX_PRESENT = const_cpu_to_le32(0x20000000), +} __attribute__((__packed__)) FILE_ATTR_FLAGS; /* * NOTE on times in NTFS: All times are in MS standard time format, i.e. they @@ -924,98 +901,91 @@ typedef enum * NOTE: Always resident. * NOTE: Present in all base file records on a volume. * NOTE: There is conflicting information about the meaning of each of the time - * fields but the meaning as defined below has been verified to be - * correct by practical experimentation on Windows NT4 SP6a and is hence - * assumed to be the one and only correct interpretation. + * fields but the meaning as defined below has been verified to be + * correct by practical experimentation on Windows NT4 SP6a and is hence + * assumed to be the one and only correct interpretation. */ -typedef struct -{ - /*Ofs*/ - /* 0*/ - s64 creation_time; /* Time file was created. Updated when - a filename is changed(?). */ - /* 8*/ s64 last_data_change_time; /* Time the data attribute was last - modified. */ - /* 16*/ s64 last_mft_change_time; /* Time this mft record was last - modified. */ - /* 24*/ s64 last_access_time; /* Approximate time when the file was - last accessed (obviously this is not - updated on read-only volumes). In - Windows this is only updated when - accessed if some time delta has - passed since the last update. Also, - last access times updates can be - disabled altogether for speed. */ - /* 32*/ FILE_ATTR_FLAGS file_attributes; /* Flags describing the file. */ - /* 36*/ union - { - /* NTFS 1.2 (and previous, presumably) */ - struct - { - /* 36 */ - u8 reserved12[12]; /* Reserved/alignment to 8-byte - boundary. */ - /* 48 */ void *v1_end[0]; /* Marker for offsetof(). */ - } __attribute__( ( __packed__ ) ); - /* sizeof() = 48 bytes */ - /* NTFS 3.0 */ - struct - { - /* - * If a volume has been upgraded from a previous NTFS version, then these - * fields are present only if the file has been accessed since the upgrade. - * Recognize the difference by comparing the length of the resident attribute - * value. If it is 48, then the following fields are missing. If it is 72 then - * the fields are present. Maybe just check like this: - * if (resident.ValueLength < sizeof(STANDARD_INFORMATION)) { - * Assume NTFS 1.2- format. - * If (volume version is 3.0+) - * Upgrade attribute to NTFS 3.0 format. - * else - * Use NTFS 1.2- format for access. - * } else - * Use NTFS 3.0 format for access. - * Only problem is that it might be legal to set the length of the value to - * arbitrarily large values thus spoiling this check. - But chkdsk probably - * views that as a corruption, assuming that it behaves like this for all - * attributes. - */ - /* 36*/ - u32 maximum_versions; /* Maximum allowed versions for - file. Zero if version numbering is disabled. */ - /* 40*/ u32 version_number; /* This file's version (if any). - Set to zero if maximum_versions is zero. */ - /* 44*/ u32 class_id; /* Class id from bidirectional - class id index (?). */ - /* 48*/ u32 owner_id; /* Owner_id of the user owning - the file. Translate via $Q index in FILE_Extend - /$Quota to the quota control entry for the user - owning the file. Zero if quotas are disabled. */ - /* 52*/ u32 security_id; /* Security_id for the file. - Translate via $SII index and $SDS data stream - in FILE_Secure to the security descriptor. */ - /* 56*/ u64 quota_charged; /* Byte size of the charge to - the quota for all streams of the file. Note: Is - zero if quotas are disabled. */ - /* 64*/ u64 usn; /* Last update sequence number - of the file. This is a direct index into the - change (aka usn) journal file. It is zero if - the usn journal is disabled. - NOTE: To disable the journal need to delete - the journal file itself and to then walk the - whole mft and set all Usn entries in all mft - records to zero! (This can take a while!) - The journal is FILE_Extend/$UsnJrnl. Win2k - will recreate the journal and initiate - logging if necessary when mounting the - partition. This, in contrast to disabling the - journal is a very fast process, so the user - won't even notice it. */ - /* 72*/ void *v3_end[0]; /* Marker for offsetof(). */ - } __attribute__( ( __packed__ ) ); - } __attribute__( ( __packed__ ) ); - /* sizeof() = 72 bytes (NTFS 3.0) */ -} __attribute__( ( __packed__ ) ) STANDARD_INFORMATION; +typedef struct { +/*Ofs*/ +/* 0*/ s64 creation_time; /* Time file was created. Updated when + a filename is changed(?). */ +/* 8*/ s64 last_data_change_time; /* Time the data attribute was last + modified. */ +/* 16*/ s64 last_mft_change_time; /* Time this mft record was last + modified. */ +/* 24*/ s64 last_access_time; /* Approximate time when the file was + last accessed (obviously this is not + updated on read-only volumes). In + Windows this is only updated when + accessed if some time delta has + passed since the last update. Also, + last access times updates can be + disabled altogether for speed. */ +/* 32*/ FILE_ATTR_FLAGS file_attributes; /* Flags describing the file. */ +/* 36*/ union { + /* NTFS 1.2 (and previous, presumably) */ + struct { + /* 36 */ u8 reserved12[12]; /* Reserved/alignment to 8-byte + boundary. */ + /* 48 */ void *v1_end[0]; /* Marker for offsetof(). */ + } __attribute__((__packed__)); +/* sizeof() = 48 bytes */ + /* NTFS 3.0 */ + struct { +/* + * If a volume has been upgraded from a previous NTFS version, then these + * fields are present only if the file has been accessed since the upgrade. + * Recognize the difference by comparing the length of the resident attribute + * value. If it is 48, then the following fields are missing. If it is 72 then + * the fields are present. Maybe just check like this: + * if (resident.ValueLength < sizeof(STANDARD_INFORMATION)) { + * Assume NTFS 1.2- format. + * If (volume version is 3.0+) + * Upgrade attribute to NTFS 3.0 format. + * else + * Use NTFS 1.2- format for access. + * } else + * Use NTFS 3.0 format for access. + * Only problem is that it might be legal to set the length of the value to + * arbitrarily large values thus spoiling this check. - But chkdsk probably + * views that as a corruption, assuming that it behaves like this for all + * attributes. + */ + /* 36*/ u32 maximum_versions; /* Maximum allowed versions for + file. Zero if version numbering is disabled. */ + /* 40*/ u32 version_number; /* This file's version (if any). + Set to zero if maximum_versions is zero. */ + /* 44*/ u32 class_id; /* Class id from bidirectional + class id index (?). */ + /* 48*/ u32 owner_id; /* Owner_id of the user owning + the file. Translate via $Q index in FILE_Extend + /$Quota to the quota control entry for the user + owning the file. Zero if quotas are disabled. */ + /* 52*/ u32 security_id; /* Security_id for the file. + Translate via $SII index and $SDS data stream + in FILE_Secure to the security descriptor. */ + /* 56*/ u64 quota_charged; /* Byte size of the charge to + the quota for all streams of the file. Note: Is + zero if quotas are disabled. */ + /* 64*/ u64 usn; /* Last update sequence number + of the file. This is a direct index into the + change (aka usn) journal file. It is zero if + the usn journal is disabled. + NOTE: To disable the journal need to delete + the journal file itself and to then walk the + whole mft and set all Usn entries in all mft + records to zero! (This can take a while!) + The journal is FILE_Extend/$UsnJrnl. Win2k + will recreate the journal and initiate + logging if necessary when mounting the + partition. This, in contrast to disabling the + journal is a very fast process, so the user + won't even notice it. */ + /* 72*/ void *v3_end[0]; /* Marker for offsetof(). */ + } __attribute__((__packed__)); + } __attribute__((__packed__)); +/* sizeof() = 72 bytes (NTFS 3.0) */ +} __attribute__((__packed__)) STANDARD_INFORMATION; /** * struct ATTR_LIST_ENTRY - Attribute: Attribute list (0x20). @@ -1031,147 +1001,138 @@ typedef struct * extent. They are ordered by lowest_vcn and have their instance set to zero. * It is not allowed to have two attributes with all sorting keys equal. * - Further restrictions: - * - If not resident, the vcn to lcn mapping array has to fit inside the - * base mft record. - * - The attribute list attribute value has a maximum size of 256kb. This - * is imposed by the Windows cache manager. + * - If not resident, the vcn to lcn mapping array has to fit inside the + * base mft record. + * - The attribute list attribute value has a maximum size of 256kb. This + * is imposed by the Windows cache manager. * - Attribute lists are only used when the attributes of mft record do not * fit inside the mft record despite all attributes (that can be made * non-resident) having been made non-resident. This can happen e.g. when: - * - File has a large number of hard links (lots of file name - * attributes present). - * - The mapping pairs array of some non-resident attribute becomes so - * large due to fragmentation that it overflows the mft record. - * - The security descriptor is very complex (not applicable to - * NTFS 3.0 volumes). - * - There are many named streams. + * - File has a large number of hard links (lots of file name + * attributes present). + * - The mapping pairs array of some non-resident attribute becomes so + * large due to fragmentation that it overflows the mft record. + * - The security descriptor is very complex (not applicable to + * NTFS 3.0 volumes). + * - There are many named streams. */ -typedef struct -{ - /*Ofs*/ - /* 0*/ - ATTR_TYPES type; /* Type of referenced attribute. */ - /* 4*/ u16 length; /* Byte size of this entry. */ - /* 6*/ u8 name_length; /* Size in Unicode chars of the name of the - attribute or 0 if unnamed. */ - /* 7*/ u8 name_offset; /* Byte offset to beginning of attribute name - (always set this to where the name would - start even if unnamed). */ - /* 8*/ VCN lowest_vcn; /* Lowest virtual cluster number of this portion - of the attribute value. This is usually 0. It - is non-zero for the case where one attribute - does not fit into one mft record and thus - several mft records are allocated to hold - this attribute. In the latter case, each mft - record holds one extent of the attribute and - there is one attribute list entry for each - extent. NOTE: This is DEFINITELY a signed - value! The windows driver uses cmp, followed - by jg when comparing this, thus it treats it - as signed. */ - /* 16*/ MFT_REF mft_reference; /* The reference of the mft record holding - the ATTR_RECORD for this portion of the - attribute value. */ - /* 24*/ u16 instance; /* If lowest_vcn = 0, the instance of the - attribute being referenced; otherwise 0. */ - /* 26*/ ntfschar name[0]; /* Use when creating only. When reading use - name_offset to determine the location of the - name. */ - /* sizeof() = 26 + (attribute_name_length * 2) bytes */ -} __attribute__( ( __packed__ ) ) ATTR_LIST_ENTRY; +typedef struct { +/*Ofs*/ +/* 0*/ ATTR_TYPES type; /* Type of referenced attribute. */ +/* 4*/ u16 length; /* Byte size of this entry. */ +/* 6*/ u8 name_length; /* Size in Unicode chars of the name of the + attribute or 0 if unnamed. */ +/* 7*/ u8 name_offset; /* Byte offset to beginning of attribute name + (always set this to where the name would + start even if unnamed). */ +/* 8*/ VCN lowest_vcn; /* Lowest virtual cluster number of this portion + of the attribute value. This is usually 0. It + is non-zero for the case where one attribute + does not fit into one mft record and thus + several mft records are allocated to hold + this attribute. In the latter case, each mft + record holds one extent of the attribute and + there is one attribute list entry for each + extent. NOTE: This is DEFINITELY a signed + value! The windows driver uses cmp, followed + by jg when comparing this, thus it treats it + as signed. */ +/* 16*/ MFT_REF mft_reference; /* The reference of the mft record holding + the ATTR_RECORD for this portion of the + attribute value. */ +/* 24*/ u16 instance; /* If lowest_vcn = 0, the instance of the + attribute being referenced; otherwise 0. */ +/* 26*/ ntfschar name[0]; /* Use when creating only. When reading use + name_offset to determine the location of the + name. */ +/* sizeof() = 26 + (attribute_name_length * 2) bytes */ +} __attribute__((__packed__)) ATTR_LIST_ENTRY; /* * The maximum allowed length for a file name. */ -#define NTFS_MAX_NAME_LEN 255 +#define NTFS_MAX_NAME_LEN 255 /** * enum FILE_NAME_TYPE_FLAGS - Possible namespaces for filenames in ntfs. * (8-bit). */ -typedef enum -{ - FILE_NAME_POSIX = 0x00, - /* This is the largest namespace. It is case sensitive and - allows all Unicode characters except for: '\0' and '/'. - Beware that in WinNT/2k files which eg have the same name - except for their case will not be distinguished by the - standard utilities and thus a "del filename" will delete - both "filename" and "fileName" without warning. */ - FILE_NAME_WIN32 = 0x01, - /* The standard WinNT/2k NTFS long filenames. Case insensitive. - All Unicode chars except: '\0', '"', '*', '/', ':', '<', - '>', '?', '\' and '|'. Further, names cannot end with a '.' - or a space. */ - FILE_NAME_DOS = 0x02, - /* The standard DOS filenames (8.3 format). Uppercase only. - All 8-bit characters greater space, except: '"', '*', '+', - ',', '/', ':', ';', '<', '=', '>', '?' and '\'. */ - FILE_NAME_WIN32_AND_DOS = 0x03, - /* 3 means that both the Win32 and the DOS filenames are - identical and hence have been saved in this single filename - record. */ -} __attribute__( ( __packed__ ) ) FILE_NAME_TYPE_FLAGS; +typedef enum { + FILE_NAME_POSIX = 0x00, + /* This is the largest namespace. It is case sensitive and + allows all Unicode characters except for: '\0' and '/'. + Beware that in WinNT/2k files which eg have the same name + except for their case will not be distinguished by the + standard utilities and thus a "del filename" will delete + both "filename" and "fileName" without warning. */ + FILE_NAME_WIN32 = 0x01, + /* The standard WinNT/2k NTFS long filenames. Case insensitive. + All Unicode chars except: '\0', '"', '*', '/', ':', '<', + '>', '?', '\' and '|'. Further, names cannot end with a '.' + or a space. */ + FILE_NAME_DOS = 0x02, + /* The standard DOS filenames (8.3 format). Uppercase only. + All 8-bit characters greater space, except: '"', '*', '+', + ',', '/', ':', ';', '<', '=', '>', '?' and '\'. */ + FILE_NAME_WIN32_AND_DOS = 0x03, + /* 3 means that both the Win32 and the DOS filenames are + identical and hence have been saved in this single filename + record. */ +} __attribute__((__packed__)) FILE_NAME_TYPE_FLAGS; /** * struct FILE_NAME_ATTR - Attribute: Filename (0x30). * * NOTE: Always resident. * NOTE: All fields, except the parent_directory, are only updated when the - * filename is changed. Until then, they just become out of sync with - * reality and the more up to date values are present in the standard - * information attribute. + * filename is changed. Until then, they just become out of sync with + * reality and the more up to date values are present in the standard + * information attribute. * NOTE: There is conflicting information about the meaning of each of the time - * fields but the meaning as defined below has been verified to be - * correct by practical experimentation on Windows NT4 SP6a and is hence - * assumed to be the one and only correct interpretation. + * fields but the meaning as defined below has been verified to be + * correct by practical experimentation on Windows NT4 SP6a and is hence + * assumed to be the one and only correct interpretation. */ -typedef struct -{ - /*hex ofs*/ - /* 0*/ - MFT_REF parent_directory; /* Directory this filename is - referenced from. */ - /* 8*/ s64 creation_time; /* Time file was created. */ - /* 10*/ s64 last_data_change_time; /* Time the data attribute was last - modified. */ - /* 18*/ s64 last_mft_change_time; /* Time this mft record was last - modified. */ - /* 20*/ s64 last_access_time; /* Last time this mft record was - accessed. */ - /* 28*/ s64 allocated_size; /* Byte size of on-disk allocated space - for the data attribute. So for - normal $DATA, this is the - allocated_size from the unnamed - $DATA attribute and for compressed - and/or sparse $DATA, this is the - compressed_size from the unnamed - $DATA attribute. NOTE: This is a - multiple of the cluster size. */ - /* 30*/ s64 data_size; /* Byte size of actual data in data - attribute. */ - /* 38*/ FILE_ATTR_FLAGS file_attributes; /* Flags describing the file. */ - /* 3c*/ union - { - /* 3c*/ - struct - { - /* 3c*/ - u16 packed_ea_size; /* Size of the buffer needed to - pack the extended attributes - (EAs), if such are present.*/ - /* 3e*/ u16 reserved; /* Reserved for alignment. */ - } __attribute__( ( __packed__ ) ); - /* 3c*/ u32 reparse_point_tag; /* Type of reparse point, - present only in reparse - points and only if there are - no EAs. */ - } __attribute__( ( __packed__ ) ); - /* 40*/ u8 file_name_length; /* Length of file name in - (Unicode) characters. */ - /* 41*/ FILE_NAME_TYPE_FLAGS file_name_type; /* Namespace of the file name.*/ - /* 42*/ ntfschar file_name[0]; /* File name in Unicode. */ -} __attribute__( ( __packed__ ) ) FILE_NAME_ATTR; +typedef struct { +/*hex ofs*/ +/* 0*/ MFT_REF parent_directory; /* Directory this filename is + referenced from. */ +/* 8*/ s64 creation_time; /* Time file was created. */ +/* 10*/ s64 last_data_change_time; /* Time the data attribute was last + modified. */ +/* 18*/ s64 last_mft_change_time; /* Time this mft record was last + modified. */ +/* 20*/ s64 last_access_time; /* Last time this mft record was + accessed. */ +/* 28*/ s64 allocated_size; /* Byte size of on-disk allocated space + for the data attribute. So for + normal $DATA, this is the + allocated_size from the unnamed + $DATA attribute and for compressed + and/or sparse $DATA, this is the + compressed_size from the unnamed + $DATA attribute. NOTE: This is a + multiple of the cluster size. */ +/* 30*/ s64 data_size; /* Byte size of actual data in data + attribute. */ +/* 38*/ FILE_ATTR_FLAGS file_attributes; /* Flags describing the file. */ +/* 3c*/ union { + /* 3c*/ struct { + /* 3c*/ u16 packed_ea_size; /* Size of the buffer needed to + pack the extended attributes + (EAs), if such are present.*/ + /* 3e*/ u16 reserved; /* Reserved for alignment. */ + } __attribute__((__packed__)); + /* 3c*/ u32 reparse_point_tag; /* Type of reparse point, + present only in reparse + points and only if there are + no EAs. */ + } __attribute__((__packed__)); +/* 40*/ u8 file_name_length; /* Length of file name in + (Unicode) characters. */ +/* 41*/ FILE_NAME_TYPE_FLAGS file_name_type; /* Namespace of the file name.*/ +/* 42*/ ntfschar file_name[0]; /* File name in Unicode. */ +} __attribute__((__packed__)) FILE_NAME_ATTR; /** * struct GUID - GUID structures store globally unique identifiers (GUID). @@ -1183,17 +1144,16 @@ typedef struct * unique identifier (UUID). * * Example of a GUID: - * 1F010768-5A73-BC91-0010-A52216A7227B + * 1F010768-5A73-BC91-0010-A52216A7227B */ -typedef struct -{ - u32 data1; /* The first eight hexadecimal digits of the GUID. */ - u16 data2; /* The first group of four hexadecimal digits. */ - u16 data3; /* The second group of four hexadecimal digits. */ - u8 data4[8]; /* The first two bytes are the third group of four - hexadecimal digits. The remaining six bytes are the - final 12 hexadecimal digits. */ -} __attribute__( ( __packed__ ) ) GUID; +typedef struct { + u32 data1; /* The first eight hexadecimal digits of the GUID. */ + u16 data2; /* The first group of four hexadecimal digits. */ + u16 data3; /* The second group of four hexadecimal digits. */ + u8 data4[8]; /* The first two bytes are the third group of four + hexadecimal digits. The remaining six bytes are the + final 12 hexadecimal digits. */ +} __attribute__((__packed__)) GUID; /** * struct OBJ_ID_INDEX_DATA - FILE_Extend/$ObjId contains an index named $O. @@ -1202,57 +1162,51 @@ typedef struct * and the corresponding mft_record numbers as the index entry data parts. * * The data part (defined below) also contains three other object_ids: - * birth_volume_id - object_id of FILE_Volume on which the file was first - * created. Optional (i.e. can be zero). - * birth_object_id - object_id of file when it was first created. Usually - * equals the object_id. Optional (i.e. can be zero). - * domain_id - Reserved (always zero). + * birth_volume_id - object_id of FILE_Volume on which the file was first + * created. Optional (i.e. can be zero). + * birth_object_id - object_id of file when it was first created. Usually + * equals the object_id. Optional (i.e. can be zero). + * domain_id - Reserved (always zero). */ -typedef struct -{ - MFT_REF mft_reference; /* Mft record containing the object_id in - the index entry key. */ - union - { - struct - { - GUID birth_volume_id; - GUID birth_object_id; - GUID domain_id; - } __attribute__( ( __packed__ ) ); - u8 extended_info[48]; - } __attribute__( ( __packed__ ) ); -} __attribute__( ( __packed__ ) ) OBJ_ID_INDEX_DATA; +typedef struct { + MFT_REF mft_reference; /* Mft record containing the object_id in + the index entry key. */ + union { + struct { + GUID birth_volume_id; + GUID birth_object_id; + GUID domain_id; + } __attribute__((__packed__)); + u8 extended_info[48]; + } __attribute__((__packed__)); +} __attribute__((__packed__)) OBJ_ID_INDEX_DATA; /** * struct OBJECT_ID_ATTR - Attribute: Object id (NTFS 3.0+) (0x40). * * NOTE: Always resident. */ -typedef struct -{ - GUID object_id; /* Unique id assigned to the - file.*/ - /* The following fields are optional. The attribute value size is 16 - bytes, i.e. sizeof(GUID), if these are not present at all. Note, - the entries can be present but one or more (or all) can be zero - meaning that that particular value(s) is(are) not defined. Note, - when the fields are missing here, it is well possible that they are - to be found within the $Extend/$ObjId system file indexed under the - above object_id. */ - union - { - struct - { - GUID birth_volume_id; /* Unique id of volume on which - the file was first created.*/ - GUID birth_object_id; /* Unique id of file when it was - first created. */ - GUID domain_id; /* Reserved, zero. */ - } __attribute__( ( __packed__ ) ); - u8 extended_info[48]; - } __attribute__( ( __packed__ ) ); -} __attribute__( ( __packed__ ) ) OBJECT_ID_ATTR; +typedef struct { + GUID object_id; /* Unique id assigned to the + file.*/ + /* The following fields are optional. The attribute value size is 16 + bytes, i.e. sizeof(GUID), if these are not present at all. Note, + the entries can be present but one or more (or all) can be zero + meaning that that particular value(s) is(are) not defined. Note, + when the fields are missing here, it is well possible that they are + to be found within the $Extend/$ObjId system file indexed under the + above object_id. */ + union { + struct { + GUID birth_volume_id; /* Unique id of volume on which + the file was first created.*/ + GUID birth_object_id; /* Unique id of file when it was + first created. */ + GUID domain_id; /* Reserved, zero. */ + } __attribute__((__packed__)); + u8 extended_info[48]; + } __attribute__((__packed__)); +} __attribute__((__packed__)) OBJECT_ID_ATTR; #if 0 /** @@ -1261,15 +1215,14 @@ typedef struct * The pre-defined IDENTIFIER_AUTHORITIES used as SID_IDENTIFIER_AUTHORITY in * the SID structure (see below). */ -typedef enum /* SID string prefix. */ -{ - SECURITY_NULL_SID_AUTHORITY = {0, 0, 0, 0, 0, 0}, /* S-1-0 */ - SECURITY_WORLD_SID_AUTHORITY = {0, 0, 0, 0, 0, 1}, /* S-1-1 */ - SECURITY_LOCAL_SID_AUTHORITY = {0, 0, 0, 0, 0, 2}, /* S-1-2 */ - SECURITY_CREATOR_SID_AUTHORITY = {0, 0, 0, 0, 0, 3}, /* S-1-3 */ - SECURITY_NON_UNIQUE_AUTHORITY = {0, 0, 0, 0, 0, 4}, /* S-1-4 */ - SECURITY_NT_SID_AUTHORITY = {0, 0, 0, 0, 0, 5}, /* S-1-5 */ - } IDENTIFIER_AUTHORITIES; +typedef enum { /* SID string prefix. */ + SECURITY_NULL_SID_AUTHORITY = {0, 0, 0, 0, 0, 0}, /* S-1-0 */ + SECURITY_WORLD_SID_AUTHORITY = {0, 0, 0, 0, 0, 1}, /* S-1-1 */ + SECURITY_LOCAL_SID_AUTHORITY = {0, 0, 0, 0, 0, 2}, /* S-1-2 */ + SECURITY_CREATOR_SID_AUTHORITY = {0, 0, 0, 0, 0, 3}, /* S-1-3 */ + SECURITY_NON_UNIQUE_AUTHORITY = {0, 0, 0, 0, 0, 4}, /* S-1-4 */ + SECURITY_NT_SID_AUTHORITY = {0, 0, 0, 0, 0, 5}, /* S-1-5 */ +} IDENTIFIER_AUTHORITIES; #endif /** @@ -1284,112 +1237,111 @@ typedef enum /* SID string prefix. */ * made up of the identifier authority SECURITY_CREATOR_SID_AUTHORITY (3) and * the relative identifier SECURITY_CREATOR_OWNER_RID (0). */ -typedef enum /* Identifier authority. */ -{ - SECURITY_NULL_RID = 0, /* S-1-0 */ - SECURITY_WORLD_RID = 0, /* S-1-1 */ - SECURITY_LOCAL_RID = 0, /* S-1-2 */ +typedef enum { /* Identifier authority. */ + SECURITY_NULL_RID = 0, /* S-1-0 */ + SECURITY_WORLD_RID = 0, /* S-1-1 */ + SECURITY_LOCAL_RID = 0, /* S-1-2 */ - SECURITY_CREATOR_OWNER_RID = 0, /* S-1-3 */ - SECURITY_CREATOR_GROUP_RID = 1, /* S-1-3 */ + SECURITY_CREATOR_OWNER_RID = 0, /* S-1-3 */ + SECURITY_CREATOR_GROUP_RID = 1, /* S-1-3 */ - SECURITY_CREATOR_OWNER_SERVER_RID = 2, /* S-1-3 */ - SECURITY_CREATOR_GROUP_SERVER_RID = 3, /* S-1-3 */ + SECURITY_CREATOR_OWNER_SERVER_RID = 2, /* S-1-3 */ + SECURITY_CREATOR_GROUP_SERVER_RID = 3, /* S-1-3 */ - SECURITY_DIALUP_RID = 1, - SECURITY_NETWORK_RID = 2, - SECURITY_BATCH_RID = 3, - SECURITY_INTERACTIVE_RID = 4, - SECURITY_SERVICE_RID = 6, - SECURITY_ANONYMOUS_LOGON_RID = 7, - SECURITY_PROXY_RID = 8, - SECURITY_ENTERPRISE_CONTROLLERS_RID = 9, - SECURITY_SERVER_LOGON_RID = 9, - SECURITY_PRINCIPAL_SELF_RID = 0xa, - SECURITY_AUTHENTICATED_USER_RID = 0xb, - SECURITY_RESTRICTED_CODE_RID = 0xc, - SECURITY_TERMINAL_SERVER_RID = 0xd, + SECURITY_DIALUP_RID = 1, + SECURITY_NETWORK_RID = 2, + SECURITY_BATCH_RID = 3, + SECURITY_INTERACTIVE_RID = 4, + SECURITY_SERVICE_RID = 6, + SECURITY_ANONYMOUS_LOGON_RID = 7, + SECURITY_PROXY_RID = 8, + SECURITY_ENTERPRISE_CONTROLLERS_RID=9, + SECURITY_SERVER_LOGON_RID = 9, + SECURITY_PRINCIPAL_SELF_RID = 0xa, + SECURITY_AUTHENTICATED_USER_RID = 0xb, + SECURITY_RESTRICTED_CODE_RID = 0xc, + SECURITY_TERMINAL_SERVER_RID = 0xd, - SECURITY_LOGON_IDS_RID = 5, - SECURITY_LOGON_IDS_RID_COUNT = 3, + SECURITY_LOGON_IDS_RID = 5, + SECURITY_LOGON_IDS_RID_COUNT = 3, - SECURITY_LOCAL_SYSTEM_RID = 0x12, + SECURITY_LOCAL_SYSTEM_RID = 0x12, - SECURITY_NT_NON_UNIQUE = 0x15, + SECURITY_NT_NON_UNIQUE = 0x15, - SECURITY_BUILTIN_DOMAIN_RID = 0x20, + SECURITY_BUILTIN_DOMAIN_RID = 0x20, - /* - * Well-known domain relative sub-authority values (RIDs). - */ + /* + * Well-known domain relative sub-authority values (RIDs). + */ - /* Users. */ - DOMAIN_USER_RID_ADMIN = 0x1f4, - DOMAIN_USER_RID_GUEST = 0x1f5, - DOMAIN_USER_RID_KRBTGT = 0x1f6, + /* Users. */ + DOMAIN_USER_RID_ADMIN = 0x1f4, + DOMAIN_USER_RID_GUEST = 0x1f5, + DOMAIN_USER_RID_KRBTGT = 0x1f6, - /* Groups. */ - DOMAIN_GROUP_RID_ADMINS = 0x200, - DOMAIN_GROUP_RID_USERS = 0x201, - DOMAIN_GROUP_RID_GUESTS = 0x202, - DOMAIN_GROUP_RID_COMPUTERS = 0x203, - DOMAIN_GROUP_RID_CONTROLLERS = 0x204, - DOMAIN_GROUP_RID_CERT_ADMINS = 0x205, - DOMAIN_GROUP_RID_SCHEMA_ADMINS = 0x206, - DOMAIN_GROUP_RID_ENTERPRISE_ADMINS = 0x207, - DOMAIN_GROUP_RID_POLICY_ADMINS = 0x208, + /* Groups. */ + DOMAIN_GROUP_RID_ADMINS = 0x200, + DOMAIN_GROUP_RID_USERS = 0x201, + DOMAIN_GROUP_RID_GUESTS = 0x202, + DOMAIN_GROUP_RID_COMPUTERS = 0x203, + DOMAIN_GROUP_RID_CONTROLLERS = 0x204, + DOMAIN_GROUP_RID_CERT_ADMINS = 0x205, + DOMAIN_GROUP_RID_SCHEMA_ADMINS = 0x206, + DOMAIN_GROUP_RID_ENTERPRISE_ADMINS= 0x207, + DOMAIN_GROUP_RID_POLICY_ADMINS = 0x208, - /* Aliases. */ - DOMAIN_ALIAS_RID_ADMINS = 0x220, - DOMAIN_ALIAS_RID_USERS = 0x221, - DOMAIN_ALIAS_RID_GUESTS = 0x222, - DOMAIN_ALIAS_RID_POWER_USERS = 0x223, + /* Aliases. */ + DOMAIN_ALIAS_RID_ADMINS = 0x220, + DOMAIN_ALIAS_RID_USERS = 0x221, + DOMAIN_ALIAS_RID_GUESTS = 0x222, + DOMAIN_ALIAS_RID_POWER_USERS = 0x223, - DOMAIN_ALIAS_RID_ACCOUNT_OPS = 0x224, - DOMAIN_ALIAS_RID_SYSTEM_OPS = 0x225, - DOMAIN_ALIAS_RID_PRINT_OPS = 0x226, - DOMAIN_ALIAS_RID_BACKUP_OPS = 0x227, + DOMAIN_ALIAS_RID_ACCOUNT_OPS = 0x224, + DOMAIN_ALIAS_RID_SYSTEM_OPS = 0x225, + DOMAIN_ALIAS_RID_PRINT_OPS = 0x226, + DOMAIN_ALIAS_RID_BACKUP_OPS = 0x227, - DOMAIN_ALIAS_RID_REPLICATOR = 0x228, - DOMAIN_ALIAS_RID_RAS_SERVERS = 0x229, - DOMAIN_ALIAS_RID_PREW2KCOMPACCESS = 0x22a, + DOMAIN_ALIAS_RID_REPLICATOR = 0x228, + DOMAIN_ALIAS_RID_RAS_SERVERS = 0x229, + DOMAIN_ALIAS_RID_PREW2KCOMPACCESS = 0x22a, } RELATIVE_IDENTIFIERS; /* * The universal well-known SIDs: * - * NULL_SID S-1-0-0 - * WORLD_SID S-1-1-0 - * LOCAL_SID S-1-2-0 - * CREATOR_OWNER_SID S-1-3-0 - * CREATOR_GROUP_SID S-1-3-1 - * CREATOR_OWNER_SERVER_SID S-1-3-2 - * CREATOR_GROUP_SERVER_SID S-1-3-3 + * NULL_SID S-1-0-0 + * WORLD_SID S-1-1-0 + * LOCAL_SID S-1-2-0 + * CREATOR_OWNER_SID S-1-3-0 + * CREATOR_GROUP_SID S-1-3-1 + * CREATOR_OWNER_SERVER_SID S-1-3-2 + * CREATOR_GROUP_SERVER_SID S-1-3-3 * - * (Non-unique IDs) S-1-4 + * (Non-unique IDs) S-1-4 * * NT well-known SIDs: * - * NT_AUTHORITY_SID S-1-5 - * DIALUP_SID S-1-5-1 + * NT_AUTHORITY_SID S-1-5 + * DIALUP_SID S-1-5-1 * - * NETWORD_SID S-1-5-2 - * BATCH_SID S-1-5-3 - * INTERACTIVE_SID S-1-5-4 - * SERVICE_SID S-1-5-6 - * ANONYMOUS_LOGON_SID S-1-5-7 (aka null logon session) - * PROXY_SID S-1-5-8 - * SERVER_LOGON_SID S-1-5-9 (aka domain controller account) - * SELF_SID S-1-5-10 (self RID) - * AUTHENTICATED_USER_SID S-1-5-11 - * RESTRICTED_CODE_SID S-1-5-12 (running restricted code) - * TERMINAL_SERVER_SID S-1-5-13 (running on terminal server) + * NETWORD_SID S-1-5-2 + * BATCH_SID S-1-5-3 + * INTERACTIVE_SID S-1-5-4 + * SERVICE_SID S-1-5-6 + * ANONYMOUS_LOGON_SID S-1-5-7 (aka null logon session) + * PROXY_SID S-1-5-8 + * SERVER_LOGON_SID S-1-5-9 (aka domain controller account) + * SELF_SID S-1-5-10 (self RID) + * AUTHENTICATED_USER_SID S-1-5-11 + * RESTRICTED_CODE_SID S-1-5-12 (running restricted code) + * TERMINAL_SERVER_SID S-1-5-13 (running on terminal server) * - * (Logon IDs) S-1-5-5-X-Y + * (Logon IDs) S-1-5-5-X-Y * - * (NT non-unique IDs) S-1-5-0x15-... + * (NT non-unique IDs) S-1-5-0x15-... * - * (Built-in domain) S-1-5-0x20 + * (Built-in domain) S-1-5-0x20 */ /** @@ -1397,15 +1349,13 @@ typedef enum /* Identifier authority. */ * * NOTE: This is stored as a big endian number. */ -typedef union -{ - struct - { - u16 high_part; /* High 16-bits. */ - u32 low_part; /* Low 32-bits. */ - } __attribute__( ( __packed__ ) ); - u8 value[6]; /* Value as individual bytes. */ -} __attribute__( ( __packed__ ) ) SID_IDENTIFIER_AUTHORITY; +typedef union { + struct { + u16 high_part; /* High 16-bits. */ + u32 low_part; /* Low 32-bits. */ + } __attribute__((__packed__)); + u8 value[6]; /* Value as individual bytes. */ +} __attribute__((__packed__)) SID_IDENTIFIER_AUTHORITY; /** * struct SID - @@ -1414,74 +1364,71 @@ typedef union * users or groups. SID stands for security identifier. * * The standard textual representation of the SID is of the form: - * S-R-I-S-S... + * S-R-I-S-S... * Where: * - The first "S" is the literal character 'S' identifying the following - * digits as a SID. + * digits as a SID. * - R is the revision level of the SID expressed as a sequence of digits - * in decimal. + * in decimal. * - I is the 48-bit identifier_authority, expressed as digits in decimal, - * if I < 2^32, or hexadecimal prefixed by "0x", if I >= 2^32. + * if I < 2^32, or hexadecimal prefixed by "0x", if I >= 2^32. * - S... is one or more sub_authority values, expressed as digits in - * decimal. + * decimal. * * Example SID; the domain-relative SID of the local Administrators group on * Windows NT/2k: - * S-1-5-32-544 + * S-1-5-32-544 * This translates to a SID with: - * revision = 1, - * sub_authority_count = 2, - * identifier_authority = {0,0,0,0,0,5}, // SECURITY_NT_AUTHORITY - * sub_authority[0] = 32, // SECURITY_BUILTIN_DOMAIN_RID - * sub_authority[1] = 544 // DOMAIN_ALIAS_RID_ADMINS + * revision = 1, + * sub_authority_count = 2, + * identifier_authority = {0,0,0,0,0,5}, // SECURITY_NT_AUTHORITY + * sub_authority[0] = 32, // SECURITY_BUILTIN_DOMAIN_RID + * sub_authority[1] = 544 // DOMAIN_ALIAS_RID_ADMINS */ -typedef struct -{ - u8 revision; - u8 sub_authority_count; - SID_IDENTIFIER_AUTHORITY identifier_authority; - u32 sub_authority[1]; /* At least one sub_authority. */ -} __attribute__( ( __packed__ ) ) SID; +typedef struct { + u8 revision; + u8 sub_authority_count; + SID_IDENTIFIER_AUTHORITY identifier_authority; + u32 sub_authority[1]; /* At least one sub_authority. */ +} __attribute__((__packed__)) SID; /** * enum SID_CONSTANTS - Current constants for SIDs. */ -typedef enum -{ - SID_REVISION = 1, /* Current revision level. */ - SID_MAX_SUB_AUTHORITIES = 15, /* Maximum number of those. */ - SID_RECOMMENDED_SUB_AUTHORITIES = 1, /* Will change to around 6 in - a future revision. */ +typedef enum { + SID_REVISION = 1, /* Current revision level. */ + SID_MAX_SUB_AUTHORITIES = 15, /* Maximum number of those. */ + SID_RECOMMENDED_SUB_AUTHORITIES = 1, /* Will change to around 6 in + a future revision. */ } SID_CONSTANTS; /** * enum ACE_TYPES - The predefined ACE types (8-bit, see below). */ -typedef enum -{ - ACCESS_MIN_MS_ACE_TYPE = 0, - ACCESS_ALLOWED_ACE_TYPE = 0, - ACCESS_DENIED_ACE_TYPE = 1, - SYSTEM_AUDIT_ACE_TYPE = 2, - SYSTEM_ALARM_ACE_TYPE = 3, /* Not implemented as of Win2k. */ - ACCESS_MAX_MS_V2_ACE_TYPE = 3, +typedef enum { + ACCESS_MIN_MS_ACE_TYPE = 0, + ACCESS_ALLOWED_ACE_TYPE = 0, + ACCESS_DENIED_ACE_TYPE = 1, + SYSTEM_AUDIT_ACE_TYPE = 2, + SYSTEM_ALARM_ACE_TYPE = 3, /* Not implemented as of Win2k. */ + ACCESS_MAX_MS_V2_ACE_TYPE = 3, - ACCESS_ALLOWED_COMPOUND_ACE_TYPE = 4, - ACCESS_MAX_MS_V3_ACE_TYPE = 4, + ACCESS_ALLOWED_COMPOUND_ACE_TYPE= 4, + ACCESS_MAX_MS_V3_ACE_TYPE = 4, - /* The following are Win2k only. */ - ACCESS_MIN_MS_OBJECT_ACE_TYPE = 5, - ACCESS_ALLOWED_OBJECT_ACE_TYPE = 5, - ACCESS_DENIED_OBJECT_ACE_TYPE = 6, - SYSTEM_AUDIT_OBJECT_ACE_TYPE = 7, - SYSTEM_ALARM_OBJECT_ACE_TYPE = 8, - ACCESS_MAX_MS_OBJECT_ACE_TYPE = 8, + /* The following are Win2k only. */ + ACCESS_MIN_MS_OBJECT_ACE_TYPE = 5, + ACCESS_ALLOWED_OBJECT_ACE_TYPE = 5, + ACCESS_DENIED_OBJECT_ACE_TYPE = 6, + SYSTEM_AUDIT_OBJECT_ACE_TYPE = 7, + SYSTEM_ALARM_OBJECT_ACE_TYPE = 8, + ACCESS_MAX_MS_OBJECT_ACE_TYPE = 8, - ACCESS_MAX_MS_V4_ACE_TYPE = 8, + ACCESS_MAX_MS_V4_ACE_TYPE = 8, - /* This one is for WinNT&2k. */ - ACCESS_MAX_MS_ACE_TYPE = 8, -} __attribute__( ( __packed__ ) ) ACE_TYPES; + /* This one is for WinNT&2k. */ + ACCESS_MAX_MS_ACE_TYPE = 8, +} __attribute__((__packed__)) ACE_TYPES; /** * enum ACE_FLAGS - The ACE flags (8-bit) for audit and inheritance. @@ -1493,20 +1440,19 @@ typedef enum * FAILED_ACCESS_ACE_FLAG is only used with system audit and alarm ACE types * to indicate that a message is generated (in Windows!) for failed accesses. */ -typedef enum -{ - /* The inheritance flags. */ - OBJECT_INHERIT_ACE = 0x01, - CONTAINER_INHERIT_ACE = 0x02, - NO_PROPAGATE_INHERIT_ACE = 0x04, - INHERIT_ONLY_ACE = 0x08, - INHERITED_ACE = 0x10, /* Win2k only. */ - VALID_INHERIT_FLAGS = 0x1f, +typedef enum { + /* The inheritance flags. */ + OBJECT_INHERIT_ACE = 0x01, + CONTAINER_INHERIT_ACE = 0x02, + NO_PROPAGATE_INHERIT_ACE = 0x04, + INHERIT_ONLY_ACE = 0x08, + INHERITED_ACE = 0x10, /* Win2k only. */ + VALID_INHERIT_FLAGS = 0x1f, - /* The audit flags. */ - SUCCESSFUL_ACCESS_ACE_FLAG = 0x40, - FAILED_ACCESS_ACE_FLAG = 0x80, -} __attribute__( ( __packed__ ) ) ACE_FLAGS; + /* The audit flags. */ + SUCCESSFUL_ACCESS_ACE_FLAG = 0x40, + FAILED_ACCESS_ACE_FLAG = 0x80, +} __attribute__((__packed__)) ACE_FLAGS; /** * struct ACE_HEADER - @@ -1521,147 +1467,145 @@ typedef enum * which specifies the type and size of the ACE. The format of the subsequent * data depends on the ACE type. */ -typedef struct -{ - ACE_TYPES type; /* Type of the ACE. */ - ACE_FLAGS flags; /* Flags describing the ACE. */ - u16 size; /* Size in bytes of the ACE. */ -} __attribute__( ( __packed__ ) ) ACE_HEADER; +typedef struct { + ACE_TYPES type; /* Type of the ACE. */ + ACE_FLAGS flags; /* Flags describing the ACE. */ + u16 size; /* Size in bytes of the ACE. */ +} __attribute__((__packed__)) ACE_HEADER; /** * enum ACCESS_MASK - The access mask (32-bit). * * Defines the access rights. */ -typedef enum -{ - /* - * The specific rights (bits 0 to 15). Depend on the type of the - * object being secured by the ACE. - */ +typedef enum { + /* + * The specific rights (bits 0 to 15). Depend on the type of the + * object being secured by the ACE. + */ - /* Specific rights for files and directories are as follows: */ + /* Specific rights for files and directories are as follows: */ - /* Right to read data from the file. (FILE) */ - FILE_READ_DATA = const_cpu_to_le32( 0x00000001 ), - /* Right to list contents of a directory. (DIRECTORY) */ - FILE_LIST_DIRECTORY = const_cpu_to_le32( 0x00000001 ), + /* Right to read data from the file. (FILE) */ + FILE_READ_DATA = const_cpu_to_le32(0x00000001), + /* Right to list contents of a directory. (DIRECTORY) */ + FILE_LIST_DIRECTORY = const_cpu_to_le32(0x00000001), - /* Right to write data to the file. (FILE) */ - FILE_WRITE_DATA = const_cpu_to_le32( 0x00000002 ), - /* Right to create a file in the directory. (DIRECTORY) */ - FILE_ADD_FILE = const_cpu_to_le32( 0x00000002 ), + /* Right to write data to the file. (FILE) */ + FILE_WRITE_DATA = const_cpu_to_le32(0x00000002), + /* Right to create a file in the directory. (DIRECTORY) */ + FILE_ADD_FILE = const_cpu_to_le32(0x00000002), - /* Right to append data to the file. (FILE) */ - FILE_APPEND_DATA = const_cpu_to_le32( 0x00000004 ), - /* Right to create a subdirectory. (DIRECTORY) */ - FILE_ADD_SUBDIRECTORY = const_cpu_to_le32( 0x00000004 ), + /* Right to append data to the file. (FILE) */ + FILE_APPEND_DATA = const_cpu_to_le32(0x00000004), + /* Right to create a subdirectory. (DIRECTORY) */ + FILE_ADD_SUBDIRECTORY = const_cpu_to_le32(0x00000004), - /* Right to read extended attributes. (FILE/DIRECTORY) */ - FILE_READ_EA = const_cpu_to_le32( 0x00000008 ), + /* Right to read extended attributes. (FILE/DIRECTORY) */ + FILE_READ_EA = const_cpu_to_le32(0x00000008), - /* Right to write extended attributes. (FILE/DIRECTORY) */ - FILE_WRITE_EA = const_cpu_to_le32( 0x00000010 ), + /* Right to write extended attributes. (FILE/DIRECTORY) */ + FILE_WRITE_EA = const_cpu_to_le32(0x00000010), - /* Right to execute a file. (FILE) */ - FILE_EXECUTE = const_cpu_to_le32( 0x00000020 ), - /* Right to traverse the directory. (DIRECTORY) */ - FILE_TRAVERSE = const_cpu_to_le32( 0x00000020 ), + /* Right to execute a file. (FILE) */ + FILE_EXECUTE = const_cpu_to_le32(0x00000020), + /* Right to traverse the directory. (DIRECTORY) */ + FILE_TRAVERSE = const_cpu_to_le32(0x00000020), - /* - * Right to delete a directory and all the files it contains (its - * children), even if the files are read-only. (DIRECTORY) - */ - FILE_DELETE_CHILD = const_cpu_to_le32( 0x00000040 ), + /* + * Right to delete a directory and all the files it contains (its + * children), even if the files are read-only. (DIRECTORY) + */ + FILE_DELETE_CHILD = const_cpu_to_le32(0x00000040), - /* Right to read file attributes. (FILE/DIRECTORY) */ - FILE_READ_ATTRIBUTES = const_cpu_to_le32( 0x00000080 ), + /* Right to read file attributes. (FILE/DIRECTORY) */ + FILE_READ_ATTRIBUTES = const_cpu_to_le32(0x00000080), - /* Right to change file attributes. (FILE/DIRECTORY) */ - FILE_WRITE_ATTRIBUTES = const_cpu_to_le32( 0x00000100 ), + /* Right to change file attributes. (FILE/DIRECTORY) */ + FILE_WRITE_ATTRIBUTES = const_cpu_to_le32(0x00000100), - /* - * The standard rights (bits 16 to 23). Are independent of the type of - * object being secured. - */ + /* + * The standard rights (bits 16 to 23). Are independent of the type of + * object being secured. + */ - /* Right to delete the object. */ - DELETE = const_cpu_to_le32( 0x00010000 ), + /* Right to delete the object. */ + DELETE = const_cpu_to_le32(0x00010000), - /* - * Right to read the information in the object's security descriptor, - * not including the information in the SACL. I.e. right to read the - * security descriptor and owner. - */ - READ_CONTROL = const_cpu_to_le32( 0x00020000 ), + /* + * Right to read the information in the object's security descriptor, + * not including the information in the SACL. I.e. right to read the + * security descriptor and owner. + */ + READ_CONTROL = const_cpu_to_le32(0x00020000), - /* Right to modify the DACL in the object's security descriptor. */ - WRITE_DAC = const_cpu_to_le32( 0x00040000 ), + /* Right to modify the DACL in the object's security descriptor. */ + WRITE_DAC = const_cpu_to_le32(0x00040000), - /* Right to change the owner in the object's security descriptor. */ - WRITE_OWNER = const_cpu_to_le32( 0x00080000 ), + /* Right to change the owner in the object's security descriptor. */ + WRITE_OWNER = const_cpu_to_le32(0x00080000), - /* - * Right to use the object for synchronization. Enables a process to - * wait until the object is in the signalled state. Some object types - * do not support this access right. - */ - SYNCHRONIZE = const_cpu_to_le32( 0x00100000 ), + /* + * Right to use the object for synchronization. Enables a process to + * wait until the object is in the signalled state. Some object types + * do not support this access right. + */ + SYNCHRONIZE = const_cpu_to_le32(0x00100000), - /* - * The following STANDARD_RIGHTS_* are combinations of the above for - * convenience and are defined by the Win32 API. - */ + /* + * The following STANDARD_RIGHTS_* are combinations of the above for + * convenience and are defined by the Win32 API. + */ - /* These are currently defined to READ_CONTROL. */ - STANDARD_RIGHTS_READ = const_cpu_to_le32( 0x00020000 ), - STANDARD_RIGHTS_WRITE = const_cpu_to_le32( 0x00020000 ), - STANDARD_RIGHTS_EXECUTE = const_cpu_to_le32( 0x00020000 ), + /* These are currently defined to READ_CONTROL. */ + STANDARD_RIGHTS_READ = const_cpu_to_le32(0x00020000), + STANDARD_RIGHTS_WRITE = const_cpu_to_le32(0x00020000), + STANDARD_RIGHTS_EXECUTE = const_cpu_to_le32(0x00020000), - /* Combines DELETE, READ_CONTROL, WRITE_DAC, and WRITE_OWNER access. */ - STANDARD_RIGHTS_REQUIRED = const_cpu_to_le32( 0x000f0000 ), + /* Combines DELETE, READ_CONTROL, WRITE_DAC, and WRITE_OWNER access. */ + STANDARD_RIGHTS_REQUIRED = const_cpu_to_le32(0x000f0000), - /* - * Combines DELETE, READ_CONTROL, WRITE_DAC, WRITE_OWNER, and - * SYNCHRONIZE access. - */ - STANDARD_RIGHTS_ALL = const_cpu_to_le32( 0x001f0000 ), + /* + * Combines DELETE, READ_CONTROL, WRITE_DAC, WRITE_OWNER, and + * SYNCHRONIZE access. + */ + STANDARD_RIGHTS_ALL = const_cpu_to_le32(0x001f0000), - /* - * The access system ACL and maximum allowed access types (bits 24 to - * 25, bits 26 to 27 are reserved). - */ - ACCESS_SYSTEM_SECURITY = const_cpu_to_le32( 0x01000000 ), - MAXIMUM_ALLOWED = const_cpu_to_le32( 0x02000000 ), + /* + * The access system ACL and maximum allowed access types (bits 24 to + * 25, bits 26 to 27 are reserved). + */ + ACCESS_SYSTEM_SECURITY = const_cpu_to_le32(0x01000000), + MAXIMUM_ALLOWED = const_cpu_to_le32(0x02000000), - /* - * The generic rights (bits 28 to 31). These map onto the standard and - * specific rights. - */ + /* + * The generic rights (bits 28 to 31). These map onto the standard and + * specific rights. + */ - /* Read, write, and execute access. */ - GENERIC_ALL = const_cpu_to_le32( 0x10000000 ), + /* Read, write, and execute access. */ + GENERIC_ALL = const_cpu_to_le32(0x10000000), - /* Execute access. */ - GENERIC_EXECUTE = const_cpu_to_le32( 0x20000000 ), + /* Execute access. */ + GENERIC_EXECUTE = const_cpu_to_le32(0x20000000), - /* - * Write access. For files, this maps onto: - * FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_DATA | - * FILE_WRITE_EA | STANDARD_RIGHTS_WRITE | SYNCHRONIZE - * For directories, the mapping has the same numerical value. See - * above for the descriptions of the rights granted. - */ - GENERIC_WRITE = const_cpu_to_le32( 0x40000000 ), + /* + * Write access. For files, this maps onto: + * FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_DATA | + * FILE_WRITE_EA | STANDARD_RIGHTS_WRITE | SYNCHRONIZE + * For directories, the mapping has the same numerical value. See + * above for the descriptions of the rights granted. + */ + GENERIC_WRITE = const_cpu_to_le32(0x40000000), - /* - * Read access. For files, this maps onto: - * FILE_READ_ATTRIBUTES | FILE_READ_DATA | FILE_READ_EA | - * STANDARD_RIGHTS_READ | SYNCHRONIZE - * For directories, the mapping has the same numerical value. See - * above for the descriptions of the rights granted. - */ - GENERIC_READ = const_cpu_to_le32( 0x80000000 ), + /* + * Read access. For files, this maps onto: + * FILE_READ_ATTRIBUTES | FILE_READ_DATA | FILE_READ_EA | + * STANDARD_RIGHTS_READ | SYNCHRONIZE + * For directories, the mapping has the same numerical value. See + * above for the descriptions of the rights granted. + */ + GENERIC_READ = const_cpu_to_le32(0x80000000), } ACCESS_MASK; /** @@ -1672,13 +1616,12 @@ typedef enum * * FIXME: What exactly is this and what is it for? (AIA) */ -typedef struct -{ - ACCESS_MASK generic_read; - ACCESS_MASK generic_write; - ACCESS_MASK generic_execute; - ACCESS_MASK generic_all; -} __attribute__( ( __packed__ ) ) GENERIC_MAPPING; +typedef struct { + ACCESS_MASK generic_read; + ACCESS_MASK generic_write; + ACCESS_MASK generic_execute; + ACCESS_MASK generic_all; +} __attribute__((__packed__)) GENERIC_MAPPING; /* * The predefined ACE type structures are as defined below. @@ -1689,46 +1632,43 @@ typedef struct * * ACCESS_ALLOWED_ACE, ACCESS_DENIED_ACE, SYSTEM_AUDIT_ACE, SYSTEM_ALARM_ACE */ -typedef struct -{ - /* 0 ACE_HEADER; -- Unfolded here as gcc doesn't like unnamed structs. */ - ACE_TYPES type; /* Type of the ACE. */ - ACE_FLAGS flags; /* Flags describing the ACE. */ - u16 size; /* Size in bytes of the ACE. */ +typedef struct { +/* 0 ACE_HEADER; -- Unfolded here as gcc doesn't like unnamed structs. */ + ACE_TYPES type; /* Type of the ACE. */ + ACE_FLAGS flags; /* Flags describing the ACE. */ + u16 size; /* Size in bytes of the ACE. */ - /* 4*/ ACCESS_MASK mask; /* Access mask associated with the ACE. */ - /* 8*/ SID sid; /* The SID associated with the ACE. */ -} __attribute__( ( __packed__ ) ) ACCESS_ALLOWED_ACE, ACCESS_DENIED_ACE, -SYSTEM_AUDIT_ACE, SYSTEM_ALARM_ACE; +/* 4*/ ACCESS_MASK mask; /* Access mask associated with the ACE. */ +/* 8*/ SID sid; /* The SID associated with the ACE. */ +} __attribute__((__packed__)) ACCESS_ALLOWED_ACE, ACCESS_DENIED_ACE, + SYSTEM_AUDIT_ACE, SYSTEM_ALARM_ACE; /** * enum OBJECT_ACE_FLAGS - The object ACE flags (32-bit). */ -typedef enum -{ - ACE_OBJECT_TYPE_PRESENT = const_cpu_to_le32( 1 ), - ACE_INHERITED_OBJECT_TYPE_PRESENT = const_cpu_to_le32( 2 ), +typedef enum { + ACE_OBJECT_TYPE_PRESENT = const_cpu_to_le32(1), + ACE_INHERITED_OBJECT_TYPE_PRESENT = const_cpu_to_le32(2), } OBJECT_ACE_FLAGS; /** * struct ACCESS_ALLOWED_OBJECT_ACE - */ -typedef struct -{ - /* 0 ACE_HEADER; -- Unfolded here as gcc doesn't like unnamed structs. */ - ACE_TYPES type; /* Type of the ACE. */ - ACE_FLAGS flags; /* Flags describing the ACE. */ - u16 size; /* Size in bytes of the ACE. */ +typedef struct { +/* 0 ACE_HEADER; -- Unfolded here as gcc doesn't like unnamed structs. */ + ACE_TYPES type; /* Type of the ACE. */ + ACE_FLAGS flags; /* Flags describing the ACE. */ + u16 size; /* Size in bytes of the ACE. */ - /* 4*/ ACCESS_MASK mask; /* Access mask associated with the ACE. */ - /* 8*/ OBJECT_ACE_FLAGS object_flags; /* Flags describing the object ACE. */ - /* 12*/ GUID object_type; - /* 28*/ GUID inherited_object_type; - /* 44*/ SID sid; /* The SID associated with the ACE. */ -} __attribute__( ( __packed__ ) ) ACCESS_ALLOWED_OBJECT_ACE, -ACCESS_DENIED_OBJECT_ACE, -SYSTEM_AUDIT_OBJECT_ACE, -SYSTEM_ALARM_OBJECT_ACE; +/* 4*/ ACCESS_MASK mask; /* Access mask associated with the ACE. */ +/* 8*/ OBJECT_ACE_FLAGS object_flags; /* Flags describing the object ACE. */ +/* 12*/ GUID object_type; +/* 28*/ GUID inherited_object_type; +/* 44*/ SID sid; /* The SID associated with the ACE. */ +} __attribute__((__packed__)) ACCESS_ALLOWED_OBJECT_ACE, + ACCESS_DENIED_OBJECT_ACE, + SYSTEM_AUDIT_OBJECT_ACE, + SYSTEM_ALARM_OBJECT_ACE; /** * struct ACL - An ACL is an access-control list (ACL). @@ -1738,33 +1678,31 @@ SYSTEM_ALARM_OBJECT_ACE; * zero or more access control entries (ACEs). The ACL as well as each ACE * are aligned on 4-byte boundaries. */ -typedef struct -{ - u8 revision; /* Revision of this ACL. */ - u8 alignment1; - u16 size; /* Allocated space in bytes for ACL. Includes this - header, the ACEs and the remaining free space. */ - u16 ace_count; /* Number of ACEs in the ACL. */ - u16 alignment2; - /* sizeof() = 8 bytes */ -} __attribute__( ( __packed__ ) ) ACL; +typedef struct { + u8 revision; /* Revision of this ACL. */ + u8 alignment1; + u16 size; /* Allocated space in bytes for ACL. Includes this + header, the ACEs and the remaining free space. */ + u16 ace_count; /* Number of ACEs in the ACL. */ + u16 alignment2; +/* sizeof() = 8 bytes */ +} __attribute__((__packed__)) ACL; /** * enum ACL_CONSTANTS - Current constants for ACLs. */ -typedef enum -{ - /* Current revision. */ - ACL_REVISION = 2, - ACL_REVISION_DS = 4, +typedef enum { + /* Current revision. */ + ACL_REVISION = 2, + ACL_REVISION_DS = 4, - /* History of revisions. */ - ACL_REVISION1 = 1, - MIN_ACL_REVISION = 2, - ACL_REVISION2 = 2, - ACL_REVISION3 = 3, - ACL_REVISION4 = 4, - MAX_ACL_REVISION = 4, + /* History of revisions. */ + ACL_REVISION1 = 1, + MIN_ACL_REVISION = 2, + ACL_REVISION2 = 2, + ACL_REVISION3 = 3, + ACL_REVISION4 = 4, + MAX_ACL_REVISION = 4, } ACL_CONSTANTS; /** @@ -1773,66 +1711,65 @@ typedef enum * The security descriptor control flags (16-bit). * * SE_OWNER_DEFAULTED - This boolean flag, when set, indicates that the - * SID pointed to by the Owner field was provided by a - * defaulting mechanism rather than explicitly provided by the - * original provider of the security descriptor. This may - * affect the treatment of the SID with respect to inheritance - * of an owner. + * SID pointed to by the Owner field was provided by a + * defaulting mechanism rather than explicitly provided by the + * original provider of the security descriptor. This may + * affect the treatment of the SID with respect to inheritance + * of an owner. * * SE_GROUP_DEFAULTED - This boolean flag, when set, indicates that the - * SID in the Group field was provided by a defaulting mechanism - * rather than explicitly provided by the original provider of - * the security descriptor. This may affect the treatment of - * the SID with respect to inheritance of a primary group. + * SID in the Group field was provided by a defaulting mechanism + * rather than explicitly provided by the original provider of + * the security descriptor. This may affect the treatment of + * the SID with respect to inheritance of a primary group. * * SE_DACL_PRESENT - This boolean flag, when set, indicates that the - * security descriptor contains a discretionary ACL. If this - * flag is set and the Dacl field of the SECURITY_DESCRIPTOR is - * null, then a null ACL is explicitly being specified. + * security descriptor contains a discretionary ACL. If this + * flag is set and the Dacl field of the SECURITY_DESCRIPTOR is + * null, then a null ACL is explicitly being specified. * * SE_DACL_DEFAULTED - This boolean flag, when set, indicates that the - * ACL pointed to by the Dacl field was provided by a defaulting - * mechanism rather than explicitly provided by the original - * provider of the security descriptor. This may affect the - * treatment of the ACL with respect to inheritance of an ACL. - * This flag is ignored if the DaclPresent flag is not set. + * ACL pointed to by the Dacl field was provided by a defaulting + * mechanism rather than explicitly provided by the original + * provider of the security descriptor. This may affect the + * treatment of the ACL with respect to inheritance of an ACL. + * This flag is ignored if the DaclPresent flag is not set. * * SE_SACL_PRESENT - This boolean flag, when set, indicates that the - * security descriptor contains a system ACL pointed to by the - * Sacl field. If this flag is set and the Sacl field of the - * SECURITY_DESCRIPTOR is null, then an empty (but present) - * ACL is being specified. + * security descriptor contains a system ACL pointed to by the + * Sacl field. If this flag is set and the Sacl field of the + * SECURITY_DESCRIPTOR is null, then an empty (but present) + * ACL is being specified. * * SE_SACL_DEFAULTED - This boolean flag, when set, indicates that the - * ACL pointed to by the Sacl field was provided by a defaulting - * mechanism rather than explicitly provided by the original - * provider of the security descriptor. This may affect the - * treatment of the ACL with respect to inheritance of an ACL. - * This flag is ignored if the SaclPresent flag is not set. + * ACL pointed to by the Sacl field was provided by a defaulting + * mechanism rather than explicitly provided by the original + * provider of the security descriptor. This may affect the + * treatment of the ACL with respect to inheritance of an ACL. + * This flag is ignored if the SaclPresent flag is not set. * * SE_SELF_RELATIVE - This boolean flag, when set, indicates that the - * security descriptor is in self-relative form. In this form, - * all fields of the security descriptor are contiguous in memory - * and all pointer fields are expressed as offsets from the - * beginning of the security descriptor. + * security descriptor is in self-relative form. In this form, + * all fields of the security descriptor are contiguous in memory + * and all pointer fields are expressed as offsets from the + * beginning of the security descriptor. */ -typedef enum -{ - SE_OWNER_DEFAULTED = const_cpu_to_le16( 0x0001 ), - SE_GROUP_DEFAULTED = const_cpu_to_le16( 0x0002 ), - SE_DACL_PRESENT = const_cpu_to_le16( 0x0004 ), - SE_DACL_DEFAULTED = const_cpu_to_le16( 0x0008 ), - SE_SACL_PRESENT = const_cpu_to_le16( 0x0010 ), - SE_SACL_DEFAULTED = const_cpu_to_le16( 0x0020 ), - SE_DACL_AUTO_INHERIT_REQ = const_cpu_to_le16( 0x0100 ), - SE_SACL_AUTO_INHERIT_REQ = const_cpu_to_le16( 0x0200 ), - SE_DACL_AUTO_INHERITED = const_cpu_to_le16( 0x0400 ), - SE_SACL_AUTO_INHERITED = const_cpu_to_le16( 0x0800 ), - SE_DACL_PROTECTED = const_cpu_to_le16( 0x1000 ), - SE_SACL_PROTECTED = const_cpu_to_le16( 0x2000 ), - SE_RM_CONTROL_VALID = const_cpu_to_le16( 0x4000 ), - SE_SELF_RELATIVE = const_cpu_to_le16( 0x8000 ), -} __attribute__( ( __packed__ ) ) SECURITY_DESCRIPTOR_CONTROL; +typedef enum { + SE_OWNER_DEFAULTED = const_cpu_to_le16(0x0001), + SE_GROUP_DEFAULTED = const_cpu_to_le16(0x0002), + SE_DACL_PRESENT = const_cpu_to_le16(0x0004), + SE_DACL_DEFAULTED = const_cpu_to_le16(0x0008), + SE_SACL_PRESENT = const_cpu_to_le16(0x0010), + SE_SACL_DEFAULTED = const_cpu_to_le16(0x0020), + SE_DACL_AUTO_INHERIT_REQ = const_cpu_to_le16(0x0100), + SE_SACL_AUTO_INHERIT_REQ = const_cpu_to_le16(0x0200), + SE_DACL_AUTO_INHERITED = const_cpu_to_le16(0x0400), + SE_SACL_AUTO_INHERITED = const_cpu_to_le16(0x0800), + SE_DACL_PROTECTED = const_cpu_to_le16(0x1000), + SE_SACL_PROTECTED = const_cpu_to_le16(0x2000), + SE_RM_CONTROL_VALID = const_cpu_to_le16(0x4000), + SE_SELF_RELATIVE = const_cpu_to_le16(0x8000), +} __attribute__((__packed__)) SECURITY_DESCRIPTOR_CONTROL; /** * struct SECURITY_DESCRIPTOR_RELATIVE - @@ -1840,28 +1777,27 @@ typedef enum * Self-relative security descriptor. Contains the owner and group SIDs as well * as the sacl and dacl ACLs inside the security descriptor itself. */ -typedef struct -{ - u8 revision; /* Revision level of the security descriptor. */ - u8 alignment; - SECURITY_DESCRIPTOR_CONTROL control; /* Flags qualifying the type of - the descriptor as well as the following fields. */ - u32 owner; /* Byte offset to a SID representing an object's - owner. If this is NULL, no owner SID is present in - the descriptor. */ - u32 group; /* Byte offset to a SID representing an object's - primary group. If this is NULL, no primary group - SID is present in the descriptor. */ - u32 sacl; /* Byte offset to a system ACL. Only valid, if - SE_SACL_PRESENT is set in the control field. If - SE_SACL_PRESENT is set but sacl is NULL, a NULL ACL - is specified. */ - u32 dacl; /* Byte offset to a discretionary ACL. Only valid, if - SE_DACL_PRESENT is set in the control field. If - SE_DACL_PRESENT is set but dacl is NULL, a NULL ACL - (unconditionally granting access) is specified. */ - /* sizeof() = 0x14 bytes */ -} __attribute__( ( __packed__ ) ) SECURITY_DESCRIPTOR_RELATIVE; +typedef struct { + u8 revision; /* Revision level of the security descriptor. */ + u8 alignment; + SECURITY_DESCRIPTOR_CONTROL control; /* Flags qualifying the type of + the descriptor as well as the following fields. */ + u32 owner; /* Byte offset to a SID representing an object's + owner. If this is NULL, no owner SID is present in + the descriptor. */ + u32 group; /* Byte offset to a SID representing an object's + primary group. If this is NULL, no primary group + SID is present in the descriptor. */ + u32 sacl; /* Byte offset to a system ACL. Only valid, if + SE_SACL_PRESENT is set in the control field. If + SE_SACL_PRESENT is set but sacl is NULL, a NULL ACL + is specified. */ + u32 dacl; /* Byte offset to a discretionary ACL. Only valid, if + SE_DACL_PRESENT is set in the control field. If + SE_DACL_PRESENT is set but dacl is NULL, a NULL ACL + (unconditionally granting access) is specified. */ +/* sizeof() = 0x14 bytes */ +} __attribute__((__packed__)) SECURITY_DESCRIPTOR_RELATIVE; /** * struct SECURITY_DESCRIPTOR - Absolute security descriptor. @@ -1873,42 +1809,40 @@ typedef struct * * On disk, a self-relative security descriptor is used. */ -typedef struct -{ - u8 revision; /* Revision level of the security descriptor. */ - u8 alignment; - SECURITY_DESCRIPTOR_CONTROL control; /* Flags qualifying the type of - the descriptor as well as the following fields. */ - SID *owner; /* Points to a SID representing an object's owner. If - this is NULL, no owner SID is present in the - descriptor. */ - SID *group; /* Points to a SID representing an object's primary - group. If this is NULL, no primary group SID is - present in the descriptor. */ - ACL *sacl; /* Points to a system ACL. Only valid, if - SE_SACL_PRESENT is set in the control field. If - SE_SACL_PRESENT is set but sacl is NULL, a NULL ACL - is specified. */ - ACL *dacl; /* Points to a discretionary ACL. Only valid, if - SE_DACL_PRESENT is set in the control field. If - SE_DACL_PRESENT is set but dacl is NULL, a NULL ACL - (unconditionally granting access) is specified. */ -} __attribute__( ( __packed__ ) ) SECURITY_DESCRIPTOR; +typedef struct { + u8 revision; /* Revision level of the security descriptor. */ + u8 alignment; + SECURITY_DESCRIPTOR_CONTROL control; /* Flags qualifying the type of + the descriptor as well as the following fields. */ + SID *owner; /* Points to a SID representing an object's owner. If + this is NULL, no owner SID is present in the + descriptor. */ + SID *group; /* Points to a SID representing an object's primary + group. If this is NULL, no primary group SID is + present in the descriptor. */ + ACL *sacl; /* Points to a system ACL. Only valid, if + SE_SACL_PRESENT is set in the control field. If + SE_SACL_PRESENT is set but sacl is NULL, a NULL ACL + is specified. */ + ACL *dacl; /* Points to a discretionary ACL. Only valid, if + SE_DACL_PRESENT is set in the control field. If + SE_DACL_PRESENT is set but dacl is NULL, a NULL ACL + (unconditionally granting access) is specified. */ +} __attribute__((__packed__)) SECURITY_DESCRIPTOR; /** * enum SECURITY_DESCRIPTOR_CONSTANTS - * * Current constants for security descriptors. */ -typedef enum -{ - /* Current revision. */ - SECURITY_DESCRIPTOR_REVISION = 1, - SECURITY_DESCRIPTOR_REVISION1 = 1, +typedef enum { + /* Current revision. */ + SECURITY_DESCRIPTOR_REVISION = 1, + SECURITY_DESCRIPTOR_REVISION1 = 1, - /* The sizes of both the absolute and relative security descriptors is - the same as pointers, at least on ia32 architecture are 32-bit. */ - SECURITY_DESCRIPTOR_MIN_LENGTH = sizeof( SECURITY_DESCRIPTOR ), + /* The sizes of both the absolute and relative security descriptors is + the same as pointers, at least on ia32 architecture are 32-bit. */ + SECURITY_DESCRIPTOR_MIN_LENGTH = sizeof(SECURITY_DESCRIPTOR), } SECURITY_DESCRIPTOR_CONSTANTS; /* @@ -1968,26 +1902,24 @@ typedef SECURITY_DESCRIPTOR_RELATIVE SECURITY_DESCRIPTOR_ATTR; * This header precedes each security descriptor in the $SDS data stream. * This is also the index entry data part of both the $SII and $SDH indexes. */ -typedef struct -{ - u32 hash; /* Hash of the security descriptor. */ - u32 security_id; /* The security_id assigned to the descriptor. */ - u64 offset; /* Byte offset of this entry in the $SDS stream. */ - u32 length; /* Size in bytes of this entry in $SDS stream. */ -} __attribute__( ( __packed__ ) ) SECURITY_DESCRIPTOR_HEADER; +typedef struct { + u32 hash; /* Hash of the security descriptor. */ + u32 security_id; /* The security_id assigned to the descriptor. */ + u64 offset; /* Byte offset of this entry in the $SDS stream. */ + u32 length; /* Size in bytes of this entry in $SDS stream. */ +} __attribute__((__packed__)) SECURITY_DESCRIPTOR_HEADER; /** * struct SDH_INDEX_DATA - */ -typedef struct -{ - u32 hash; /* Hash of the security descriptor. */ - u32 security_id; /* The security_id assigned to the descriptor. */ - u64 offset; /* Byte offset of this entry in the $SDS stream. */ - u32 length; /* Size in bytes of this entry in $SDS stream. */ - u32 reserved_II; /* Padding - always unicode "II" or zero. This field - isn't counted in INDEX_ENTRY's data_length. */ -} __attribute__( ( __packed__ ) ) SDH_INDEX_DATA; +typedef struct { + u32 hash; /* Hash of the security descriptor. */ + u32 security_id; /* The security_id assigned to the descriptor. */ + u64 offset; /* Byte offset of this entry in the $SDS stream. */ + u32 length; /* Size in bytes of this entry in $SDS stream. */ + u32 reserved_II; /* Padding - always unicode "II" or zero. This field + isn't counted in INDEX_ENTRY's data_length. */ +} __attribute__((__packed__)) SDH_INDEX_DATA; /** * struct SII_INDEX_DATA - @@ -2007,27 +1939,25 @@ typedef SECURITY_DESCRIPTOR_HEADER SII_INDEX_DATA; * the first copy of the security descriptor will be at offset 0x51d0 in the * $SDS data stream and the second copy will be at offset 0x451d0. */ -typedef struct -{ - /* 0 SECURITY_DESCRIPTOR_HEADER; -- Unfolded here as gcc doesn't like - unnamed structs. */ - u32 hash; /* Hash of the security descriptor. */ - u32 security_id; /* The security_id assigned to the descriptor. */ - u64 offset; /* Byte offset of this entry in the $SDS stream. */ - u32 length; /* Size in bytes of this entry in $SDS stream. */ - /* 20*/ SECURITY_DESCRIPTOR_RELATIVE sid; /* The self-relative security - descriptor. */ -} __attribute__( ( __packed__ ) ) SDS_ENTRY; +typedef struct { +/* 0 SECURITY_DESCRIPTOR_HEADER; -- Unfolded here as gcc doesn't like + unnamed structs. */ + u32 hash; /* Hash of the security descriptor. */ + u32 security_id; /* The security_id assigned to the descriptor. */ + u64 offset; /* Byte offset of this entry in the $SDS stream. */ + u32 length; /* Size in bytes of this entry in $SDS stream. */ +/* 20*/ SECURITY_DESCRIPTOR_RELATIVE sid; /* The self-relative security + descriptor. */ +} __attribute__((__packed__)) SDS_ENTRY; /** * struct SII_INDEX_KEY - The index entry key used in the $SII index. * * The collation type is COLLATION_NTOFS_ULONG. */ -typedef struct -{ - u32 security_id; /* The security_id assigned to the descriptor. */ -} __attribute__( ( __packed__ ) ) SII_INDEX_KEY; +typedef struct { + u32 security_id; /* The security_id assigned to the descriptor. */ +} __attribute__((__packed__)) SII_INDEX_KEY; /** * struct SDH_INDEX_KEY - The index entry key used in the $SDH index. @@ -2035,11 +1965,10 @@ typedef struct * The keys are sorted first by hash and then by security_id. * The collation rule is COLLATION_NTOFS_SECURITY_HASH. */ -typedef struct -{ - u32 hash; /* Hash of the security descriptor. */ - u32 security_id; /* The security_id assigned to the descriptor. */ -} __attribute__( ( __packed__ ) ) SDH_INDEX_KEY; +typedef struct { + u32 hash; /* Hash of the security descriptor. */ + u32 security_id; /* The security_id assigned to the descriptor. */ +} __attribute__((__packed__)) SDH_INDEX_KEY; /** * struct VOLUME_NAME - Attribute: Volume name (0x60). @@ -2047,26 +1976,24 @@ typedef struct * NOTE: Always resident. * NOTE: Present only in FILE_Volume. */ -typedef struct -{ - ntfschar name[0]; /* The name of the volume in Unicode. */ -} __attribute__( ( __packed__ ) ) VOLUME_NAME; +typedef struct { + ntfschar name[0]; /* The name of the volume in Unicode. */ +} __attribute__((__packed__)) VOLUME_NAME; /** * enum VOLUME_FLAGS - Possible flags for the volume (16-bit). */ -typedef enum -{ - VOLUME_IS_DIRTY = const_cpu_to_le16( 0x0001 ), - VOLUME_RESIZE_LOG_FILE = const_cpu_to_le16( 0x0002 ), - VOLUME_UPGRADE_ON_MOUNT = const_cpu_to_le16( 0x0004 ), - VOLUME_MOUNTED_ON_NT4 = const_cpu_to_le16( 0x0008 ), - VOLUME_DELETE_USN_UNDERWAY = const_cpu_to_le16( 0x0010 ), - VOLUME_REPAIR_OBJECT_ID = const_cpu_to_le16( 0x0020 ), - VOLUME_CHKDSK_UNDERWAY = const_cpu_to_le16( 0x4000 ), - VOLUME_MODIFIED_BY_CHKDSK = const_cpu_to_le16( 0x8000 ), - VOLUME_FLAGS_MASK = const_cpu_to_le16( 0xc03f ), -} __attribute__( ( __packed__ ) ) VOLUME_FLAGS; +typedef enum { + VOLUME_IS_DIRTY = const_cpu_to_le16(0x0001), + VOLUME_RESIZE_LOG_FILE = const_cpu_to_le16(0x0002), + VOLUME_UPGRADE_ON_MOUNT = const_cpu_to_le16(0x0004), + VOLUME_MOUNTED_ON_NT4 = const_cpu_to_le16(0x0008), + VOLUME_DELETE_USN_UNDERWAY = const_cpu_to_le16(0x0010), + VOLUME_REPAIR_OBJECT_ID = const_cpu_to_le16(0x0020), + VOLUME_CHKDSK_UNDERWAY = const_cpu_to_le16(0x4000), + VOLUME_MODIFIED_BY_CHKDSK = const_cpu_to_le16(0x8000), + VOLUME_FLAGS_MASK = const_cpu_to_le16(0xc03f), +} __attribute__((__packed__)) VOLUME_FLAGS; /** * struct VOLUME_INFORMATION - Attribute: Volume information (0x70). @@ -2074,15 +2001,14 @@ typedef enum * NOTE: Always resident. * NOTE: Present only in FILE_Volume. * NOTE: Windows 2000 uses NTFS 3.0 while Windows NT4 service pack 6a uses - * NTFS 1.2. I haven't personally seen other values yet. + * NTFS 1.2. I haven't personally seen other values yet. */ -typedef struct -{ - u64 reserved; /* Not used (yet?). */ - u8 major_ver; /* Major version of the ntfs format. */ - u8 minor_ver; /* Minor version of the ntfs format. */ - VOLUME_FLAGS flags; /* Bit array of VOLUME_* flags. */ -} __attribute__( ( __packed__ ) ) VOLUME_INFORMATION; +typedef struct { + u64 reserved; /* Not used (yet?). */ + u8 major_ver; /* Major version of the ntfs format. */ + u8 minor_ver; /* Minor version of the ntfs format. */ + VOLUME_FLAGS flags; /* Bit array of VOLUME_* flags. */ +} __attribute__((__packed__)) VOLUME_INFORMATION; /** * struct DATA_ATTR - Attribute: Data attribute (0x80). @@ -2091,33 +2017,31 @@ typedef struct * * Data contents of a file (i.e. the unnamed stream) or of a named stream. */ -typedef struct -{ - u8 data[0]; /* The file's data contents. */ -} __attribute__( ( __packed__ ) ) DATA_ATTR; +typedef struct { + u8 data[0]; /* The file's data contents. */ +} __attribute__((__packed__)) DATA_ATTR; /** * enum INDEX_HEADER_FLAGS - Index header flags (8-bit). */ -typedef enum -{ - /* When index header is in an index root attribute: */ - SMALL_INDEX = 0, /* The index is small enough to fit inside the - index root attribute and there is no index - allocation attribute present. */ - LARGE_INDEX = 1, /* The index is too large to fit in the index - root attribute and/or an index allocation - attribute is present. */ - /* - * When index header is in an index block, i.e. is part of index - * allocation attribute: - */ - LEAF_NODE = 0, /* This is a leaf node, i.e. there are no more - nodes branching off it. */ - INDEX_NODE = 1, /* This node indexes other nodes, i.e. is not a - leaf node. */ - NODE_MASK = 1, /* Mask for accessing the *_NODE bits. */ -} __attribute__( ( __packed__ ) ) INDEX_HEADER_FLAGS; +typedef enum { + /* When index header is in an index root attribute: */ + SMALL_INDEX = 0, /* The index is small enough to fit inside the + index root attribute and there is no index + allocation attribute present. */ + LARGE_INDEX = 1, /* The index is too large to fit in the index + root attribute and/or an index allocation + attribute is present. */ + /* + * When index header is in an index block, i.e. is part of index + * allocation attribute: + */ + LEAF_NODE = 0, /* This is a leaf node, i.e. there are no more + nodes branching off it. */ + INDEX_NODE = 1, /* This node indexes other nodes, i.e. is not a + leaf node. */ + NODE_MASK = 1, /* Mask for accessing the *_NODE bits. */ +} __attribute__((__packed__)) INDEX_HEADER_FLAGS; /** * struct INDEX_HEADER - @@ -2130,28 +2054,26 @@ typedef enum * relative to the start of the index header structure and not relative to the * start of the index root or index allocation structures themselves. */ -typedef struct -{ - /* 0*/ - u32 entries_offset; /* Byte offset from the INDEX_HEADER to first - INDEX_ENTRY, aligned to 8-byte boundary. */ - /* 4*/ u32 index_length; /* Data size in byte of the INDEX_ENTRY's, - including the INDEX_HEADER, aligned to 8. */ - /* 8*/ u32 allocated_size; /* Allocated byte size of this index (block), - multiple of 8 bytes. See more below. */ - /* - For the index root attribute, the above two numbers are always - equal, as the attribute is resident and it is resized as needed. - - For the index allocation attribute, the attribute is not resident - and the allocated_size is equal to the index_block_size specified - by the corresponding INDEX_ROOT attribute minus the INDEX_BLOCK - size not counting the INDEX_HEADER part (i.e. minus -24). - */ - /* 12*/ INDEX_HEADER_FLAGS ih_flags; /* Bit field of INDEX_HEADER_FLAGS. */ - /* 13*/ u8 reserved[3]; /* Reserved/align to 8-byte boundary.*/ - /* sizeof() == 16 */ -} __attribute__( ( __packed__ ) ) INDEX_HEADER; +typedef struct { +/* 0*/ u32 entries_offset; /* Byte offset from the INDEX_HEADER to first + INDEX_ENTRY, aligned to 8-byte boundary. */ +/* 4*/ u32 index_length; /* Data size in byte of the INDEX_ENTRY's, + including the INDEX_HEADER, aligned to 8. */ +/* 8*/ u32 allocated_size; /* Allocated byte size of this index (block), + multiple of 8 bytes. See more below. */ + /* + For the index root attribute, the above two numbers are always + equal, as the attribute is resident and it is resized as needed. + + For the index allocation attribute, the attribute is not resident + and the allocated_size is equal to the index_block_size specified + by the corresponding INDEX_ROOT attribute minus the INDEX_BLOCK + size not counting the INDEX_HEADER part (i.e. minus -24). + */ +/* 12*/ INDEX_HEADER_FLAGS ih_flags; /* Bit field of INDEX_HEADER_FLAGS. */ +/* 13*/ u8 reserved[3]; /* Reserved/align to 8-byte boundary.*/ +/* sizeof() == 16 */ +} __attribute__((__packed__)) INDEX_HEADER; /** * struct INDEX_ROOT - Attribute: Index root (0x90). @@ -2172,27 +2094,25 @@ typedef struct * NOTE: The root directory (FILE_root) contains an entry for itself. Other * directories do not contain entries for themselves, though. */ -typedef struct -{ - /* 0*/ - ATTR_TYPES type; /* Type of the indexed attribute. Is - $FILE_NAME for directories, zero - for view indexes. No other values - allowed. */ - /* 4*/ COLLATION_RULES collation_rule; /* Collation rule used to sort the - index entries. If type is $FILE_NAME, - this must be COLLATION_FILE_NAME. */ - /* 8*/ u32 index_block_size; /* Size of index block in bytes (in - the index allocation attribute). */ - /* 12*/ s8 clusters_per_index_block; /* Size of index block in clusters (in - the index allocation attribute), when - an index block is >= than a cluster, - otherwise sectors per index block. */ - /* 13*/ u8 reserved[3]; /* Reserved/align to 8-byte boundary. */ - /* 16*/ INDEX_HEADER index; /* Index header describing the - following index entries. */ - /* sizeof()= 32 bytes */ -} __attribute__( ( __packed__ ) ) INDEX_ROOT; +typedef struct { +/* 0*/ ATTR_TYPES type; /* Type of the indexed attribute. Is + $FILE_NAME for directories, zero + for view indexes. No other values + allowed. */ +/* 4*/ COLLATION_RULES collation_rule; /* Collation rule used to sort the + index entries. If type is $FILE_NAME, + this must be COLLATION_FILE_NAME. */ +/* 8*/ u32 index_block_size; /* Size of index block in bytes (in + the index allocation attribute). */ +/* 12*/ s8 clusters_per_index_block; /* Size of index block in clusters (in + the index allocation attribute), when + an index block is >= than a cluster, + otherwise sectors per index block. */ +/* 13*/ u8 reserved[3]; /* Reserved/align to 8-byte boundary. */ +/* 16*/ INDEX_HEADER index; /* Index header describing the + following index entries. */ +/* sizeof()= 32 bytes */ +} __attribute__((__packed__)) INDEX_ROOT; /** * struct INDEX_BLOCK - Attribute: Index allocation (0xa0). @@ -2203,28 +2123,27 @@ typedef struct * INDEX_BLOCK structure containing an index header, followed by a sequence of * index entries (INDEX_ENTRY structures), as described by the INDEX_HEADER. */ -typedef struct -{ - /* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */ - NTFS_RECORD_TYPES magic;/* Magic is "INDX". */ - u16 usa_ofs; /* See NTFS_RECORD definition. */ - u16 usa_count; /* See NTFS_RECORD definition. */ +typedef struct { +/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */ + NTFS_RECORD_TYPES magic;/* Magic is "INDX". */ + u16 usa_ofs; /* See NTFS_RECORD definition. */ + u16 usa_count; /* See NTFS_RECORD definition. */ - /* 8*/ LSN lsn; /* $LogFile sequence number of the last - modification of this index block. */ - /* 16*/ VCN index_block_vcn; /* Virtual cluster number of the index block. */ - /* 24*/ INDEX_HEADER index; /* Describes the following index entries. */ - /* sizeof()= 40 (0x28) bytes */ - /* - * When creating the index block, we place the update sequence array at this - * offset, i.e. before we start with the index entries. This also makes sense, - * otherwise we could run into problems with the update sequence array - * containing in itself the last two bytes of a sector which would mean that - * multi sector transfer protection wouldn't work. As you can't protect data - * by overwriting it since you then can't get it back... - * When reading use the data from the ntfs record header. - */ -} __attribute__( ( __packed__ ) ) INDEX_BLOCK; +/* 8*/ LSN lsn; /* $LogFile sequence number of the last + modification of this index block. */ +/* 16*/ VCN index_block_vcn; /* Virtual cluster number of the index block. */ +/* 24*/ INDEX_HEADER index; /* Describes the following index entries. */ +/* sizeof()= 40 (0x28) bytes */ +/* + * When creating the index block, we place the update sequence array at this + * offset, i.e. before we start with the index entries. This also makes sense, + * otherwise we could run into problems with the update sequence array + * containing in itself the last two bytes of a sector which would mean that + * multi sector transfer protection wouldn't work. As you can't protect data + * by overwriting it since you then can't get it back... + * When reading use the data from the ntfs record header. + */ +} __attribute__((__packed__)) INDEX_BLOCK; typedef INDEX_BLOCK INDEX_ALLOCATION; @@ -2239,36 +2158,34 @@ typedef INDEX_BLOCK INDEX_ALLOCATION; * COLLATION_NTOFS_ULONGS. FIXME: Verify whether the reparse_tag is not the * primary key / is not a key at all. (AIA) */ -typedef struct -{ - u32 reparse_tag; /* Reparse point type (inc. flags). */ - MFT_REF file_id; /* Mft record of the file containing the - reparse point attribute. */ -} __attribute__( ( __packed__ ) ) REPARSE_INDEX_KEY; +typedef struct { + u32 reparse_tag; /* Reparse point type (inc. flags). */ + MFT_REF file_id; /* Mft record of the file containing the + reparse point attribute. */ +} __attribute__((__packed__)) REPARSE_INDEX_KEY; /** * enum QUOTA_FLAGS - Quota flags (32-bit). */ -typedef enum -{ - /* The user quota flags. Names explain meaning. */ - QUOTA_FLAG_DEFAULT_LIMITS = const_cpu_to_le32( 0x00000001 ), - QUOTA_FLAG_LIMIT_REACHED = const_cpu_to_le32( 0x00000002 ), - QUOTA_FLAG_ID_DELETED = const_cpu_to_le32( 0x00000004 ), +typedef enum { + /* The user quota flags. Names explain meaning. */ + QUOTA_FLAG_DEFAULT_LIMITS = const_cpu_to_le32(0x00000001), + QUOTA_FLAG_LIMIT_REACHED = const_cpu_to_le32(0x00000002), + QUOTA_FLAG_ID_DELETED = const_cpu_to_le32(0x00000004), - QUOTA_FLAG_USER_MASK = const_cpu_to_le32( 0x00000007 ), - /* Bit mask for user quota flags. */ + QUOTA_FLAG_USER_MASK = const_cpu_to_le32(0x00000007), + /* Bit mask for user quota flags. */ - /* These flags are only present in the quota defaults index entry, - i.e. in the entry where owner_id = QUOTA_DEFAULTS_ID. */ - QUOTA_FLAG_TRACKING_ENABLED = const_cpu_to_le32( 0x00000010 ), - QUOTA_FLAG_ENFORCEMENT_ENABLED = const_cpu_to_le32( 0x00000020 ), - QUOTA_FLAG_TRACKING_REQUESTED = const_cpu_to_le32( 0x00000040 ), - QUOTA_FLAG_LOG_THRESHOLD = const_cpu_to_le32( 0x00000080 ), - QUOTA_FLAG_LOG_LIMIT = const_cpu_to_le32( 0x00000100 ), - QUOTA_FLAG_OUT_OF_DATE = const_cpu_to_le32( 0x00000200 ), - QUOTA_FLAG_CORRUPT = const_cpu_to_le32( 0x00000400 ), - QUOTA_FLAG_PENDING_DELETES = const_cpu_to_le32( 0x00000800 ), + /* These flags are only present in the quota defaults index entry, + i.e. in the entry where owner_id = QUOTA_DEFAULTS_ID. */ + QUOTA_FLAG_TRACKING_ENABLED = const_cpu_to_le32(0x00000010), + QUOTA_FLAG_ENFORCEMENT_ENABLED = const_cpu_to_le32(0x00000020), + QUOTA_FLAG_TRACKING_REQUESTED = const_cpu_to_le32(0x00000040), + QUOTA_FLAG_LOG_THRESHOLD = const_cpu_to_le32(0x00000080), + QUOTA_FLAG_LOG_LIMIT = const_cpu_to_le32(0x00000100), + QUOTA_FLAG_OUT_OF_DATE = const_cpu_to_le32(0x00000200), + QUOTA_FLAG_CORRUPT = const_cpu_to_le32(0x00000400), + QUOTA_FLAG_PENDING_DELETES = const_cpu_to_le32(0x00000800), } QUOTA_FLAGS; /** @@ -2294,89 +2211,81 @@ typedef enum * * The $Q index entry data is the quota control entry and is defined below. */ -typedef struct -{ - u32 version; /* Currently equals 2. */ - QUOTA_FLAGS flags; /* Flags describing this quota entry. */ - u64 bytes_used; /* How many bytes of the quota are in use. */ - s64 change_time; /* Last time this quota entry was changed. */ - s64 threshold; /* Soft quota (-1 if not limited). */ - s64 limit; /* Hard quota (-1 if not limited). */ - s64 exceeded_time; /* How long the soft quota has been exceeded. */ - /* The below field is NOT present for the quota defaults entry. */ - SID sid; /* The SID of the user/object associated with - this quota entry. If this field is missing - then the INDEX_ENTRY is padded with zeros - to multiply of 8 which are not counted in - the data_length field. If the sid is present - then this structure is padded with zeros to - multiply of 8 and the padding is counted in - the INDEX_ENTRY's data_length. */ -} __attribute__( ( __packed__ ) ) QUOTA_CONTROL_ENTRY; +typedef struct { + u32 version; /* Currently equals 2. */ + QUOTA_FLAGS flags; /* Flags describing this quota entry. */ + u64 bytes_used; /* How many bytes of the quota are in use. */ + s64 change_time; /* Last time this quota entry was changed. */ + s64 threshold; /* Soft quota (-1 if not limited). */ + s64 limit; /* Hard quota (-1 if not limited). */ + s64 exceeded_time; /* How long the soft quota has been exceeded. */ +/* The below field is NOT present for the quota defaults entry. */ + SID sid; /* The SID of the user/object associated with + this quota entry. If this field is missing + then the INDEX_ENTRY is padded with zeros + to multiply of 8 which are not counted in + the data_length field. If the sid is present + then this structure is padded with zeros to + multiply of 8 and the padding is counted in + the INDEX_ENTRY's data_length. */ +} __attribute__((__packed__)) QUOTA_CONTROL_ENTRY; /** * struct QUOTA_O_INDEX_DATA - */ -typedef struct -{ - u32 owner_id; - u32 unknown; /* Always 32. Seems to be padding and it's not - counted in the INDEX_ENTRY's data_length. - This field shouldn't be really here. */ -} __attribute__( ( __packed__ ) ) QUOTA_O_INDEX_DATA; +typedef struct { + u32 owner_id; + u32 unknown; /* Always 32. Seems to be padding and it's not + counted in the INDEX_ENTRY's data_length. + This field shouldn't be really here. */ +} __attribute__((__packed__)) QUOTA_O_INDEX_DATA; /** * enum PREDEFINED_OWNER_IDS - Predefined owner_id values (32-bit). */ -typedef enum -{ - QUOTA_INVALID_ID = const_cpu_to_le32( 0x00000000 ), - QUOTA_DEFAULTS_ID = const_cpu_to_le32( 0x00000001 ), - QUOTA_FIRST_USER_ID = const_cpu_to_le32( 0x00000100 ), +typedef enum { + QUOTA_INVALID_ID = const_cpu_to_le32(0x00000000), + QUOTA_DEFAULTS_ID = const_cpu_to_le32(0x00000001), + QUOTA_FIRST_USER_ID = const_cpu_to_le32(0x00000100), } PREDEFINED_OWNER_IDS; /** * enum INDEX_ENTRY_FLAGS - Index entry flags (16-bit). */ -typedef enum -{ - INDEX_ENTRY_NODE = const_cpu_to_le16( 1 ), /* This entry contains a - sub-node, i.e. a reference to an index - block in form of a virtual cluster - number (see below). */ - INDEX_ENTRY_END = const_cpu_to_le16( 2 ), /* This signifies the last - entry in an index block. The index - entry does not represent a file but it - can point to a sub-node. */ - INDEX_ENTRY_SPACE_FILLER = 0xffff, /* Just to force 16-bit width. */ -} __attribute__( ( __packed__ ) ) INDEX_ENTRY_FLAGS; +typedef enum { + INDEX_ENTRY_NODE = const_cpu_to_le16(1), /* This entry contains a + sub-node, i.e. a reference to an index + block in form of a virtual cluster + number (see below). */ + INDEX_ENTRY_END = const_cpu_to_le16(2), /* This signifies the last + entry in an index block. The index + entry does not represent a file but it + can point to a sub-node. */ + INDEX_ENTRY_SPACE_FILLER = 0xffff, /* Just to force 16-bit width. */ +} __attribute__((__packed__)) INDEX_ENTRY_FLAGS; /** * struct INDEX_ENTRY_HEADER - This the index entry header (see below). - * + * * ========================================================== * !!!!! SEE DESCRIPTION OF THE FIELDS AT INDEX_ENTRY !!!!! * ========================================================== */ -typedef struct -{ - /* 0*/ - union - { - MFT_REF indexed_file; - struct - { - u16 data_offset; - u16 data_length; - u32 reservedV; - } __attribute__( ( __packed__ ) ); - } __attribute__( ( __packed__ ) ); - /* 8*/ u16 length; - /* 10*/ u16 key_length; - /* 12*/ INDEX_ENTRY_FLAGS flags; - /* 14*/ u16 reserved; - /* sizeof() = 16 bytes */ -} __attribute__( ( __packed__ ) ) INDEX_ENTRY_HEADER; +typedef struct { +/* 0*/ union { + MFT_REF indexed_file; + struct { + u16 data_offset; + u16 data_length; + u32 reservedV; + } __attribute__((__packed__)); + } __attribute__((__packed__)); +/* 8*/ u16 length; +/* 10*/ u16 key_length; +/* 12*/ INDEX_ENTRY_FLAGS flags; +/* 14*/ u16 reserved; +/* sizeof() = 16 bytes */ +} __attribute__((__packed__)) INDEX_ENTRY_HEADER; /** * struct INDEX_ENTRY - This is an index entry. @@ -2387,67 +2296,63 @@ typedef struct * * NOTE: Before NTFS 3.0 only filename attributes were indexed. */ -typedef struct -{ - /* 0 INDEX_ENTRY_HEADER; -- Unfolded here as gcc dislikes unnamed structs. */ - union /* Only valid when INDEX_ENTRY_END is not set. */ - { - MFT_REF indexed_file; /* The mft reference of the file - described by this index - entry. Used for directory - indexes. */ - struct /* Used for views/indexes to find the entry's data. */ - { - u16 data_offset; /* Data byte offset from this - INDEX_ENTRY. Follows the - index key. */ - u16 data_length; /* Data length in bytes. */ - u32 reservedV; /* Reserved (zero). */ - } __attribute__( ( __packed__ ) ); - } __attribute__( ( __packed__ ) ); - /* 8*/ u16 length; /* Byte size of this index entry, multiple of - 8-bytes. Size includes INDEX_ENTRY_HEADER - and the optional subnode VCN. See below. */ - /* 10*/ u16 key_length; /* Byte size of the key value, which is in the - index entry. It follows field reserved. Not - multiple of 8-bytes. */ - /* 12*/ INDEX_ENTRY_FLAGS ie_flags; /* Bit field of INDEX_ENTRY_* flags. */ - /* 14*/ u16 reserved; /* Reserved/align to 8-byte boundary. */ - /* End of INDEX_ENTRY_HEADER */ - /* 16*/ union - { /* The key of the indexed attribute. NOTE: Only present - if INDEX_ENTRY_END bit in flags is not set. NOTE: On - NTFS versions before 3.0 the only valid key is the - FILE_NAME_ATTR. On NTFS 3.0+ the following - additional index keys are defined: */ - FILE_NAME_ATTR file_name;/* $I30 index in directories. */ - SII_INDEX_KEY sii; /* $SII index in $Secure. */ - SDH_INDEX_KEY sdh; /* $SDH index in $Secure. */ - GUID object_id; /* $O index in FILE_Extend/$ObjId: The - object_id of the mft record found in - the data part of the index. */ - REPARSE_INDEX_KEY reparse; /* $R index in - FILE_Extend/$Reparse. */ - SID sid; /* $O index in FILE_Extend/$Quota: - SID of the owner of the user_id. */ - u32 owner_id; /* $Q index in FILE_Extend/$Quota: - user_id of the owner of the quota - control entry in the data part of - the index. */ - } __attribute__( ( __packed__ ) ) key; - /* The (optional) index data is inserted here when creating. - VCN vcn; If INDEX_ENTRY_NODE bit in ie_flags is set, the last - eight bytes of this index entry contain the virtual - cluster number of the index block that holds the - entries immediately preceding the current entry. - - If the key_length is zero, then the vcn immediately - follows the INDEX_ENTRY_HEADER. - - The address of the vcn of "ie" INDEX_ENTRY is given by - (char*)ie + le16_to_cpu(ie->length) - sizeof(VCN) - */ -} __attribute__( ( __packed__ ) ) INDEX_ENTRY; +typedef struct { +/* 0 INDEX_ENTRY_HEADER; -- Unfolded here as gcc dislikes unnamed structs. */ + union { /* Only valid when INDEX_ENTRY_END is not set. */ + MFT_REF indexed_file; /* The mft reference of the file + described by this index + entry. Used for directory + indexes. */ + struct { /* Used for views/indexes to find the entry's data. */ + u16 data_offset; /* Data byte offset from this + INDEX_ENTRY. Follows the + index key. */ + u16 data_length; /* Data length in bytes. */ + u32 reservedV; /* Reserved (zero). */ + } __attribute__((__packed__)); + } __attribute__((__packed__)); +/* 8*/ u16 length; /* Byte size of this index entry, multiple of + 8-bytes. Size includes INDEX_ENTRY_HEADER + and the optional subnode VCN. See below. */ +/* 10*/ u16 key_length; /* Byte size of the key value, which is in the + index entry. It follows field reserved. Not + multiple of 8-bytes. */ +/* 12*/ INDEX_ENTRY_FLAGS ie_flags; /* Bit field of INDEX_ENTRY_* flags. */ +/* 14*/ u16 reserved; /* Reserved/align to 8-byte boundary. */ +/* End of INDEX_ENTRY_HEADER */ +/* 16*/ union { /* The key of the indexed attribute. NOTE: Only present + if INDEX_ENTRY_END bit in flags is not set. NOTE: On + NTFS versions before 3.0 the only valid key is the + FILE_NAME_ATTR. On NTFS 3.0+ the following + additional index keys are defined: */ + FILE_NAME_ATTR file_name;/* $I30 index in directories. */ + SII_INDEX_KEY sii; /* $SII index in $Secure. */ + SDH_INDEX_KEY sdh; /* $SDH index in $Secure. */ + GUID object_id; /* $O index in FILE_Extend/$ObjId: The + object_id of the mft record found in + the data part of the index. */ + REPARSE_INDEX_KEY reparse; /* $R index in + FILE_Extend/$Reparse. */ + SID sid; /* $O index in FILE_Extend/$Quota: + SID of the owner of the user_id. */ + u32 owner_id; /* $Q index in FILE_Extend/$Quota: + user_id of the owner of the quota + control entry in the data part of + the index. */ + } __attribute__((__packed__)) key; + /* The (optional) index data is inserted here when creating. + VCN vcn; If INDEX_ENTRY_NODE bit in ie_flags is set, the last + eight bytes of this index entry contain the virtual + cluster number of the index block that holds the + entries immediately preceding the current entry. + + If the key_length is zero, then the vcn immediately + follows the INDEX_ENTRY_HEADER. + + The address of the vcn of "ie" INDEX_ENTRY is given by + (char*)ie + le16_to_cpu(ie->length) - sizeof(VCN) + */ +} __attribute__((__packed__)) INDEX_ENTRY; /** * struct BITMAP_ATTR - Attribute: Bitmap (0xb0). @@ -2459,10 +2364,9 @@ typedef struct * the number of bits in the bitmap * index block size / cluster size is the * number of clusters in the index allocation attribute. */ -typedef struct -{ - u8 bitmap[0]; /* Array of bits. */ -} __attribute__( ( __packed__ ) ) BITMAP_ATTR; +typedef struct { + u8 bitmap[0]; /* Array of bits. */ +} __attribute__((__packed__)) BITMAP_ATTR; /** * enum PREDEFINED_REPARSE_TAGS - @@ -2477,35 +2381,34 @@ typedef struct * 2. The 13 bits after this (i.e. bits 16 to 28) are reserved for future use. * 3. The most significant three bits are flags describing the reparse point. * They are defined as follows: - * bit 29: Name surrogate bit. If set, the filename is an alias for - * another object in the system. - * bit 30: High-latency bit. If set, accessing the first byte of data will - * be slow. (E.g. the data is stored on a tape drive.) - * bit 31: Microsoft bit. If set, the tag is owned by Microsoft. User - * defined tags have to use zero here. + * bit 29: Name surrogate bit. If set, the filename is an alias for + * another object in the system. + * bit 30: High-latency bit. If set, accessing the first byte of data will + * be slow. (E.g. the data is stored on a tape drive.) + * bit 31: Microsoft bit. If set, the tag is owned by Microsoft. User + * defined tags have to use zero here. */ -typedef enum -{ - IO_REPARSE_TAG_IS_ALIAS = const_cpu_to_le32( 0x20000000 ), - IO_REPARSE_TAG_IS_HIGH_LATENCY = const_cpu_to_le32( 0x40000000 ), - IO_REPARSE_TAG_IS_MICROSOFT = const_cpu_to_le32( 0x80000000 ), +typedef enum { + IO_REPARSE_TAG_IS_ALIAS = const_cpu_to_le32(0x20000000), + IO_REPARSE_TAG_IS_HIGH_LATENCY = const_cpu_to_le32(0x40000000), + IO_REPARSE_TAG_IS_MICROSOFT = const_cpu_to_le32(0x80000000), - IO_REPARSE_TAG_RESERVED_ZERO = const_cpu_to_le32( 0x00000000 ), - IO_REPARSE_TAG_RESERVED_ONE = const_cpu_to_le32( 0x00000001 ), - IO_REPARSE_TAG_RESERVED_RANGE = const_cpu_to_le32( 0x00000001 ), + IO_REPARSE_TAG_RESERVED_ZERO = const_cpu_to_le32(0x00000000), + IO_REPARSE_TAG_RESERVED_ONE = const_cpu_to_le32(0x00000001), + IO_REPARSE_TAG_RESERVED_RANGE = const_cpu_to_le32(0x00000001), - IO_REPARSE_TAG_NSS = const_cpu_to_le32( 0x68000005 ), - IO_REPARSE_TAG_NSS_RECOVER = const_cpu_to_le32( 0x68000006 ), - IO_REPARSE_TAG_SIS = const_cpu_to_le32( 0x68000007 ), - IO_REPARSE_TAG_DFS = const_cpu_to_le32( 0x68000008 ), + IO_REPARSE_TAG_NSS = const_cpu_to_le32(0x68000005), + IO_REPARSE_TAG_NSS_RECOVER = const_cpu_to_le32(0x68000006), + IO_REPARSE_TAG_SIS = const_cpu_to_le32(0x68000007), + IO_REPARSE_TAG_DFS = const_cpu_to_le32(0x68000008), - IO_REPARSE_TAG_MOUNT_POINT = const_cpu_to_le32( 0x88000003 ), + IO_REPARSE_TAG_MOUNT_POINT = const_cpu_to_le32(0x88000003), - IO_REPARSE_TAG_HSM = const_cpu_to_le32( 0xa8000004 ), + IO_REPARSE_TAG_HSM = const_cpu_to_le32(0xa8000004), - IO_REPARSE_TAG_SYMBOLIC_LINK = const_cpu_to_le32( 0xe8000000 ), + IO_REPARSE_TAG_SYMBOLIC_LINK = const_cpu_to_le32(0xe8000000), - IO_REPARSE_TAG_VALID_VALUES = const_cpu_to_le32( 0xe000ffff ), + IO_REPARSE_TAG_VALID_VALUES = const_cpu_to_le32(0xe000ffff), } PREDEFINED_REPARSE_TAGS; /** @@ -2513,42 +2416,39 @@ typedef enum * * NOTE: Can be resident or non-resident. */ -typedef struct -{ - u32 reparse_tag; /* Reparse point type (inc. flags). */ - u16 reparse_data_length; /* Byte size of reparse data. */ - u16 reserved; /* Align to 8-byte boundary. */ - u8 reparse_data[0]; /* Meaning depends on reparse_tag. */ -} __attribute__( ( __packed__ ) ) REPARSE_POINT; +typedef struct { + u32 reparse_tag; /* Reparse point type (inc. flags). */ + u16 reparse_data_length; /* Byte size of reparse data. */ + u16 reserved; /* Align to 8-byte boundary. */ + u8 reparse_data[0]; /* Meaning depends on reparse_tag. */ +} __attribute__((__packed__)) REPARSE_POINT; /** * struct EA_INFORMATION - Attribute: Extended attribute information (0xd0). * * NOTE: Always resident. */ -typedef struct -{ - u16 ea_length; /* Byte size of the packed extended - attributes. */ - u16 need_ea_count; /* The number of extended attributes which have - the NEED_EA bit set. */ - u32 ea_query_length; /* Byte size of the buffer required to query - the extended attributes when calling - ZwQueryEaFile() in Windows NT/2k. I.e. the - byte size of the unpacked extended - attributes. */ -} __attribute__( ( __packed__ ) ) EA_INFORMATION; +typedef struct { + u16 ea_length; /* Byte size of the packed extended + attributes. */ + u16 need_ea_count; /* The number of extended attributes which have + the NEED_EA bit set. */ + u32 ea_query_length; /* Byte size of the buffer required to query + the extended attributes when calling + ZwQueryEaFile() in Windows NT/2k. I.e. the + byte size of the unpacked extended + attributes. */ +} __attribute__((__packed__)) EA_INFORMATION; /** * enum EA_FLAGS - Extended attribute flags (8-bit). */ -typedef enum -{ - NEED_EA = 0x80, /* Indicate that the file to which the EA - belongs cannot be interpreted without - understanding the associated extended - attributes. */ -} __attribute__( ( __packed__ ) ) EA_FLAGS; +typedef enum { + NEED_EA = 0x80, /* Indicate that the file to which the EA + belongs cannot be interpreted without + understanding the associated extended + attributes. */ +} __attribute__((__packed__)) EA_FLAGS; /** * struct EA_ATTR - Attribute: Extended attribute (EA) (0xe0). @@ -2559,17 +2459,16 @@ typedef enum * FIXME: It appears weird that the EA name is not Unicode. Is it true? * FIXME: It seems that name is always uppercased. Is it true? */ -typedef struct -{ - u32 next_entry_offset; /* Offset to the next EA_ATTR. */ - EA_FLAGS flags; /* Flags describing the EA. */ - u8 name_length; /* Length of the name of the extended - attribute in bytes. */ - u16 value_length; /* Byte size of the EA's value. */ - u8 name[0]; /* Name of the EA. */ - u8 value[0]; /* The value of the EA. Immediately - follows the name. */ -} __attribute__( ( __packed__ ) ) EA_ATTR; +typedef struct { + u32 next_entry_offset; /* Offset to the next EA_ATTR. */ + EA_FLAGS flags; /* Flags describing the EA. */ + u8 name_length; /* Length of the name of the extended + attribute in bytes. */ + u16 value_length; /* Byte size of the EA's value. */ + u8 name[0]; /* Name of the EA. */ + u8 value[0]; /* The value of the EA. Immediately + follows the name. */ +} __attribute__((__packed__)) EA_ATTR; /** * struct PROPERTY_SET - Attribute: Property set (0xf0). @@ -2577,10 +2476,9 @@ typedef struct * Intended to support Native Structure Storage (NSS) - a feature removed from * NTFS 3.0 during beta testing. */ -typedef struct -{ - /* Irrelevant as feature unused. */ -} __attribute__( ( __packed__ ) ) PROPERTY_SET; +typedef struct { + /* Irrelevant as feature unused. */ +} __attribute__((__packed__)) PROPERTY_SET; /** * struct LOGGED_UTILITY_STREAM - Attribute: Logged utility stream (0x100). @@ -2593,10 +2491,9 @@ typedef struct * Used by the Encrypting File System (EFS). All encrypted files have this * attribute with the name $EFS. See below for the relevant structures. */ -typedef struct -{ - /* Can be anything the creator chooses. */ -} __attribute__( ( __packed__ ) ) LOGGED_UTILITY_STREAM; +typedef struct { + /* Can be anything the creator chooses. */ +} __attribute__((__packed__)) LOGGED_UTILITY_STREAM; /* * $EFS Data Structure: @@ -2630,153 +2527,135 @@ typedef struct * * The header of the Logged utility stream (0x100) attribute named "$EFS". */ -typedef struct -{ - /* 0*/ - u32 length; /* Length of EFS attribute in bytes. */ - u32 state; /* Always 0? */ - u32 version; /* Efs version. Always 2? */ - u32 crypto_api_version; /* Always 0? */ - /* 16*/ u8 unknown4[16]; /* MD5 hash of decrypted FEK? This field is - created with a call to UuidCreate() so is - unlikely to be an MD5 hash and is more - likely to be GUID of this encrytped file - or something like that. */ - /* 32*/ u8 unknown5[16]; /* MD5 hash of DDFs? */ - /* 48*/ u8 unknown6[16]; /* MD5 hash of DRFs? */ - /* 64*/ u32 offset_to_ddf_array;/* Offset in bytes to the array of data - decryption fields (DDF), see below. Zero if - no DDFs are present. */ - u32 offset_to_drf_array;/* Offset in bytes to the array of data - recovery fields (DRF), see below. Zero if - no DRFs are present. */ - u32 reserved; /* Reserved. */ -} __attribute__( ( __packed__ ) ) EFS_ATTR_HEADER; +typedef struct { +/* 0*/ u32 length; /* Length of EFS attribute in bytes. */ + u32 state; /* Always 0? */ + u32 version; /* Efs version. Always 2? */ + u32 crypto_api_version; /* Always 0? */ +/* 16*/ u8 unknown4[16]; /* MD5 hash of decrypted FEK? This field is + created with a call to UuidCreate() so is + unlikely to be an MD5 hash and is more + likely to be GUID of this encrytped file + or something like that. */ +/* 32*/ u8 unknown5[16]; /* MD5 hash of DDFs? */ +/* 48*/ u8 unknown6[16]; /* MD5 hash of DRFs? */ +/* 64*/ u32 offset_to_ddf_array;/* Offset in bytes to the array of data + decryption fields (DDF), see below. Zero if + no DDFs are present. */ + u32 offset_to_drf_array;/* Offset in bytes to the array of data + recovery fields (DRF), see below. Zero if + no DRFs are present. */ + u32 reserved; /* Reserved. */ +} __attribute__((__packed__)) EFS_ATTR_HEADER; /** * struct EFS_DF_ARRAY_HEADER - */ -typedef struct -{ - u32 df_count; /* Number of data decryption/recovery fields in - the array. */ -} __attribute__( ( __packed__ ) ) EFS_DF_ARRAY_HEADER; +typedef struct { + u32 df_count; /* Number of data decryption/recovery fields in + the array. */ +} __attribute__((__packed__)) EFS_DF_ARRAY_HEADER; /** * struct EFS_DF_HEADER - */ -typedef struct -{ - /* 0*/ - u32 df_length; /* Length of this data decryption/recovery - field in bytes. */ - u32 cred_header_offset; /* Offset in bytes to the credential header. */ - u32 fek_size; /* Size in bytes of the encrypted file - encryption key (FEK). */ - u32 fek_offset; /* Offset in bytes to the FEK from the start of - the data decryption/recovery field. */ - /* 16*/ u32 unknown1; /* always 0? Might be just padding. */ -} __attribute__( ( __packed__ ) ) EFS_DF_HEADER; +typedef struct { +/* 0*/ u32 df_length; /* Length of this data decryption/recovery + field in bytes. */ + u32 cred_header_offset; /* Offset in bytes to the credential header. */ + u32 fek_size; /* Size in bytes of the encrypted file + encryption key (FEK). */ + u32 fek_offset; /* Offset in bytes to the FEK from the start of + the data decryption/recovery field. */ +/* 16*/ u32 unknown1; /* always 0? Might be just padding. */ +} __attribute__((__packed__)) EFS_DF_HEADER; /** * struct EFS_DF_CREDENTIAL_HEADER - */ -typedef struct -{ - /* 0*/ - u32 cred_length; /* Length of this credential in bytes. */ - u32 sid_offset; /* Offset in bytes to the user's sid from start - of this structure. Zero if no sid is - present. */ - /* 8*/ u32 type; /* Type of this credential: - 1 = CryptoAPI container. - 2 = Unexpected type. - 3 = Certificate thumbprint. - other = Unknown type. */ - union - { - /* CryptoAPI container. */ - struct - { - /* 12*/ - u32 container_name_offset; /* Offset in bytes to - the name of the container from start of this - structure (may not be zero). */ - /* 16*/ u32 provider_name_offset; /* Offset in bytes to - the name of the provider from start of this - structure (may not be zero). */ - u32 public_key_blob_offset; /* Offset in bytes to - the public key blob from start of this - structure. */ - /* 24*/ u32 public_key_blob_size; /* Size in bytes of - public key blob. */ - } __attribute__( ( __packed__ ) ); - /* Certificate thumbprint. */ - struct - { - /* 12*/ - u32 cert_thumbprint_header_size; /* Size in - bytes of the header of the certificate - thumbprint. */ - /* 16*/ u32 cert_thumbprint_header_offset; /* Offset in - bytes to the header of the certificate - thumbprint from start of this structure. */ - u32 unknown1; /* Always 0? Might be padding... */ - u32 unknown2; /* Always 0? Might be padding... */ - } __attribute__( ( __packed__ ) ); - } __attribute__( ( __packed__ ) ); -} __attribute__( ( __packed__ ) ) EFS_DF_CREDENTIAL_HEADER; +typedef struct { +/* 0*/ u32 cred_length; /* Length of this credential in bytes. */ + u32 sid_offset; /* Offset in bytes to the user's sid from start + of this structure. Zero if no sid is + present. */ +/* 8*/ u32 type; /* Type of this credential: + 1 = CryptoAPI container. + 2 = Unexpected type. + 3 = Certificate thumbprint. + other = Unknown type. */ + union { + /* CryptoAPI container. */ + struct { +/* 12*/ u32 container_name_offset; /* Offset in bytes to + the name of the container from start of this + structure (may not be zero). */ +/* 16*/ u32 provider_name_offset; /* Offset in bytes to + the name of the provider from start of this + structure (may not be zero). */ + u32 public_key_blob_offset; /* Offset in bytes to + the public key blob from start of this + structure. */ +/* 24*/ u32 public_key_blob_size; /* Size in bytes of + public key blob. */ + } __attribute__((__packed__)); + /* Certificate thumbprint. */ + struct { +/* 12*/ u32 cert_thumbprint_header_size; /* Size in + bytes of the header of the certificate + thumbprint. */ +/* 16*/ u32 cert_thumbprint_header_offset; /* Offset in + bytes to the header of the certificate + thumbprint from start of this structure. */ + u32 unknown1; /* Always 0? Might be padding... */ + u32 unknown2; /* Always 0? Might be padding... */ + } __attribute__((__packed__)); + } __attribute__((__packed__)); +} __attribute__((__packed__)) EFS_DF_CREDENTIAL_HEADER; typedef EFS_DF_CREDENTIAL_HEADER EFS_DF_CRED_HEADER; /** * struct EFS_DF_CERTIFICATE_THUMBPRINT_HEADER - */ -typedef struct -{ - /* 0*/ - u32 thumbprint_offset; /* Offset in bytes to the thumbprint. */ - u32 thumbprint_size; /* Size of thumbprint in bytes. */ - /* 8*/ u32 container_name_offset; /* Offset in bytes to the name of the - container from start of this - structure or 0 if no name present. */ - u32 provider_name_offset; /* Offset in bytes to the name of the - cryptographic provider from start of - this structure or 0 if no name - present. */ - /* 16*/ u32 user_name_offset; /* Offset in bytes to the user name - from start of this structure or 0 if - no user name present. (This is also - known as lpDisplayInformation.) */ -} __attribute__( ( __packed__ ) ) EFS_DF_CERTIFICATE_THUMBPRINT_HEADER; +typedef struct { +/* 0*/ u32 thumbprint_offset; /* Offset in bytes to the thumbprint. */ + u32 thumbprint_size; /* Size of thumbprint in bytes. */ +/* 8*/ u32 container_name_offset; /* Offset in bytes to the name of the + container from start of this + structure or 0 if no name present. */ + u32 provider_name_offset; /* Offset in bytes to the name of the + cryptographic provider from start of + this structure or 0 if no name + present. */ +/* 16*/ u32 user_name_offset; /* Offset in bytes to the user name + from start of this structure or 0 if + no user name present. (This is also + known as lpDisplayInformation.) */ +} __attribute__((__packed__)) EFS_DF_CERTIFICATE_THUMBPRINT_HEADER; typedef EFS_DF_CERTIFICATE_THUMBPRINT_HEADER EFS_DF_CERT_THUMBPRINT_HEADER; -typedef enum -{ - INTX_SYMBOLIC_LINK = - const_cpu_to_le64( 0x014B4E4C78746E49ULL ), /* "IntxLNK\1" */ - INTX_CHARACTER_DEVICE = - const_cpu_to_le64( 0x0052484378746E49ULL ), /* "IntxCHR\0" */ - INTX_BLOCK_DEVICE = - const_cpu_to_le64( 0x004B4C4278746E49ULL ), /* "IntxBLK\0" */ +typedef enum { + INTX_SYMBOLIC_LINK = + const_cpu_to_le64(0x014B4E4C78746E49ULL), /* "IntxLNK\1" */ + INTX_CHARACTER_DEVICE = + const_cpu_to_le64(0x0052484378746E49ULL), /* "IntxCHR\0" */ + INTX_BLOCK_DEVICE = + const_cpu_to_le64(0x004B4C4278746E49ULL), /* "IntxBLK\0" */ } INTX_FILE_TYPES; -typedef struct -{ - INTX_FILE_TYPES magic; /* Intx file magic. */ - union - { - /* For character and block devices. */ - struct - { - u64 major; /* Major device number. */ - u64 minor; /* Minor device number. */ - void *device_end[0]; /* Marker for offsetof(). */ - } __attribute__( ( __packed__ ) ); - /* For symbolic links. */ - ntfschar target[0]; - } __attribute__( ( __packed__ ) ); -} __attribute__( ( __packed__ ) ) INTX_FILE; +typedef struct { + INTX_FILE_TYPES magic; /* Intx file magic. */ + union { + /* For character and block devices. */ + struct { + u64 major; /* Major device number. */ + u64 minor; /* Minor device number. */ + void *device_end[0]; /* Marker for offsetof(). */ + } __attribute__((__packed__)); + /* For symbolic links. */ + ntfschar target[0]; + } __attribute__((__packed__)); +} __attribute__((__packed__)) INTX_FILE; #endif /* defined _NTFS_LAYOUT_H */ diff --git a/source/libntfs/lcnalloc.c b/source/libntfs/lcnalloc.c index 5399503d..e84d2431 100644 --- a/source/libntfs/lcnalloc.c +++ b/source/libntfs/lcnalloc.c @@ -48,177 +48,157 @@ /* * Plenty possibilities for big optimizations all over in the cluster - * allocation, however at the moment the dominant bottleneck (~ 90%) is + * allocation, however at the moment the dominant bottleneck (~ 90%) is * the update of the mapping pairs which converges to the cubic Faulhaber's * formula as the function of the number of extents (fragments, runs). */ #define NTFS_LCNALLOC_BSIZE 4096 #define NTFS_LCNALLOC_SKIP NTFS_LCNALLOC_BSIZE -enum -{ - ZONE_MFT = 1, - ZONE_DATA1 = 2, - ZONE_DATA2 = 4 +enum { + ZONE_MFT = 1, + ZONE_DATA1 = 2, + ZONE_DATA2 = 4 } ; -static void ntfs_cluster_set_zone_pos( LCN start, LCN end, LCN *pos, LCN tc ) +static void ntfs_cluster_set_zone_pos(LCN start, LCN end, LCN *pos, LCN tc) { - ntfs_log_trace( "pos: %lld tc: %lld\n", ( long long )*pos, ( long long )tc ); + ntfs_log_trace("pos: %lld tc: %lld\n", (long long)*pos, (long long)tc); - if ( tc >= end ) - *pos = start; - else if ( tc >= start ) - *pos = tc; + if (tc >= end) + *pos = start; + else if (tc >= start) + *pos = tc; } -static void ntfs_cluster_update_zone_pos( ntfs_volume *vol, u8 zone, LCN tc ) +static void ntfs_cluster_update_zone_pos(ntfs_volume *vol, u8 zone, LCN tc) { - ntfs_log_trace( "tc = %lld, zone = %d\n", ( long long )tc, zone ); - - if ( zone == ZONE_MFT ) - ntfs_cluster_set_zone_pos( vol->mft_lcn, vol->mft_zone_end, - &vol->mft_zone_pos, tc ); - else if ( zone == ZONE_DATA1 ) - ntfs_cluster_set_zone_pos( vol->mft_zone_end, vol->nr_clusters, - &vol->data1_zone_pos, tc ); - else /* zone == ZONE_DATA2 */ - ntfs_cluster_set_zone_pos( 0, vol->mft_zone_start, - &vol->data2_zone_pos, tc ); + ntfs_log_trace("tc = %lld, zone = %d\n", (long long)tc, zone); + + if (zone == ZONE_MFT) + ntfs_cluster_set_zone_pos(vol->mft_lcn, vol->mft_zone_end, + &vol->mft_zone_pos, tc); + else if (zone == ZONE_DATA1) + ntfs_cluster_set_zone_pos(vol->mft_zone_end, vol->nr_clusters, + &vol->data1_zone_pos, tc); + else /* zone == ZONE_DATA2 */ + ntfs_cluster_set_zone_pos(0, vol->mft_zone_start, + &vol->data2_zone_pos, tc); } /* - * Unmark full zones when a cluster has been freed in a full zone + * Unmark full zones when a cluster has been freed in a full zone * - * Next allocation will reuse the freed cluster + * Next allocation will reuse the freed cluster */ -static void update_full_status( ntfs_volume *vol, LCN lcn ) +static void update_full_status(ntfs_volume *vol, LCN lcn) { - if ( lcn >= vol->mft_zone_end ) - { - if ( vol->full_zones & ZONE_DATA1 ) - { - ntfs_cluster_update_zone_pos( vol, ZONE_DATA1, lcn ); - vol->full_zones &= ~ZONE_DATA1; - } - } - else if ( lcn < vol->mft_zone_start ) - { - if ( vol->full_zones & ZONE_DATA2 ) - { - ntfs_cluster_update_zone_pos( vol, ZONE_DATA2, lcn ); - vol->full_zones &= ~ZONE_DATA2; - } - } - else - { - if ( vol->full_zones & ZONE_MFT ) - { - ntfs_cluster_update_zone_pos( vol, ZONE_MFT, lcn ); - vol->full_zones &= ~ZONE_MFT; - } - } + if (lcn >= vol->mft_zone_end) { + if (vol->full_zones & ZONE_DATA1) { + ntfs_cluster_update_zone_pos(vol, ZONE_DATA1, lcn); + vol->full_zones &= ~ZONE_DATA1; + } + } else + if (lcn < vol->mft_zone_start) { + if (vol->full_zones & ZONE_DATA2) { + ntfs_cluster_update_zone_pos(vol, ZONE_DATA2, lcn); + vol->full_zones &= ~ZONE_DATA2; + } + } else { + if (vol->full_zones & ZONE_MFT) { + ntfs_cluster_update_zone_pos(vol, ZONE_MFT, lcn); + vol->full_zones &= ~ZONE_MFT; + } + } +} + +static s64 max_empty_bit_range(unsigned char *buf, int size) +{ + int i, j, run = 0; + int max_range = 0; + s64 start_pos = -1; + + ntfs_log_trace("Entering\n"); + + i = 0; + while (i < size) { + switch (*buf) { + case 0 : + do { + buf++; + run += 8; + i++; + } while ((i < size) && !*buf); + break; + case 255 : + if (run > max_range) { + max_range = run; + start_pos = (s64)i * 8 - run; + } + run = 0; + do { + buf++; + i++; + } while ((i < size) && (*buf == 255)); + break; + default : + for (j = 0; j < 8; j++) { + + int bit = *buf & (1 << j); + + if (bit) { + if (run > max_range) { + max_range = run; + start_pos = (s64)i * 8 + (j - run); + } + run = 0; + } else + run++; + } + i++; + buf++; + + } + } + + if (run > max_range) + start_pos = (s64)i * 8 - run; + + return start_pos; } -static s64 max_empty_bit_range( unsigned char *buf, int size ) +static int bitmap_writeback(ntfs_volume *vol, s64 pos, s64 size, void *b, + u8 *writeback) { - int i, j, run = 0; - int max_range = 0; - s64 start_pos = -1; + s64 written; + + ntfs_log_trace("Entering\n"); + + if (!*writeback) + return 0; + + *writeback = 0; + + written = ntfs_attr_pwrite(vol->lcnbmp_na, pos, size, b); + if (written != size) { + if (!written) + errno = EIO; + ntfs_log_perror("Bitmap write error (%lld, %lld)", + (long long)pos, (long long)size); + return -1; + } - ntfs_log_trace( "Entering\n" ); - - i = 0; - while ( i < size ) - { - switch ( *buf ) - { - case 0 : - do - { - buf++; - run += 8; - i++; - } - while ( ( i < size ) && !*buf ); - break; - case 255 : - if ( run > max_range ) - { - max_range = run; - start_pos = ( s64 )i * 8 - run; - } - run = 0; - do - { - buf++; - i++; - } - while ( ( i < size ) && ( *buf == 255 ) ); - break; - default : - for ( j = 0; j < 8; j++ ) - { - - int bit = *buf & ( 1 << j ); - - if ( bit ) - { - if ( run > max_range ) - { - max_range = run; - start_pos = ( s64 )i * 8 + ( j - run ); - } - run = 0; - } - else - run++; - } - i++; - buf++; - - } - } - - if ( run > max_range ) - start_pos = ( s64 )i * 8 - run; - - return start_pos; -} - -static int bitmap_writeback( ntfs_volume *vol, s64 pos, s64 size, void *b, - u8 *writeback ) -{ - s64 written; - - ntfs_log_trace( "Entering\n" ); - - if ( !*writeback ) - return 0; - - *writeback = 0; - - written = ntfs_attr_pwrite( vol->lcnbmp_na, pos, size, b ); - if ( written != size ) - { - if ( !written ) - errno = EIO; - ntfs_log_perror( "Bitmap write error (%lld, %lld)", - ( long long )pos, ( long long )size ); - return -1; - } - - return 0; + return 0; } /** * ntfs_cluster_alloc - allocate clusters on an ntfs volume - * @vol: mounted ntfs volume on which to allocate the clusters - * @start_vcn: vcn to use for the first allocated cluster - * @count: number of clusters to allocate - * @start_lcn: starting lcn at which to allocate the clusters (or -1 if none) - * @zone: zone from which to allocate the clusters + * @vol: mounted ntfs volume on which to allocate the clusters + * @start_vcn: vcn to use for the first allocated cluster + * @count: number of clusters to allocate + * @start_lcn: starting lcn at which to allocate the clusters (or -1 if none) + * @zone: zone from which to allocate the clusters * * Allocate @count clusters preferably starting at cluster @start_lcn or at the * current allocator position if @start_lcn is -1, on the mounted ntfs volume @@ -240,477 +220,437 @@ static int bitmap_writeback( ntfs_volume *vol, s64 pos, s64 size, void *b, * expanded to cover the start of the volume in order to reserve space for the * mft bitmap attribute. * - * The complexity stems from the need of implementing the mft vs data zoned - * approach and from the fact that we have access to the lcn bitmap via up to - * NTFS_LCNALLOC_BSIZE bytes at a time, so we need to cope with crossing over - * boundaries of two buffers. Further, the fact that the allocator allows for - * caller supplied hints as to the location of where allocation should begin - * and the fact that the allocator keeps track of where in the data zones the - * next natural allocation should occur, contribute to the complexity of the - * function. But it should all be worthwhile, because this allocator: + * The complexity stems from the need of implementing the mft vs data zoned + * approach and from the fact that we have access to the lcn bitmap via up to + * NTFS_LCNALLOC_BSIZE bytes at a time, so we need to cope with crossing over + * boundaries of two buffers. Further, the fact that the allocator allows for + * caller supplied hints as to the location of where allocation should begin + * and the fact that the allocator keeps track of where in the data zones the + * next natural allocation should occur, contribute to the complexity of the + * function. But it should all be worthwhile, because this allocator: * 1) implements MFT zone reservation - * 2) causes reduction in fragmentation. + * 2) causes reduction in fragmentation. * The code is not optimized for speed. */ -runlist *ntfs_cluster_alloc( ntfs_volume *vol, VCN start_vcn, s64 count, - LCN start_lcn, const NTFS_CLUSTER_ALLOCATION_ZONES zone ) +runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count, + LCN start_lcn, const NTFS_CLUSTER_ALLOCATION_ZONES zone) { - LCN zone_start, zone_end; /* current search range */ - LCN last_read_pos, lcn; - LCN bmp_pos; /* current bit position inside the bitmap */ - LCN prev_lcn = 0, prev_run_len = 0; - s64 clusters, br; - runlist *rl = NULL, *trl; - u8 *buf, *byte, bit, writeback; - u8 pass = 1; /* 1: inside zone; 2: start of zone */ - u8 search_zone; /* 4: data2 (start) 1: mft (middle) 2: data1 (end) */ - u8 done_zones = 0; - u8 has_guess, used_zone_pos; - int err = 0, rlpos, rlsize, buf_size; + LCN zone_start, zone_end; /* current search range */ + LCN last_read_pos, lcn; + LCN bmp_pos; /* current bit position inside the bitmap */ + LCN prev_lcn = 0, prev_run_len = 0; + s64 clusters, br; + runlist *rl = NULL, *trl; + u8 *buf, *byte, bit, writeback; + u8 pass = 1; /* 1: inside zone; 2: start of zone */ + u8 search_zone; /* 4: data2 (start) 1: mft (middle) 2: data1 (end) */ + u8 done_zones = 0; + u8 has_guess, used_zone_pos; + int err = 0, rlpos, rlsize, buf_size; - ntfs_log_enter( "Entering with count = 0x%llx, start_lcn = 0x%llx, " - "zone = %s_ZONE.\n", ( long long )count, ( long long ) - start_lcn, zone == MFT_ZONE ? "MFT" : "DATA" ); + ntfs_log_enter("Entering with count = 0x%llx, start_lcn = 0x%llx, " + "zone = %s_ZONE.\n", (long long)count, (long long) + start_lcn, zone == MFT_ZONE ? "MFT" : "DATA"); + + if (!vol || count < 0 || start_lcn < -1 || !vol->lcnbmp_na || + (s8)zone < FIRST_ZONE || zone > LAST_ZONE) { + errno = EINVAL; + ntfs_log_perror("%s: vcn: %lld, count: %lld, lcn: %lld", + __FUNCTION__, (long long)start_vcn, + (long long)count, (long long)start_lcn); + goto out; + } - if ( !vol || count < 0 || start_lcn < -1 || !vol->lcnbmp_na || - ( s8 )zone < FIRST_ZONE || zone > LAST_ZONE ) - { - errno = EINVAL; - ntfs_log_perror( "%s: vcn: %lld, count: %lld, lcn: %lld", - __FUNCTION__, ( long long )start_vcn, - ( long long )count, ( long long )start_lcn ); - goto out; - } + /* Return empty runlist if @count == 0 */ + if (!count) { + rl = ntfs_malloc(0x1000); + if (rl) { + rl[0].vcn = start_vcn; + rl[0].lcn = LCN_RL_NOT_MAPPED; + rl[0].length = 0; + } + goto out; + } - /* Return empty runlist if @count == 0 */ - if ( !count ) - { - rl = ntfs_malloc( 0x1000 ); - if ( rl ) - { - rl[0].vcn = start_vcn; - rl[0].lcn = LCN_RL_NOT_MAPPED; - rl[0].length = 0; - } - goto out; - } + buf = ntfs_malloc(NTFS_LCNALLOC_BSIZE); + if (!buf) + goto out; + /* + * If no @start_lcn was requested, use the current zone + * position otherwise use the requested @start_lcn. + */ + has_guess = 1; + zone_start = start_lcn; + + if (zone_start < 0) { + if (zone == DATA_ZONE) + zone_start = vol->data1_zone_pos; + else + zone_start = vol->mft_zone_pos; + has_guess = 0; + } + + used_zone_pos = has_guess ? 0 : 1; + + if (!zone_start || zone_start == vol->mft_zone_start || + zone_start == vol->mft_zone_end) + pass = 2; + + if (zone_start < vol->mft_zone_start) { + zone_end = vol->mft_zone_start; + search_zone = ZONE_DATA2; + } else if (zone_start < vol->mft_zone_end) { + zone_end = vol->mft_zone_end; + search_zone = ZONE_MFT; + } else { + zone_end = vol->nr_clusters; + search_zone = ZONE_DATA1; + } + + bmp_pos = zone_start; - buf = ntfs_malloc( NTFS_LCNALLOC_BSIZE ); - if ( !buf ) - goto out; - /* - * If no @start_lcn was requested, use the current zone - * position otherwise use the requested @start_lcn. - */ - has_guess = 1; - zone_start = start_lcn; + /* Loop until all clusters are allocated. */ + clusters = count; + rlpos = rlsize = 0; + while (1) { + /* check whether we have exhausted the current zone */ + if (search_zone & vol->full_zones) + goto zone_pass_done; + last_read_pos = bmp_pos >> 3; + br = ntfs_attr_pread(vol->lcnbmp_na, last_read_pos, + NTFS_LCNALLOC_BSIZE, buf); + if (br <= 0) { + if (!br) + goto zone_pass_done; + err = errno; + ntfs_log_perror("Reading $BITMAP failed"); + goto err_ret; + } + /* + * We might have read less than NTFS_LCNALLOC_BSIZE bytes + * if we are close to the end of the attribute. + */ + buf_size = (int)br << 3; + lcn = bmp_pos & 7; + bmp_pos &= ~7; + writeback = 0; + + while (lcn < buf_size) { + byte = buf + (lcn >> 3); + bit = 1 << (lcn & 7); + if (has_guess) { + if (*byte & bit) { + has_guess = 0; + break; + } + } else { + lcn = max_empty_bit_range(buf, br); + if (lcn < 0) + break; + has_guess = 1; + continue; + } - if ( zone_start < 0 ) - { - if ( zone == DATA_ZONE ) - zone_start = vol->data1_zone_pos; - else - zone_start = vol->mft_zone_pos; - has_guess = 0; - } - - used_zone_pos = has_guess ? 0 : 1; - - if ( !zone_start || zone_start == vol->mft_zone_start || - zone_start == vol->mft_zone_end ) - pass = 2; - - if ( zone_start < vol->mft_zone_start ) - { - zone_end = vol->mft_zone_start; - search_zone = ZONE_DATA2; - } - else if ( zone_start < vol->mft_zone_end ) - { - zone_end = vol->mft_zone_end; - search_zone = ZONE_MFT; - } - else - { - zone_end = vol->nr_clusters; - search_zone = ZONE_DATA1; - } - - bmp_pos = zone_start; - - /* Loop until all clusters are allocated. */ - clusters = count; - rlpos = rlsize = 0; - while ( 1 ) - { - /* check whether we have exhausted the current zone */ - if ( search_zone & vol->full_zones ) - goto zone_pass_done; - last_read_pos = bmp_pos >> 3; - br = ntfs_attr_pread( vol->lcnbmp_na, last_read_pos, - NTFS_LCNALLOC_BSIZE, buf ); - if ( br <= 0 ) - { - if ( !br ) - goto zone_pass_done; - err = errno; - ntfs_log_perror( "Reading $BITMAP failed" ); - goto err_ret; - } - /* - * We might have read less than NTFS_LCNALLOC_BSIZE bytes - * if we are close to the end of the attribute. - */ - buf_size = ( int )br << 3; - lcn = bmp_pos & 7; - bmp_pos &= ~7; - writeback = 0; - - while ( lcn < buf_size ) - { - byte = buf + ( lcn >> 3 ); - bit = 1 << ( lcn & 7 ); - if ( has_guess ) - { - if ( *byte & bit ) - { - has_guess = 0; - break; - } - } - else - { - lcn = max_empty_bit_range( buf, br ); - if ( lcn < 0 ) - break; - has_guess = 1; - continue; - } - - /* First free bit is at lcn + bmp_pos. */ - - /* Reallocate memory if necessary. */ - if ( ( rlpos + 2 ) * ( int )sizeof( runlist ) >= rlsize ) - { - rlsize += 4096; - trl = realloc( rl, rlsize ); - if ( !trl ) - { - err = ENOMEM; - ntfs_log_perror( "realloc() failed" ); - goto wb_err_ret; - } - rl = trl; - } - - /* Allocate the bitmap bit. */ - *byte |= bit; - writeback = 1; - if ( vol->free_clusters <= 0 ) - ntfs_log_error( "Non-positive free clusters " - "(%lld)!\n", - ( long long )vol->free_clusters ); - else - vol->free_clusters--; - - /* - * Coalesce with previous run if adjacent LCNs. - * Otherwise, append a new run. - */ - if ( prev_lcn == lcn + bmp_pos - prev_run_len && rlpos ) - { - ntfs_log_debug( "Cluster coalesce: prev_lcn: " - "%lld lcn: %lld bmp_pos: %lld " - "prev_run_len: %lld\n", - ( long long )prev_lcn, - ( long long )lcn, ( long long )bmp_pos, - ( long long )prev_run_len ); - rl[rlpos - 1].length = ++prev_run_len; - } - else - { - if ( rlpos ) - rl[rlpos].vcn = rl[rlpos - 1].vcn + - prev_run_len; - else - { - rl[rlpos].vcn = start_vcn; - ntfs_log_debug( "Start_vcn: %lld\n", - ( long long )start_vcn ); - } - - rl[rlpos].lcn = prev_lcn = lcn + bmp_pos; - rl[rlpos].length = prev_run_len = 1; - rlpos++; - } - - ntfs_log_debug( "RUN: %-16lld %-16lld %-16lld\n", - ( long long )rl[rlpos - 1].vcn, - ( long long )rl[rlpos - 1].lcn, - ( long long )rl[rlpos - 1].length ); - /* Done? */ - if ( !--clusters ) - { - if ( used_zone_pos ) - ntfs_cluster_update_zone_pos( vol, - search_zone, lcn + bmp_pos + 1 + - NTFS_LCNALLOC_SKIP ); - goto done_ret; - } - - lcn++; - } - - if ( bitmap_writeback( vol, last_read_pos, br, buf, &writeback ) ) - { - err = errno; - goto err_ret; - } - - if ( !used_zone_pos ) - { - - used_zone_pos = 1; - - if ( search_zone == ZONE_MFT ) - zone_start = vol->mft_zone_pos; - else if ( search_zone == ZONE_DATA1 ) - zone_start = vol->data1_zone_pos; - else - zone_start = vol->data2_zone_pos; - - if ( !zone_start || zone_start == vol->mft_zone_start || - zone_start == vol->mft_zone_end ) - pass = 2; - bmp_pos = zone_start; - } - else - bmp_pos += buf_size; - - if ( bmp_pos < zone_end ) - continue; + /* First free bit is at lcn + bmp_pos. */ + + /* Reallocate memory if necessary. */ + if ((rlpos + 2) * (int)sizeof(runlist) >= rlsize) { + rlsize += 4096; + trl = realloc(rl, rlsize); + if (!trl) { + err = ENOMEM; + ntfs_log_perror("realloc() failed"); + goto wb_err_ret; + } + rl = trl; + } + + /* Allocate the bitmap bit. */ + *byte |= bit; + writeback = 1; + if (vol->free_clusters <= 0) + ntfs_log_error("Non-positive free clusters " + "(%lld)!\n", + (long long)vol->free_clusters); + else + vol->free_clusters--; + + /* + * Coalesce with previous run if adjacent LCNs. + * Otherwise, append a new run. + */ + if (prev_lcn == lcn + bmp_pos - prev_run_len && rlpos) { + ntfs_log_debug("Cluster coalesce: prev_lcn: " + "%lld lcn: %lld bmp_pos: %lld " + "prev_run_len: %lld\n", + (long long)prev_lcn, + (long long)lcn, (long long)bmp_pos, + (long long)prev_run_len); + rl[rlpos - 1].length = ++prev_run_len; + } else { + if (rlpos) + rl[rlpos].vcn = rl[rlpos - 1].vcn + + prev_run_len; + else { + rl[rlpos].vcn = start_vcn; + ntfs_log_debug("Start_vcn: %lld\n", + (long long)start_vcn); + } + + rl[rlpos].lcn = prev_lcn = lcn + bmp_pos; + rl[rlpos].length = prev_run_len = 1; + rlpos++; + } + + ntfs_log_debug("RUN: %-16lld %-16lld %-16lld\n", + (long long)rl[rlpos - 1].vcn, + (long long)rl[rlpos - 1].lcn, + (long long)rl[rlpos - 1].length); + /* Done? */ + if (!--clusters) { + if (used_zone_pos) + ntfs_cluster_update_zone_pos(vol, + search_zone, lcn + bmp_pos + 1 + + NTFS_LCNALLOC_SKIP); + goto done_ret; + } + + lcn++; + } + + if (bitmap_writeback(vol, last_read_pos, br, buf, &writeback)) { + err = errno; + goto err_ret; + } + + if (!used_zone_pos) { + + used_zone_pos = 1; + + if (search_zone == ZONE_MFT) + zone_start = vol->mft_zone_pos; + else if (search_zone == ZONE_DATA1) + zone_start = vol->data1_zone_pos; + else + zone_start = vol->data2_zone_pos; + + if (!zone_start || zone_start == vol->mft_zone_start || + zone_start == vol->mft_zone_end) + pass = 2; + bmp_pos = zone_start; + } else + bmp_pos += buf_size; + + if (bmp_pos < zone_end) + continue; zone_pass_done: - ntfs_log_trace( "Finished current zone pass(%i).\n", pass ); - if ( pass == 1 ) - { - pass = 2; - zone_end = zone_start; - - if ( search_zone == ZONE_MFT ) - zone_start = vol->mft_zone_start; - else if ( search_zone == ZONE_DATA1 ) - zone_start = vol->mft_zone_end; - else - zone_start = 0; - - /* Sanity check. */ - if ( zone_end < zone_start ) - zone_end = zone_start; - - bmp_pos = zone_start; - - continue; - } - /* pass == 2 */ + ntfs_log_trace("Finished current zone pass(%i).\n", pass); + if (pass == 1) { + pass = 2; + zone_end = zone_start; + + if (search_zone == ZONE_MFT) + zone_start = vol->mft_zone_start; + else if (search_zone == ZONE_DATA1) + zone_start = vol->mft_zone_end; + else + zone_start = 0; + + /* Sanity check. */ + if (zone_end < zone_start) + zone_end = zone_start; + + bmp_pos = zone_start; + + continue; + } + /* pass == 2 */ done_zones_check: - done_zones |= search_zone; - vol->full_zones |= search_zone; - if ( done_zones < ( ZONE_MFT + ZONE_DATA1 + ZONE_DATA2 ) ) - { - ntfs_log_trace( "Switching zone.\n" ); - pass = 1; - if ( rlpos ) - { - LCN tc = rl[rlpos - 1].lcn + - rl[rlpos - 1].length + NTFS_LCNALLOC_SKIP; - - if ( used_zone_pos ) - ntfs_cluster_update_zone_pos( vol, - search_zone, tc ); - } - - switch ( search_zone ) - { - case ZONE_MFT: - ntfs_log_trace( "Zone switch: mft -> data1\n" ); -switch_to_data1_zone: search_zone = ZONE_DATA1; - zone_start = vol->data1_zone_pos; - zone_end = vol->nr_clusters; - if ( zone_start == vol->mft_zone_end ) - pass = 2; - break; - case ZONE_DATA1: - ntfs_log_trace( "Zone switch: data1 -> data2\n" ); - search_zone = ZONE_DATA2; - zone_start = vol->data2_zone_pos; - zone_end = vol->mft_zone_start; - if ( !zone_start ) - pass = 2; - break; - case ZONE_DATA2: - if ( !( done_zones & ZONE_DATA1 ) ) - { - ntfs_log_trace( "data2 -> data1\n" ); - goto switch_to_data1_zone; - } - ntfs_log_trace( "Zone switch: data2 -> mft\n" ); - search_zone = ZONE_MFT; - zone_start = vol->mft_zone_pos; - zone_end = vol->mft_zone_end; - if ( zone_start == vol->mft_zone_start ) - pass = 2; - break; - } - - bmp_pos = zone_start; - - if ( zone_start == zone_end ) - { - ntfs_log_trace( "Empty zone, skipped.\n" ); - goto done_zones_check; - } - - continue; - } - - ntfs_log_trace( "All zones are finished, no space on device.\n" ); - err = ENOSPC; - goto err_ret; - } + done_zones |= search_zone; + vol->full_zones |= search_zone; + if (done_zones < (ZONE_MFT + ZONE_DATA1 + ZONE_DATA2)) { + ntfs_log_trace("Switching zone.\n"); + pass = 1; + if (rlpos) { + LCN tc = rl[rlpos - 1].lcn + + rl[rlpos - 1].length + NTFS_LCNALLOC_SKIP; + + if (used_zone_pos) + ntfs_cluster_update_zone_pos(vol, + search_zone, tc); + } + + switch (search_zone) { + case ZONE_MFT: + ntfs_log_trace("Zone switch: mft -> data1\n"); +switch_to_data1_zone: search_zone = ZONE_DATA1; + zone_start = vol->data1_zone_pos; + zone_end = vol->nr_clusters; + if (zone_start == vol->mft_zone_end) + pass = 2; + break; + case ZONE_DATA1: + ntfs_log_trace("Zone switch: data1 -> data2\n"); + search_zone = ZONE_DATA2; + zone_start = vol->data2_zone_pos; + zone_end = vol->mft_zone_start; + if (!zone_start) + pass = 2; + break; + case ZONE_DATA2: + if (!(done_zones & ZONE_DATA1)) { + ntfs_log_trace("data2 -> data1\n"); + goto switch_to_data1_zone; + } + ntfs_log_trace("Zone switch: data2 -> mft\n"); + search_zone = ZONE_MFT; + zone_start = vol->mft_zone_pos; + zone_end = vol->mft_zone_end; + if (zone_start == vol->mft_zone_start) + pass = 2; + break; + } + + bmp_pos = zone_start; + + if (zone_start == zone_end) { + ntfs_log_trace("Empty zone, skipped.\n"); + goto done_zones_check; + } + + continue; + } + + ntfs_log_trace("All zones are finished, no space on device.\n"); + err = ENOSPC; + goto err_ret; + } done_ret: - ntfs_log_debug( "At done_ret.\n" ); - /* Add runlist terminator element. */ - rl[rlpos].vcn = rl[rlpos - 1].vcn + rl[rlpos - 1].length; - rl[rlpos].lcn = LCN_RL_NOT_MAPPED; - rl[rlpos].length = 0; - if ( bitmap_writeback( vol, last_read_pos, br, buf, &writeback ) ) - { - err = errno; - goto err_ret; - } + ntfs_log_debug("At done_ret.\n"); + /* Add runlist terminator element. */ + rl[rlpos].vcn = rl[rlpos - 1].vcn + rl[rlpos - 1].length; + rl[rlpos].lcn = LCN_RL_NOT_MAPPED; + rl[rlpos].length = 0; + if (bitmap_writeback(vol, last_read_pos, br, buf, &writeback)) { + err = errno; + goto err_ret; + } done_err_ret: - free( buf ); - if ( err ) - { - errno = err; - ntfs_log_perror( "Failed to allocate clusters" ); - rl = NULL; - } -out: - ntfs_log_leave( "\n" ); - return rl; + free(buf); + if (err) { + errno = err; + ntfs_log_perror("Failed to allocate clusters"); + rl = NULL; + } +out: + ntfs_log_leave("\n"); + return rl; wb_err_ret: - ntfs_log_trace( "At wb_err_ret.\n" ); - if ( bitmap_writeback( vol, last_read_pos, br, buf, &writeback ) ) - err = errno; + ntfs_log_trace("At wb_err_ret.\n"); + if (bitmap_writeback(vol, last_read_pos, br, buf, &writeback)) + err = errno; err_ret: - ntfs_log_trace( "At err_ret.\n" ); - if ( rl ) - { - /* Add runlist terminator element. */ - rl[rlpos].vcn = rl[rlpos - 1].vcn + rl[rlpos - 1].length; - rl[rlpos].lcn = LCN_RL_NOT_MAPPED; - rl[rlpos].length = 0; - ntfs_debug_runlist_dump( rl ); - ntfs_cluster_free_from_rl( vol, rl ); - free( rl ); - rl = NULL; - } - goto done_err_ret; + ntfs_log_trace("At err_ret.\n"); + if (rl) { + /* Add runlist terminator element. */ + rl[rlpos].vcn = rl[rlpos - 1].vcn + rl[rlpos - 1].length; + rl[rlpos].lcn = LCN_RL_NOT_MAPPED; + rl[rlpos].length = 0; + ntfs_debug_runlist_dump(rl); + ntfs_cluster_free_from_rl(vol, rl); + free(rl); + rl = NULL; + } + goto done_err_ret; } /** * ntfs_cluster_free_from_rl - free clusters from runlist - * @vol: mounted ntfs volume on which to free the clusters - * @rl: runlist from which deallocate clusters + * @vol: mounted ntfs volume on which to free the clusters + * @rl: runlist from which deallocate clusters * * On success return 0 and on error return -1 with errno set to the error code. */ -int ntfs_cluster_free_from_rl( ntfs_volume *vol, runlist *rl ) +int ntfs_cluster_free_from_rl(ntfs_volume *vol, runlist *rl) { - s64 nr_freed = 0; - int ret = -1; + s64 nr_freed = 0; + int ret = -1; - ntfs_log_trace( "Entering.\n" ); + ntfs_log_trace("Entering.\n"); - for ( ; rl->length; rl++ ) - { + for (; rl->length; rl++) { - ntfs_log_trace( "Dealloc lcn 0x%llx, len 0x%llx.\n", - ( long long )rl->lcn, ( long long )rl->length ); + ntfs_log_trace("Dealloc lcn 0x%llx, len 0x%llx.\n", + (long long)rl->lcn, (long long)rl->length); - if ( rl->lcn >= 0 ) - { - update_full_status( vol, rl->lcn ); - if ( ntfs_bitmap_clear_run( vol->lcnbmp_na, rl->lcn, - rl->length ) ) - { - ntfs_log_perror( "Cluster deallocation failed " - "(%lld, %lld)", - ( long long )rl->lcn, - ( long long )rl->length ); - goto out; - } - nr_freed += rl->length ; - } - } + if (rl->lcn >= 0) { + update_full_status(vol,rl->lcn); + if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn, + rl->length)) { + ntfs_log_perror("Cluster deallocation failed " + "(%lld, %lld)", + (long long)rl->lcn, + (long long)rl->length); + goto out; + } + nr_freed += rl->length ; + } + } - ret = 0; + ret = 0; out: - vol->free_clusters += nr_freed; - if ( vol->free_clusters > vol->nr_clusters ) - ntfs_log_error( "Too many free clusters (%lld > %lld)!", - ( long long )vol->free_clusters, - ( long long )vol->nr_clusters ); - return ret; + vol->free_clusters += nr_freed; + if (vol->free_clusters > vol->nr_clusters) + ntfs_log_error("Too many free clusters (%lld > %lld)!", + (long long)vol->free_clusters, + (long long)vol->nr_clusters); + return ret; } /* - * Basic cluster run free - * Returns 0 if successful + * Basic cluster run free + * Returns 0 if successful */ -int ntfs_cluster_free_basic( ntfs_volume *vol, s64 lcn, s64 count ) +int ntfs_cluster_free_basic(ntfs_volume *vol, s64 lcn, s64 count) { - s64 nr_freed = 0; - int ret = -1; + s64 nr_freed = 0; + int ret = -1; - ntfs_log_trace( "Entering.\n" ); - ntfs_log_trace( "Dealloc lcn 0x%llx, len 0x%llx.\n", - ( long long )lcn, ( long long )count ); + ntfs_log_trace("Entering.\n"); + ntfs_log_trace("Dealloc lcn 0x%llx, len 0x%llx.\n", + (long long)lcn, (long long)count); - if ( lcn >= 0 ) - { - update_full_status( vol, lcn ); - if ( ntfs_bitmap_clear_run( vol->lcnbmp_na, lcn, - count ) ) - { - ntfs_log_perror( "Cluster deallocation failed " - "(%lld, %lld)", - ( long long )lcn, - ( long long )count ); - goto out; - } - nr_freed += count; - } - ret = 0; + if (lcn >= 0) { + update_full_status(vol,lcn); + if (ntfs_bitmap_clear_run(vol->lcnbmp_na, lcn, + count)) { + ntfs_log_perror("Cluster deallocation failed " + "(%lld, %lld)", + (long long)lcn, + (long long)count); + goto out; + } + nr_freed += count; + } + ret = 0; out: - vol->free_clusters += nr_freed; - if ( vol->free_clusters > vol->nr_clusters ) - ntfs_log_error( "Too many free clusters (%lld > %lld)!", - ( long long )vol->free_clusters, - ( long long )vol->nr_clusters ); - return ret; + vol->free_clusters += nr_freed; + if (vol->free_clusters > vol->nr_clusters) + ntfs_log_error("Too many free clusters (%lld > %lld)!", + (long long)vol->free_clusters, + (long long)vol->nr_clusters); + return ret; } /** * ntfs_cluster_free - free clusters on an ntfs volume - * @vol: mounted ntfs volume on which to free the clusters - * @na: attribute whose runlist describes the clusters to free - * @start_vcn: vcn in @rl at which to start freeing clusters - * @count: number of clusters to free or -1 for all clusters + * @vol: mounted ntfs volume on which to free the clusters + * @na: attribute whose runlist describes the clusters to free + * @start_vcn: vcn in @rl at which to start freeing clusters + * @count: number of clusters to free or -1 for all clusters * * Free @count clusters starting at the cluster @start_vcn in the runlist * described by the attribute @na from the mounted ntfs volume @vol. @@ -721,120 +661,111 @@ out: * On success return the number of deallocated clusters (not counting sparse * clusters) and on error return -1 with errno set to the error code. */ -int ntfs_cluster_free( ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, s64 count ) +int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, s64 count) { - runlist *rl; - s64 delta, to_free, nr_freed = 0; - int ret = -1; + runlist *rl; + s64 delta, to_free, nr_freed = 0; + int ret = -1; - if ( !vol || !vol->lcnbmp_na || !na || start_vcn < 0 || - ( count < 0 && count != -1 ) ) - { - ntfs_log_trace( "Invalid arguments!\n" ); - errno = EINVAL; - return -1; - } + if (!vol || !vol->lcnbmp_na || !na || start_vcn < 0 || + (count < 0 && count != -1)) { + ntfs_log_trace("Invalid arguments!\n"); + errno = EINVAL; + return -1; + } + + ntfs_log_enter("Entering for inode 0x%llx, attr 0x%x, count 0x%llx, " + "vcn 0x%llx.\n", (unsigned long long)na->ni->mft_no, + na->type, (long long)count, (long long)start_vcn); - ntfs_log_enter( "Entering for inode 0x%llx, attr 0x%x, count 0x%llx, " - "vcn 0x%llx.\n", ( unsigned long long )na->ni->mft_no, - na->type, ( long long )count, ( long long )start_vcn ); + rl = ntfs_attr_find_vcn(na, start_vcn); + if (!rl) { + if (errno == ENOENT) + ret = 0; + goto leave; + } - rl = ntfs_attr_find_vcn( na, start_vcn ); - if ( !rl ) - { - if ( errno == ENOENT ) - ret = 0; - goto leave; - } + if (rl->lcn < 0 && rl->lcn != LCN_HOLE) { + errno = EIO; + ntfs_log_perror("%s: Unexpected lcn (%lld)", __FUNCTION__, + (long long)rl->lcn); + goto leave; + } - if ( rl->lcn < 0 && rl->lcn != LCN_HOLE ) - { - errno = EIO; - ntfs_log_perror( "%s: Unexpected lcn (%lld)", __FUNCTION__, - ( long long )rl->lcn ); - goto leave; - } + /* Find the starting cluster inside the run that needs freeing. */ + delta = start_vcn - rl->vcn; - /* Find the starting cluster inside the run that needs freeing. */ - delta = start_vcn - rl->vcn; + /* The number of clusters in this run that need freeing. */ + to_free = rl->length - delta; + if (count >= 0 && to_free > count) + to_free = count; - /* The number of clusters in this run that need freeing. */ - to_free = rl->length - delta; - if ( count >= 0 && to_free > count ) - to_free = count; + if (rl->lcn != LCN_HOLE) { + /* Do the actual freeing of the clusters in this run. */ + update_full_status(vol,rl->lcn + delta); + if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn + delta, + to_free)) + goto leave; + nr_freed = to_free; + } - if ( rl->lcn != LCN_HOLE ) - { - /* Do the actual freeing of the clusters in this run. */ - update_full_status( vol, rl->lcn + delta ); - if ( ntfs_bitmap_clear_run( vol->lcnbmp_na, rl->lcn + delta, - to_free ) ) - goto leave; - nr_freed = to_free; - } + /* Go to the next run and adjust the number of clusters left to free. */ + ++rl; + if (count >= 0) + count -= to_free; - /* Go to the next run and adjust the number of clusters left to free. */ - ++rl; - if ( count >= 0 ) - count -= to_free; + /* + * Loop over the remaining runs, using @count as a capping value, and + * free them. + */ + for (; rl->length && count != 0; ++rl) { + // FIXME: Need to try ntfs_attr_map_runlist() for attribute + // list support! (AIA) + if (rl->lcn < 0 && rl->lcn != LCN_HOLE) { + // FIXME: Eeek! We need rollback! (AIA) + errno = EIO; + ntfs_log_perror("%s: Invalid lcn (%lli)", + __FUNCTION__, (long long)rl->lcn); + goto out; + } - /* - * Loop over the remaining runs, using @count as a capping value, and - * free them. - */ - for ( ; rl->length && count != 0; ++rl ) - { - // FIXME: Need to try ntfs_attr_map_runlist() for attribute - // list support! (AIA) - if ( rl->lcn < 0 && rl->lcn != LCN_HOLE ) - { - // FIXME: Eeek! We need rollback! (AIA) - errno = EIO; - ntfs_log_perror( "%s: Invalid lcn (%lli)", - __FUNCTION__, ( long long )rl->lcn ); - goto out; - } + /* The number of clusters in this run that need freeing. */ + to_free = rl->length; + if (count >= 0 && to_free > count) + to_free = count; - /* The number of clusters in this run that need freeing. */ - to_free = rl->length; - if ( count >= 0 && to_free > count ) - to_free = count; + if (rl->lcn != LCN_HOLE) { + update_full_status(vol,rl->lcn); + if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn, + to_free)) { + // FIXME: Eeek! We need rollback! (AIA) + ntfs_log_perror("%s: Clearing bitmap run failed", + __FUNCTION__); + goto out; + } + nr_freed += to_free; + } - if ( rl->lcn != LCN_HOLE ) - { - update_full_status( vol, rl->lcn ); - if ( ntfs_bitmap_clear_run( vol->lcnbmp_na, rl->lcn, - to_free ) ) - { - // FIXME: Eeek! We need rollback! (AIA) - ntfs_log_perror( "%s: Clearing bitmap run failed", - __FUNCTION__ ); - goto out; - } - nr_freed += to_free; - } + if (count >= 0) + count -= to_free; + } - if ( count >= 0 ) - count -= to_free; - } + if (count != -1 && count != 0) { + // FIXME: Eeek! BUG() + errno = EIO; + ntfs_log_perror("%s: count still not zero (%lld)", __FUNCTION__, + (long long)count); + goto out; + } - if ( count != -1 && count != 0 ) - { - // FIXME: Eeek! BUG() - errno = EIO; - ntfs_log_perror( "%s: count still not zero (%lld)", __FUNCTION__, - ( long long )count ); - goto out; - } - - ret = nr_freed; + ret = nr_freed; out: - vol->free_clusters += nr_freed ; - if ( vol->free_clusters > vol->nr_clusters ) - ntfs_log_error( "Too many free clusters (%lld > %lld)!", - ( long long )vol->free_clusters, - ( long long )vol->nr_clusters ); -leave: - ntfs_log_leave( "\n" ); - return ret; + vol->free_clusters += nr_freed ; + if (vol->free_clusters > vol->nr_clusters) + ntfs_log_error("Too many free clusters (%lld > %lld)!", + (long long)vol->free_clusters, + (long long)vol->nr_clusters); +leave: + ntfs_log_leave("\n"); + return ret; } diff --git a/source/libntfs/lcnalloc.h b/source/libntfs/lcnalloc.h index 001717a0..cbf4c5cd 100644 --- a/source/libntfs/lcnalloc.h +++ b/source/libntfs/lcnalloc.h @@ -1,6 +1,6 @@ /* * lcnalloc.h - Exports for cluster (de)allocation. Originated from the Linux-NTFS - * project. + * project. * * Copyright (c) 2002 Anton Altaparmakov * Copyright (c) 2004 Yura Pakhuchiy @@ -31,22 +31,21 @@ /** * enum NTFS_CLUSTER_ALLOCATION_ZONES - */ -typedef enum -{ - FIRST_ZONE = 0, /* For sanity checking. */ - MFT_ZONE = 0, /* Allocate from $MFT zone. */ - DATA_ZONE = 1, /* Allocate from $DATA zone. */ - LAST_ZONE = 1, /* For sanity checking. */ +typedef enum { + FIRST_ZONE = 0, /* For sanity checking. */ + MFT_ZONE = 0, /* Allocate from $MFT zone. */ + DATA_ZONE = 1, /* Allocate from $DATA zone. */ + LAST_ZONE = 1, /* For sanity checking. */ } NTFS_CLUSTER_ALLOCATION_ZONES; -extern runlist *ntfs_cluster_alloc( ntfs_volume *vol, VCN start_vcn, s64 count, - LCN start_lcn, const NTFS_CLUSTER_ALLOCATION_ZONES zone ); +extern runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count, + LCN start_lcn, const NTFS_CLUSTER_ALLOCATION_ZONES zone); -extern int ntfs_cluster_free_from_rl( ntfs_volume *vol, runlist *rl ); -extern int ntfs_cluster_free_basic( ntfs_volume *vol, s64 lcn, s64 count ); +extern int ntfs_cluster_free_from_rl(ntfs_volume *vol, runlist *rl); +extern int ntfs_cluster_free_basic(ntfs_volume *vol, s64 lcn, s64 count); -extern int ntfs_cluster_free( ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, - s64 count ); +extern int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, + s64 count); #endif /* defined _NTFS_LCNALLOC_H */ diff --git a/source/libntfs/logfile.c b/source/libntfs/logfile.c index d3c4ed4c..277ad142 100644 --- a/source/libntfs/logfile.c +++ b/source/libntfs/logfile.c @@ -45,8 +45,8 @@ /** * ntfs_check_restart_page_header - check the page header for consistency - * @rp: restart page header to check - * @pos: position in logfile at which the restart page header resides + * @rp: restart page header to check + * @pos: position in logfile at which the restart page header resides * * Check the restart page header @rp for consistency and return TRUE if it is * consistent and FALSE otherwise. @@ -54,108 +54,100 @@ * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not * require the full restart page. */ -static BOOL ntfs_check_restart_page_header( RESTART_PAGE_HEADER *rp, s64 pos ) +static BOOL ntfs_check_restart_page_header(RESTART_PAGE_HEADER *rp, s64 pos) { - u32 logfile_system_page_size, logfile_log_page_size; - u16 ra_ofs, usa_count, usa_ofs, usa_end = 0; - BOOL have_usa = TRUE; + u32 logfile_system_page_size, logfile_log_page_size; + u16 ra_ofs, usa_count, usa_ofs, usa_end = 0; + BOOL have_usa = TRUE; - ntfs_log_trace( "Entering.\n" ); - /* - * If the system or log page sizes are smaller than the ntfs block size - * or either is not a power of 2 we cannot handle this log file. - */ - logfile_system_page_size = le32_to_cpu( rp->system_page_size ); - logfile_log_page_size = le32_to_cpu( rp->log_page_size ); - if ( logfile_system_page_size < NTFS_BLOCK_SIZE || - logfile_log_page_size < NTFS_BLOCK_SIZE || - logfile_system_page_size & - ( logfile_system_page_size - 1 ) || - logfile_log_page_size & ( logfile_log_page_size - 1 ) ) - { - ntfs_log_error( "$LogFile uses unsupported page size.\n" ); - return FALSE; - } - /* - * We must be either at !pos (1st restart page) or at pos = system page - * size (2nd restart page). - */ - if ( pos && pos != logfile_system_page_size ) - { - ntfs_log_error( "Found restart area in incorrect " - "position in $LogFile.\n" ); - return FALSE; - } - /* We only know how to handle version 1.1. */ - if ( sle16_to_cpu( rp->major_ver ) != 1 || - sle16_to_cpu( rp->minor_ver ) != 1 ) - { - ntfs_log_error( "$LogFile version %i.%i is not " - "supported. (This driver supports version " - "1.1 only.)\n", ( int )sle16_to_cpu( rp->major_ver ), - ( int )sle16_to_cpu( rp->minor_ver ) ); - return FALSE; - } - /* - * If chkdsk has been run the restart page may not be protected by an - * update sequence array. - */ - if ( ntfs_is_chkd_record( rp->magic ) && !le16_to_cpu( rp->usa_count ) ) - { - have_usa = FALSE; - goto skip_usa_checks; - } - /* Verify the size of the update sequence array. */ - usa_count = 1 + ( logfile_system_page_size >> NTFS_BLOCK_SIZE_BITS ); - if ( usa_count != le16_to_cpu( rp->usa_count ) ) - { - ntfs_log_error( "$LogFile restart page specifies " - "inconsistent update sequence array count.\n" ); - return FALSE; - } - /* Verify the position of the update sequence array. */ - usa_ofs = le16_to_cpu( rp->usa_ofs ); - usa_end = usa_ofs + usa_count * sizeof( u16 ); - if ( usa_ofs < sizeof( RESTART_PAGE_HEADER ) || - usa_end > NTFS_BLOCK_SIZE - sizeof( u16 ) ) - { - ntfs_log_error( "$LogFile restart page specifies " - "inconsistent update sequence array offset.\n" ); - return FALSE; - } + ntfs_log_trace("Entering.\n"); + /* + * If the system or log page sizes are smaller than the ntfs block size + * or either is not a power of 2 we cannot handle this log file. + */ + logfile_system_page_size = le32_to_cpu(rp->system_page_size); + logfile_log_page_size = le32_to_cpu(rp->log_page_size); + if (logfile_system_page_size < NTFS_BLOCK_SIZE || + logfile_log_page_size < NTFS_BLOCK_SIZE || + logfile_system_page_size & + (logfile_system_page_size - 1) || + logfile_log_page_size & (logfile_log_page_size - 1)) { + ntfs_log_error("$LogFile uses unsupported page size.\n"); + return FALSE; + } + /* + * We must be either at !pos (1st restart page) or at pos = system page + * size (2nd restart page). + */ + if (pos && pos != logfile_system_page_size) { + ntfs_log_error("Found restart area in incorrect " + "position in $LogFile.\n"); + return FALSE; + } + /* We only know how to handle version 1.1. */ + if (sle16_to_cpu(rp->major_ver) != 1 || + sle16_to_cpu(rp->minor_ver) != 1) { + ntfs_log_error("$LogFile version %i.%i is not " + "supported. (This driver supports version " + "1.1 only.)\n", (int)sle16_to_cpu(rp->major_ver), + (int)sle16_to_cpu(rp->minor_ver)); + return FALSE; + } + /* + * If chkdsk has been run the restart page may not be protected by an + * update sequence array. + */ + if (ntfs_is_chkd_record(rp->magic) && !le16_to_cpu(rp->usa_count)) { + have_usa = FALSE; + goto skip_usa_checks; + } + /* Verify the size of the update sequence array. */ + usa_count = 1 + (logfile_system_page_size >> NTFS_BLOCK_SIZE_BITS); + if (usa_count != le16_to_cpu(rp->usa_count)) { + ntfs_log_error("$LogFile restart page specifies " + "inconsistent update sequence array count.\n"); + return FALSE; + } + /* Verify the position of the update sequence array. */ + usa_ofs = le16_to_cpu(rp->usa_ofs); + usa_end = usa_ofs + usa_count * sizeof(u16); + if (usa_ofs < sizeof(RESTART_PAGE_HEADER) || + usa_end > NTFS_BLOCK_SIZE - sizeof(u16)) { + ntfs_log_error("$LogFile restart page specifies " + "inconsistent update sequence array offset.\n"); + return FALSE; + } skip_usa_checks: - /* - * Verify the position of the restart area. It must be: - * - aligned to 8-byte boundary, - * - after the update sequence array, and - * - within the system page size. - */ - ra_ofs = le16_to_cpu( rp->restart_area_offset ); - if ( ra_ofs & 7 || ( have_usa ? ra_ofs < usa_end : - ra_ofs < sizeof( RESTART_PAGE_HEADER ) ) || - ra_ofs > logfile_system_page_size ) - { - ntfs_log_error( "$LogFile restart page specifies " - "inconsistent restart area offset.\n" ); - return FALSE; - } - /* - * Only restart pages modified by chkdsk are allowed to have chkdsk_lsn - * set. - */ - if ( !ntfs_is_chkd_record( rp->magic ) && sle64_to_cpu( rp->chkdsk_lsn ) ) - { - ntfs_log_error( "$LogFile restart page is not modified " - "by chkdsk but a chkdsk LSN is specified.\n" ); - return FALSE; - } - ntfs_log_trace( "Done.\n" ); - return TRUE; + /* + * Verify the position of the restart area. It must be: + * - aligned to 8-byte boundary, + * - after the update sequence array, and + * - within the system page size. + */ + ra_ofs = le16_to_cpu(rp->restart_area_offset); + if (ra_ofs & 7 || (have_usa ? ra_ofs < usa_end : + ra_ofs < sizeof(RESTART_PAGE_HEADER)) || + ra_ofs > logfile_system_page_size) { + ntfs_log_error("$LogFile restart page specifies " + "inconsistent restart area offset.\n"); + return FALSE; + } + /* + * Only restart pages modified by chkdsk are allowed to have chkdsk_lsn + * set. + */ + if (!ntfs_is_chkd_record(rp->magic) && sle64_to_cpu(rp->chkdsk_lsn)) { + ntfs_log_error("$LogFile restart page is not modified " + "by chkdsk but a chkdsk LSN is specified.\n"); + return FALSE; + } + ntfs_log_trace("Done.\n"); + return TRUE; } /** * ntfs_check_restart_area - check the restart area for consistency - * @rp: restart page whose restart area to check + * @rp: restart page whose restart area to check * * Check the restart area of the restart page @rp for consistency and return * TRUE if it is consistent and FALSE otherwise. @@ -166,118 +158,110 @@ skip_usa_checks: * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not * require the full restart page. */ -static BOOL ntfs_check_restart_area( RESTART_PAGE_HEADER *rp ) +static BOOL ntfs_check_restart_area(RESTART_PAGE_HEADER *rp) { - u64 file_size; - RESTART_AREA *ra; - u16 ra_ofs, ra_len, ca_ofs; - u8 fs_bits; + u64 file_size; + RESTART_AREA *ra; + u16 ra_ofs, ra_len, ca_ofs; + u8 fs_bits; - ntfs_log_trace( "Entering.\n" ); - ra_ofs = le16_to_cpu( rp->restart_area_offset ); - ra = ( RESTART_AREA* )( ( u8* )rp + ra_ofs ); - /* - * Everything before ra->file_size must be before the first word - * protected by an update sequence number. This ensures that it is - * safe to access ra->client_array_offset. - */ - if ( ra_ofs + offsetof( RESTART_AREA, file_size ) > - NTFS_BLOCK_SIZE - sizeof( u16 ) ) - { - ntfs_log_error( "$LogFile restart area specifies " - "inconsistent file offset.\n" ); - return FALSE; - } - /* - * Now that we can access ra->client_array_offset, make sure everything - * up to the log client array is before the first word protected by an - * update sequence number. This ensures we can access all of the - * restart area elements safely. Also, the client array offset must be - * aligned to an 8-byte boundary. - */ - ca_ofs = le16_to_cpu( ra->client_array_offset ); - if ( ( ( ca_ofs + 7 ) & ~7 ) != ca_ofs || - ra_ofs + ca_ofs > ( u16 )( NTFS_BLOCK_SIZE - - sizeof( u16 ) ) ) - { - ntfs_log_error( "$LogFile restart area specifies " - "inconsistent client array offset.\n" ); - return FALSE; - } - /* - * The restart area must end within the system page size both when - * calculated manually and as specified by ra->restart_area_length. - * Also, the calculated length must not exceed the specified length. - */ - ra_len = ca_ofs + le16_to_cpu( ra->log_clients ) * - sizeof( LOG_CLIENT_RECORD ); - if ( ( u32 )( ra_ofs + ra_len ) > le32_to_cpu( rp->system_page_size ) || - ( u32 )( ra_ofs + le16_to_cpu( ra->restart_area_length ) ) > - le32_to_cpu( rp->system_page_size ) || - ra_len > le16_to_cpu( ra->restart_area_length ) ) - { - ntfs_log_error( "$LogFile restart area is out of bounds " - "of the system page size specified by the " - "restart page header and/or the specified " - "restart area length is inconsistent.\n" ); - return FALSE; - } - /* - * The ra->client_free_list and ra->client_in_use_list must be either - * LOGFILE_NO_CLIENT or less than ra->log_clients or they are - * overflowing the client array. - */ - if ( ( ra->client_free_list != LOGFILE_NO_CLIENT && - le16_to_cpu( ra->client_free_list ) >= - le16_to_cpu( ra->log_clients ) ) || - ( ra->client_in_use_list != LOGFILE_NO_CLIENT && - le16_to_cpu( ra->client_in_use_list ) >= - le16_to_cpu( ra->log_clients ) ) ) - { - ntfs_log_error( "$LogFile restart area specifies " - "overflowing client free and/or in use lists.\n" ); - return FALSE; - } - /* - * Check ra->seq_number_bits against ra->file_size for consistency. - * We cannot just use ffs() because the file size is not a power of 2. - */ - file_size = ( u64 )sle64_to_cpu( ra->file_size ); - fs_bits = 0; - while ( file_size ) - { - file_size >>= 1; - fs_bits++; - } - if ( le32_to_cpu( ra->seq_number_bits ) != ( u32 )( 67 - fs_bits ) ) - { - ntfs_log_error( "$LogFile restart area specifies " - "inconsistent sequence number bits.\n" ); - return FALSE; - } - /* The log record header length must be a multiple of 8. */ - if ( ( ( le16_to_cpu( ra->log_record_header_length ) + 7 ) & ~7 ) != - le16_to_cpu( ra->log_record_header_length ) ) - { - ntfs_log_error( "$LogFile restart area specifies " - "inconsistent log record header length.\n" ); - return FALSE; - } - /* Ditto for the log page data offset. */ - if ( ( ( le16_to_cpu( ra->log_page_data_offset ) + 7 ) & ~7 ) != - le16_to_cpu( ra->log_page_data_offset ) ) - { - ntfs_log_error( "$LogFile restart area specifies " - "inconsistent log page data offset.\n" ); - return FALSE; - } - ntfs_log_trace( "Done.\n" ); - return TRUE; + ntfs_log_trace("Entering.\n"); + ra_ofs = le16_to_cpu(rp->restart_area_offset); + ra = (RESTART_AREA*)((u8*)rp + ra_ofs); + /* + * Everything before ra->file_size must be before the first word + * protected by an update sequence number. This ensures that it is + * safe to access ra->client_array_offset. + */ + if (ra_ofs + offsetof(RESTART_AREA, file_size) > + NTFS_BLOCK_SIZE - sizeof(u16)) { + ntfs_log_error("$LogFile restart area specifies " + "inconsistent file offset.\n"); + return FALSE; + } + /* + * Now that we can access ra->client_array_offset, make sure everything + * up to the log client array is before the first word protected by an + * update sequence number. This ensures we can access all of the + * restart area elements safely. Also, the client array offset must be + * aligned to an 8-byte boundary. + */ + ca_ofs = le16_to_cpu(ra->client_array_offset); + if (((ca_ofs + 7) & ~7) != ca_ofs || + ra_ofs + ca_ofs > (u16)(NTFS_BLOCK_SIZE - + sizeof(u16))) { + ntfs_log_error("$LogFile restart area specifies " + "inconsistent client array offset.\n"); + return FALSE; + } + /* + * The restart area must end within the system page size both when + * calculated manually and as specified by ra->restart_area_length. + * Also, the calculated length must not exceed the specified length. + */ + ra_len = ca_ofs + le16_to_cpu(ra->log_clients) * + sizeof(LOG_CLIENT_RECORD); + if ((u32)(ra_ofs + ra_len) > le32_to_cpu(rp->system_page_size) || + (u32)(ra_ofs + le16_to_cpu(ra->restart_area_length)) > + le32_to_cpu(rp->system_page_size) || + ra_len > le16_to_cpu(ra->restart_area_length)) { + ntfs_log_error("$LogFile restart area is out of bounds " + "of the system page size specified by the " + "restart page header and/or the specified " + "restart area length is inconsistent.\n"); + return FALSE; + } + /* + * The ra->client_free_list and ra->client_in_use_list must be either + * LOGFILE_NO_CLIENT or less than ra->log_clients or they are + * overflowing the client array. + */ + if ((ra->client_free_list != LOGFILE_NO_CLIENT && + le16_to_cpu(ra->client_free_list) >= + le16_to_cpu(ra->log_clients)) || + (ra->client_in_use_list != LOGFILE_NO_CLIENT && + le16_to_cpu(ra->client_in_use_list) >= + le16_to_cpu(ra->log_clients))) { + ntfs_log_error("$LogFile restart area specifies " + "overflowing client free and/or in use lists.\n"); + return FALSE; + } + /* + * Check ra->seq_number_bits against ra->file_size for consistency. + * We cannot just use ffs() because the file size is not a power of 2. + */ + file_size = (u64)sle64_to_cpu(ra->file_size); + fs_bits = 0; + while (file_size) { + file_size >>= 1; + fs_bits++; + } + if (le32_to_cpu(ra->seq_number_bits) != (u32)(67 - fs_bits)) { + ntfs_log_error("$LogFile restart area specifies " + "inconsistent sequence number bits.\n"); + return FALSE; + } + /* The log record header length must be a multiple of 8. */ + if (((le16_to_cpu(ra->log_record_header_length) + 7) & ~7) != + le16_to_cpu(ra->log_record_header_length)) { + ntfs_log_error("$LogFile restart area specifies " + "inconsistent log record header length.\n"); + return FALSE; + } + /* Ditto for the log page data offset. */ + if (((le16_to_cpu(ra->log_page_data_offset) + 7) & ~7) != + le16_to_cpu(ra->log_page_data_offset)) { + ntfs_log_error("$LogFile restart area specifies " + "inconsistent log page data offset.\n"); + return FALSE; + } + ntfs_log_trace("Done.\n"); + return TRUE; } /** * ntfs_check_log_client_array - check the log client array for consistency - * @rp: restart page whose log client array to check + * @rp: restart page whose log client array to check * * Check the log client array of the restart page @rp for consistency and * return TRUE if it is consistent and FALSE otherwise. @@ -289,63 +273,60 @@ static BOOL ntfs_check_restart_area( RESTART_PAGE_HEADER *rp ) * function needs @rp->system_page_size bytes in @rp, i.e. it requires the full * restart page and the page must be multi sector transfer deprotected. */ -static BOOL ntfs_check_log_client_array( RESTART_PAGE_HEADER *rp ) +static BOOL ntfs_check_log_client_array(RESTART_PAGE_HEADER *rp) { - RESTART_AREA *ra; - LOG_CLIENT_RECORD *ca, *cr; - u16 nr_clients, idx; - BOOL in_free_list, idx_is_first; + RESTART_AREA *ra; + LOG_CLIENT_RECORD *ca, *cr; + u16 nr_clients, idx; + BOOL in_free_list, idx_is_first; - ntfs_log_trace( "Entering.\n" ); - ra = ( RESTART_AREA* )( ( u8* )rp + le16_to_cpu( rp->restart_area_offset ) ); - ca = ( LOG_CLIENT_RECORD* )( ( u8* )ra + - le16_to_cpu( ra->client_array_offset ) ); - /* - * Check the ra->client_free_list first and then check the - * ra->client_in_use_list. Check each of the log client records in - * each of the lists and check that the array does not overflow the - * ra->log_clients value. Also keep track of the number of records - * visited as there cannot be more than ra->log_clients records and - * that way we detect eventual loops in within a list. - */ - nr_clients = le16_to_cpu( ra->log_clients ); - idx = le16_to_cpu( ra->client_free_list ); - in_free_list = TRUE; + ntfs_log_trace("Entering.\n"); + ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); + ca = (LOG_CLIENT_RECORD*)((u8*)ra + + le16_to_cpu(ra->client_array_offset)); + /* + * Check the ra->client_free_list first and then check the + * ra->client_in_use_list. Check each of the log client records in + * each of the lists and check that the array does not overflow the + * ra->log_clients value. Also keep track of the number of records + * visited as there cannot be more than ra->log_clients records and + * that way we detect eventual loops in within a list. + */ + nr_clients = le16_to_cpu(ra->log_clients); + idx = le16_to_cpu(ra->client_free_list); + in_free_list = TRUE; check_list: - for ( idx_is_first = TRUE; idx != LOGFILE_NO_CLIENT_CPU; nr_clients--, - idx = le16_to_cpu( cr->next_client ) ) - { - if ( !nr_clients || idx >= le16_to_cpu( ra->log_clients ) ) - goto err_out; - /* Set @cr to the current log client record. */ - cr = ca + idx; - /* The first log client record must not have a prev_client. */ - if ( idx_is_first ) - { - if ( cr->prev_client != LOGFILE_NO_CLIENT ) - goto err_out; - idx_is_first = FALSE; - } - } - /* Switch to and check the in use list if we just did the free list. */ - if ( in_free_list ) - { - in_free_list = FALSE; - idx = le16_to_cpu( ra->client_in_use_list ); - goto check_list; - } - ntfs_log_trace( "Done.\n" ); - return TRUE; + for (idx_is_first = TRUE; idx != LOGFILE_NO_CLIENT_CPU; nr_clients--, + idx = le16_to_cpu(cr->next_client)) { + if (!nr_clients || idx >= le16_to_cpu(ra->log_clients)) + goto err_out; + /* Set @cr to the current log client record. */ + cr = ca + idx; + /* The first log client record must not have a prev_client. */ + if (idx_is_first) { + if (cr->prev_client != LOGFILE_NO_CLIENT) + goto err_out; + idx_is_first = FALSE; + } + } + /* Switch to and check the in use list if we just did the free list. */ + if (in_free_list) { + in_free_list = FALSE; + idx = le16_to_cpu(ra->client_in_use_list); + goto check_list; + } + ntfs_log_trace("Done.\n"); + return TRUE; err_out: - ntfs_log_error( "$LogFile log client array is corrupt.\n" ); - return FALSE; + ntfs_log_error("$LogFile log client array is corrupt.\n"); + return FALSE; } /** * ntfs_check_and_load_restart_page - check the restart page for consistency - * @log_na: opened ntfs attribute for journal $LogFile - * @rp: restart page to check - * @pos: position in @log_na at which the restart page resides + * @log_na: opened ntfs attribute for journal $LogFile + * @rp: restart page to check + * @pos: position in @log_na at which the restart page resides * @wrp: [OUT] copy of the multi sector transfer deprotected restart page * @lsn: [OUT] set to the current logfile lsn on success * @@ -368,112 +349,103 @@ err_out: * ENOMEM - Not enough memory to load the restart page. * EIO - Failed to reading from $LogFile. */ -static int ntfs_check_and_load_restart_page( ntfs_attr *log_na, - RESTART_PAGE_HEADER *rp, s64 pos, RESTART_PAGE_HEADER **wrp, - LSN *lsn ) +static int ntfs_check_and_load_restart_page(ntfs_attr *log_na, + RESTART_PAGE_HEADER *rp, s64 pos, RESTART_PAGE_HEADER **wrp, + LSN *lsn) { - RESTART_AREA *ra; - RESTART_PAGE_HEADER *trp; - int err; + RESTART_AREA *ra; + RESTART_PAGE_HEADER *trp; + int err; - ntfs_log_trace( "Entering.\n" ); - /* Check the restart page header for consistency. */ - if ( !ntfs_check_restart_page_header( rp, pos ) ) - { - /* Error output already done inside the function. */ - return EINVAL; - } - /* Check the restart area for consistency. */ - if ( !ntfs_check_restart_area( rp ) ) - { - /* Error output already done inside the function. */ - return EINVAL; - } - ra = ( RESTART_AREA* )( ( u8* )rp + le16_to_cpu( rp->restart_area_offset ) ); - /* - * Allocate a buffer to store the whole restart page so we can multi - * sector transfer deprotect it. - */ - trp = ntfs_malloc( le32_to_cpu( rp->system_page_size ) ); - if ( !trp ) - return errno; - /* - * Read the whole of the restart page into the buffer. If it fits - * completely inside @rp, just copy it from there. Otherwise read it - * from disk. - */ - if ( le32_to_cpu( rp->system_page_size ) <= NTFS_BLOCK_SIZE ) - memcpy( trp, rp, le32_to_cpu( rp->system_page_size ) ); - else if ( ntfs_attr_pread( log_na, pos, - le32_to_cpu( rp->system_page_size ), trp ) != - le32_to_cpu( rp->system_page_size ) ) - { - err = errno; - ntfs_log_error( "Failed to read whole restart page into the " - "buffer.\n" ); - if ( err != ENOMEM ) - err = EIO; - goto err_out; - } - /* - * Perform the multi sector transfer deprotection on the buffer if the - * restart page is protected. - */ - if ( ( !ntfs_is_chkd_record( trp->magic ) || le16_to_cpu( trp->usa_count ) ) - && ntfs_mst_post_read_fixup( ( NTFS_RECORD* )trp, - le32_to_cpu( rp->system_page_size ) ) ) - { - /* - * A multi sector tranfer error was detected. We only need to - * abort if the restart page contents exceed the multi sector - * transfer fixup of the first sector. - */ - if ( le16_to_cpu( rp->restart_area_offset ) + - le16_to_cpu( ra->restart_area_length ) > - NTFS_BLOCK_SIZE - ( int )sizeof( u16 ) ) - { - ntfs_log_error( "Multi sector transfer error " - "detected in $LogFile restart page.\n" ); - err = EINVAL; - goto err_out; - } - } - /* - * If the restart page is modified by chkdsk or there are no active - * logfile clients, the logfile is consistent. Otherwise, need to - * check the log client records for consistency, too. - */ - err = 0; - if ( ntfs_is_rstr_record( rp->magic ) && - ra->client_in_use_list != LOGFILE_NO_CLIENT ) - { - if ( !ntfs_check_log_client_array( trp ) ) - { - err = EINVAL; - goto err_out; - } - } - if ( lsn ) - { - if ( ntfs_is_rstr_record( rp->magic ) ) - *lsn = sle64_to_cpu( ra->current_lsn ); - else /* if (ntfs_is_chkd_record(rp->magic)) */ - *lsn = sle64_to_cpu( rp->chkdsk_lsn ); - } - ntfs_log_trace( "Done.\n" ); - if ( wrp ) - *wrp = trp; - else - { + ntfs_log_trace("Entering.\n"); + /* Check the restart page header for consistency. */ + if (!ntfs_check_restart_page_header(rp, pos)) { + /* Error output already done inside the function. */ + return EINVAL; + } + /* Check the restart area for consistency. */ + if (!ntfs_check_restart_area(rp)) { + /* Error output already done inside the function. */ + return EINVAL; + } + ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); + /* + * Allocate a buffer to store the whole restart page so we can multi + * sector transfer deprotect it. + */ + trp = ntfs_malloc(le32_to_cpu(rp->system_page_size)); + if (!trp) + return errno; + /* + * Read the whole of the restart page into the buffer. If it fits + * completely inside @rp, just copy it from there. Otherwise read it + * from disk. + */ + if (le32_to_cpu(rp->system_page_size) <= NTFS_BLOCK_SIZE) + memcpy(trp, rp, le32_to_cpu(rp->system_page_size)); + else if (ntfs_attr_pread(log_na, pos, + le32_to_cpu(rp->system_page_size), trp) != + le32_to_cpu(rp->system_page_size)) { + err = errno; + ntfs_log_error("Failed to read whole restart page into the " + "buffer.\n"); + if (err != ENOMEM) + err = EIO; + goto err_out; + } + /* + * Perform the multi sector transfer deprotection on the buffer if the + * restart page is protected. + */ + if ((!ntfs_is_chkd_record(trp->magic) || le16_to_cpu(trp->usa_count)) + && ntfs_mst_post_read_fixup((NTFS_RECORD*)trp, + le32_to_cpu(rp->system_page_size))) { + /* + * A multi sector tranfer error was detected. We only need to + * abort if the restart page contents exceed the multi sector + * transfer fixup of the first sector. + */ + if (le16_to_cpu(rp->restart_area_offset) + + le16_to_cpu(ra->restart_area_length) > + NTFS_BLOCK_SIZE - (int)sizeof(u16)) { + ntfs_log_error("Multi sector transfer error " + "detected in $LogFile restart page.\n"); + err = EINVAL; + goto err_out; + } + } + /* + * If the restart page is modified by chkdsk or there are no active + * logfile clients, the logfile is consistent. Otherwise, need to + * check the log client records for consistency, too. + */ + err = 0; + if (ntfs_is_rstr_record(rp->magic) && + ra->client_in_use_list != LOGFILE_NO_CLIENT) { + if (!ntfs_check_log_client_array(trp)) { + err = EINVAL; + goto err_out; + } + } + if (lsn) { + if (ntfs_is_rstr_record(rp->magic)) + *lsn = sle64_to_cpu(ra->current_lsn); + else /* if (ntfs_is_chkd_record(rp->magic)) */ + *lsn = sle64_to_cpu(rp->chkdsk_lsn); + } + ntfs_log_trace("Done.\n"); + if (wrp) + *wrp = trp; + else { err_out: - free( trp ); - } - return err; + free(trp); + } + return err; } /** * ntfs_check_logfile - check in the journal if the volume is consistent - * @log_na: ntfs attribute of loaded journal $LogFile to check + * @log_na: ntfs attribute of loaded journal $LogFile to check * @rp: [OUT] on success this is a copy of the current restart page * * Check the $LogFile journal for consistency and return TRUE if it is @@ -488,190 +460,177 @@ err_out: * if the $LogFile was created on a system with a different page size to ours * yet and mst deprotection would fail if our page size is smaller. */ -BOOL ntfs_check_logfile( ntfs_attr *log_na, RESTART_PAGE_HEADER **rp ) +BOOL ntfs_check_logfile(ntfs_attr *log_na, RESTART_PAGE_HEADER **rp) { - s64 size, pos; - LSN rstr1_lsn, rstr2_lsn; - ntfs_volume *vol = log_na->ni->vol; - u8 *kaddr = NULL; - RESTART_PAGE_HEADER *rstr1_ph = NULL; - RESTART_PAGE_HEADER *rstr2_ph = NULL; - int log_page_size, log_page_mask, err; - BOOL logfile_is_empty = TRUE; - u8 log_page_bits; + s64 size, pos; + LSN rstr1_lsn, rstr2_lsn; + ntfs_volume *vol = log_na->ni->vol; + u8 *kaddr = NULL; + RESTART_PAGE_HEADER *rstr1_ph = NULL; + RESTART_PAGE_HEADER *rstr2_ph = NULL; + int log_page_size, log_page_mask, err; + BOOL logfile_is_empty = TRUE; + u8 log_page_bits; - ntfs_log_trace( "Entering.\n" ); - /* An empty $LogFile must have been clean before it got emptied. */ - if ( NVolLogFileEmpty( vol ) ) - goto is_empty; - size = log_na->data_size; - /* Make sure the file doesn't exceed the maximum allowed size. */ - if ( size > ( s64 )MaxLogFileSize ) - size = MaxLogFileSize; - log_page_size = DefaultLogPageSize; - log_page_mask = log_page_size - 1; - /* - * Use generic_ffs() instead of ffs() to enable the compiler to - * optimize log_page_size and log_page_bits into constants. - */ - log_page_bits = ffs( log_page_size ) - 1; - size &= ~( log_page_size - 1 ); + ntfs_log_trace("Entering.\n"); + /* An empty $LogFile must have been clean before it got emptied. */ + if (NVolLogFileEmpty(vol)) + goto is_empty; + size = log_na->data_size; + /* Make sure the file doesn't exceed the maximum allowed size. */ + if (size > (s64)MaxLogFileSize) + size = MaxLogFileSize; + log_page_size = DefaultLogPageSize; + log_page_mask = log_page_size - 1; + /* + * Use generic_ffs() instead of ffs() to enable the compiler to + * optimize log_page_size and log_page_bits into constants. + */ + log_page_bits = ffs(log_page_size) - 1; + size &= ~(log_page_size - 1); - /* - * Ensure the log file is big enough to store at least the two restart - * pages and the minimum number of log record pages. - */ - if ( size < log_page_size * 2 || ( size - log_page_size * 2 ) >> - log_page_bits < MinLogRecordPages ) - { - ntfs_log_error( "$LogFile is too small.\n" ); - return FALSE; - } - /* Allocate memory for restart page. */ - kaddr = ntfs_malloc( NTFS_BLOCK_SIZE ); - if ( !kaddr ) - return FALSE; - /* - * Read through the file looking for a restart page. Since the restart - * page header is at the beginning of a page we only need to search at - * what could be the beginning of a page (for each page size) rather - * than scanning the whole file byte by byte. If all potential places - * contain empty and uninitialized records, the log file can be assumed - * to be empty. - */ - for ( pos = 0; pos < size; pos <<= 1 ) - { - /* - * Read first NTFS_BLOCK_SIZE bytes of potential restart page. - */ - if ( ntfs_attr_pread( log_na, pos, NTFS_BLOCK_SIZE, kaddr ) != - NTFS_BLOCK_SIZE ) - { - ntfs_log_error( "Failed to read first NTFS_BLOCK_SIZE " - "bytes of potential restart page.\n" ); - goto err_out; - } + /* + * Ensure the log file is big enough to store at least the two restart + * pages and the minimum number of log record pages. + */ + if (size < log_page_size * 2 || (size - log_page_size * 2) >> + log_page_bits < MinLogRecordPages) { + ntfs_log_error("$LogFile is too small.\n"); + return FALSE; + } + /* Allocate memory for restart page. */ + kaddr = ntfs_malloc(NTFS_BLOCK_SIZE); + if (!kaddr) + return FALSE; + /* + * Read through the file looking for a restart page. Since the restart + * page header is at the beginning of a page we only need to search at + * what could be the beginning of a page (for each page size) rather + * than scanning the whole file byte by byte. If all potential places + * contain empty and uninitialized records, the log file can be assumed + * to be empty. + */ + for (pos = 0; pos < size; pos <<= 1) { + /* + * Read first NTFS_BLOCK_SIZE bytes of potential restart page. + */ + if (ntfs_attr_pread(log_na, pos, NTFS_BLOCK_SIZE, kaddr) != + NTFS_BLOCK_SIZE) { + ntfs_log_error("Failed to read first NTFS_BLOCK_SIZE " + "bytes of potential restart page.\n"); + goto err_out; + } - /* - * A non-empty block means the logfile is not empty while an - * empty block after a non-empty block has been encountered - * means we are done. - */ - if ( !ntfs_is_empty_recordp( ( le32* )kaddr ) ) - logfile_is_empty = FALSE; - else if ( !logfile_is_empty ) - break; - /* - * A log record page means there cannot be a restart page after - * this so no need to continue searching. - */ - if ( ntfs_is_rcrd_recordp( ( le32* )kaddr ) ) - break; - /* If not a (modified by chkdsk) restart page, continue. */ - if ( !ntfs_is_rstr_recordp( ( le32* )kaddr ) && - !ntfs_is_chkd_recordp( ( le32* )kaddr ) ) - { - if ( !pos ) - pos = NTFS_BLOCK_SIZE >> 1; - continue; - } - /* - * Check the (modified by chkdsk) restart page for consistency - * and get a copy of the complete multi sector transfer - * deprotected restart page. - */ - err = ntfs_check_and_load_restart_page( log_na, - ( RESTART_PAGE_HEADER* )kaddr, pos, - !rstr1_ph ? &rstr1_ph : &rstr2_ph, - !rstr1_ph ? &rstr1_lsn : &rstr2_lsn ); - if ( !err ) - { - /* - * If we have now found the first (modified by chkdsk) - * restart page, continue looking for the second one. - */ - if ( !pos ) - { - pos = NTFS_BLOCK_SIZE >> 1; - continue; - } - /* - * We have now found the second (modified by chkdsk) - * restart page, so we can stop looking. - */ - break; - } - /* - * Error output already done inside the function. Note, we do - * not abort if the restart page was invalid as we might still - * find a valid one further in the file. - */ - if ( err != EINVAL ) - goto err_out; - /* Continue looking. */ - if ( !pos ) - pos = NTFS_BLOCK_SIZE >> 1; - } - if ( kaddr ) - { - free( kaddr ); - kaddr = NULL; - } - if ( logfile_is_empty ) - { - NVolSetLogFileEmpty( vol ); + /* + * A non-empty block means the logfile is not empty while an + * empty block after a non-empty block has been encountered + * means we are done. + */ + if (!ntfs_is_empty_recordp((le32*)kaddr)) + logfile_is_empty = FALSE; + else if (!logfile_is_empty) + break; + /* + * A log record page means there cannot be a restart page after + * this so no need to continue searching. + */ + if (ntfs_is_rcrd_recordp((le32*)kaddr)) + break; + /* If not a (modified by chkdsk) restart page, continue. */ + if (!ntfs_is_rstr_recordp((le32*)kaddr) && + !ntfs_is_chkd_recordp((le32*)kaddr)) { + if (!pos) + pos = NTFS_BLOCK_SIZE >> 1; + continue; + } + /* + * Check the (modified by chkdsk) restart page for consistency + * and get a copy of the complete multi sector transfer + * deprotected restart page. + */ + err = ntfs_check_and_load_restart_page(log_na, + (RESTART_PAGE_HEADER*)kaddr, pos, + !rstr1_ph ? &rstr1_ph : &rstr2_ph, + !rstr1_ph ? &rstr1_lsn : &rstr2_lsn); + if (!err) { + /* + * If we have now found the first (modified by chkdsk) + * restart page, continue looking for the second one. + */ + if (!pos) { + pos = NTFS_BLOCK_SIZE >> 1; + continue; + } + /* + * We have now found the second (modified by chkdsk) + * restart page, so we can stop looking. + */ + break; + } + /* + * Error output already done inside the function. Note, we do + * not abort if the restart page was invalid as we might still + * find a valid one further in the file. + */ + if (err != EINVAL) + goto err_out; + /* Continue looking. */ + if (!pos) + pos = NTFS_BLOCK_SIZE >> 1; + } + if (kaddr) { + free(kaddr); + kaddr = NULL; + } + if (logfile_is_empty) { + NVolSetLogFileEmpty(vol); is_empty: - ntfs_log_trace( "Done. ($LogFile is empty.)\n" ); - return TRUE; - } - if ( !rstr1_ph ) - { - if ( rstr2_ph ) - ntfs_log_error( "BUG: rstr2_ph isn't NULL!\n" ); - ntfs_log_error( "Did not find any restart pages in " - "$LogFile and it was not empty.\n" ); - return FALSE; - } - /* If both restart pages were found, use the more recent one. */ - if ( rstr2_ph ) - { - /* - * If the second restart area is more recent, switch to it. - * Otherwise just throw it away. - */ - if ( rstr2_lsn > rstr1_lsn ) - { - ntfs_log_debug( "Using second restart page as it is more " - "recent.\n" ); - free( rstr1_ph ); - rstr1_ph = rstr2_ph; - /* rstr1_lsn = rstr2_lsn; */ - } - else - { - ntfs_log_debug( "Using first restart page as it is more " - "recent.\n" ); - free( rstr2_ph ); - } - rstr2_ph = NULL; - } - /* All consistency checks passed. */ - if ( rp ) - *rp = rstr1_ph; - else - free( rstr1_ph ); - ntfs_log_trace( "Done.\n" ); - return TRUE; + ntfs_log_trace("Done. ($LogFile is empty.)\n"); + return TRUE; + } + if (!rstr1_ph) { + if (rstr2_ph) + ntfs_log_error("BUG: rstr2_ph isn't NULL!\n"); + ntfs_log_error("Did not find any restart pages in " + "$LogFile and it was not empty.\n"); + return FALSE; + } + /* If both restart pages were found, use the more recent one. */ + if (rstr2_ph) { + /* + * If the second restart area is more recent, switch to it. + * Otherwise just throw it away. + */ + if (rstr2_lsn > rstr1_lsn) { + ntfs_log_debug("Using second restart page as it is more " + "recent.\n"); + free(rstr1_ph); + rstr1_ph = rstr2_ph; + /* rstr1_lsn = rstr2_lsn; */ + } else { + ntfs_log_debug("Using first restart page as it is more " + "recent.\n"); + free(rstr2_ph); + } + rstr2_ph = NULL; + } + /* All consistency checks passed. */ + if (rp) + *rp = rstr1_ph; + else + free(rstr1_ph); + ntfs_log_trace("Done.\n"); + return TRUE; err_out: - free( kaddr ); - free( rstr1_ph ); - free( rstr2_ph ); - return FALSE; + free(kaddr); + free(rstr1_ph); + free(rstr2_ph); + return FALSE; } /** * ntfs_is_logfile_clean - check in the journal if the volume is clean - * @log_na: ntfs attribute of loaded journal $LogFile to check + * @log_na: ntfs attribute of loaded journal $LogFile to check * @rp: copy of the current restart page * * Analyze the $LogFile journal and return TRUE if it indicates the volume was @@ -689,51 +648,47 @@ err_out: * is empty this function requires that NVolLogFileEmpty() is true otherwise an * empty volume will be reported as dirty. */ -BOOL ntfs_is_logfile_clean( ntfs_attr *log_na, RESTART_PAGE_HEADER *rp ) +BOOL ntfs_is_logfile_clean(ntfs_attr *log_na, RESTART_PAGE_HEADER *rp) { - RESTART_AREA *ra; + RESTART_AREA *ra; - ntfs_log_trace( "Entering.\n" ); - /* An empty $LogFile must have been clean before it got emptied. */ - if ( NVolLogFileEmpty( log_na->ni->vol ) ) - { - ntfs_log_trace( "$LogFile is empty\n" ); - return TRUE; - } - if ( !rp ) - { - ntfs_log_error( "Restart page header is NULL\n" ); - return FALSE; - } - if ( !ntfs_is_rstr_record( rp->magic ) && - !ntfs_is_chkd_record( rp->magic ) ) - { - ntfs_log_error( "Restart page buffer is invalid\n" ); - return FALSE; - } + ntfs_log_trace("Entering.\n"); + /* An empty $LogFile must have been clean before it got emptied. */ + if (NVolLogFileEmpty(log_na->ni->vol)) { + ntfs_log_trace("$LogFile is empty\n"); + return TRUE; + } + if (!rp) { + ntfs_log_error("Restart page header is NULL\n"); + return FALSE; + } + if (!ntfs_is_rstr_record(rp->magic) && + !ntfs_is_chkd_record(rp->magic)) { + ntfs_log_error("Restart page buffer is invalid\n"); + return FALSE; + } - ra = ( RESTART_AREA* )( ( u8* )rp + le16_to_cpu( rp->restart_area_offset ) ); - /* - * If the $LogFile has active clients, i.e. it is open, and we do not - * have the RESTART_VOLUME_IS_CLEAN bit set in the restart area flags, - * we assume there was an unclean shutdown. - */ - if ( ra->client_in_use_list != LOGFILE_NO_CLIENT && - !( ra->flags & RESTART_VOLUME_IS_CLEAN ) ) - { - ntfs_log_error( "The disk contains an unclean file system (%d, " - "%d).\n", le16_to_cpu( ra->client_in_use_list ), - le16_to_cpu( ra->flags ) ); - return FALSE; - } - /* $LogFile indicates a clean shutdown. */ - ntfs_log_trace( "$LogFile indicates a clean shutdown\n" ); - return TRUE; + ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); + /* + * If the $LogFile has active clients, i.e. it is open, and we do not + * have the RESTART_VOLUME_IS_CLEAN bit set in the restart area flags, + * we assume there was an unclean shutdown. + */ + if (ra->client_in_use_list != LOGFILE_NO_CLIENT && + !(ra->flags & RESTART_VOLUME_IS_CLEAN)) { + ntfs_log_error("The disk contains an unclean file system (%d, " + "%d).\n", le16_to_cpu(ra->client_in_use_list), + le16_to_cpu(ra->flags)); + return FALSE; + } + /* $LogFile indicates a clean shutdown. */ + ntfs_log_trace("$LogFile indicates a clean shutdown\n"); + return TRUE; } /** * ntfs_empty_logfile - empty the contents of the $LogFile journal - * @na: ntfs attribute of journal $LogFile to empty + * @na: ntfs attribute of journal $LogFile to empty * * Empty the contents of the $LogFile journal @na and return 0 on success and * -1 on error. @@ -742,44 +697,41 @@ BOOL ntfs_is_logfile_clean( ntfs_attr *log_na, RESTART_PAGE_HEADER *rp ) * checked by a call to ntfs_check_logfile() and that ntfs_is_logfile_clean() * has been used to ensure that the $LogFile is clean. */ -int ntfs_empty_logfile( ntfs_attr *na ) +int ntfs_empty_logfile(ntfs_attr *na) { - s64 pos, count; - char buf[NTFS_BUF_SIZE]; + s64 pos, count; + char buf[NTFS_BUF_SIZE]; - ntfs_log_trace( "Entering.\n" ); + ntfs_log_trace("Entering.\n"); + + if (NVolLogFileEmpty(na->ni->vol)) + return 0; - if ( NVolLogFileEmpty( na->ni->vol ) ) - return 0; + if (!NAttrNonResident(na)) { + errno = EIO; + ntfs_log_perror("Resident $LogFile $DATA attribute"); + return -1; + } - if ( !NAttrNonResident( na ) ) - { - errno = EIO; - ntfs_log_perror( "Resident $LogFile $DATA attribute" ); - return -1; - } + memset(buf, -1, NTFS_BUF_SIZE); - memset( buf, -1, NTFS_BUF_SIZE ); + pos = 0; + while ((count = na->data_size - pos) > 0) { + + if (count > NTFS_BUF_SIZE) + count = NTFS_BUF_SIZE; - pos = 0; - while ( ( count = na->data_size - pos ) > 0 ) - { + count = ntfs_attr_pwrite(na, pos, count, buf); + if (count <= 0) { + ntfs_log_perror("Failed to reset $LogFile"); + if (count != -1) + errno = EIO; + return -1; + } + pos += count; + } - if ( count > NTFS_BUF_SIZE ) - count = NTFS_BUF_SIZE; - - count = ntfs_attr_pwrite( na, pos, count, buf ); - if ( count <= 0 ) - { - ntfs_log_perror( "Failed to reset $LogFile" ); - if ( count != -1 ) - errno = EIO; - return -1; - } - pos += count; - } - - NVolSetLogFileEmpty( na->ni->vol ); - - return 0; + NVolSetLogFileEmpty(na->ni->vol); + + return 0; } diff --git a/source/libntfs/logfile.h b/source/libntfs/logfile.h index 1502d1f4..798d562d 100644 --- a/source/libntfs/logfile.h +++ b/source/libntfs/logfile.h @@ -52,69 +52,66 @@ */ /* Some $LogFile related constants. */ -#define MaxLogFileSize 0x100000000ULL -#define DefaultLogPageSize 4096 -#define MinLogRecordPages 48 +#define MaxLogFileSize 0x100000000ULL +#define DefaultLogPageSize 4096 +#define MinLogRecordPages 48 /** * struct RESTART_PAGE_HEADER - Log file restart page header. * * Begins the restart area. */ -typedef struct -{ - /*Ofs*/ - /* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */ - /* 0*/ - NTFS_RECORD_TYPES magic;/* The magic is "RSTR". */ - /* 4*/ le16 usa_ofs; /* See NTFS_RECORD definition in layout.h. - When creating, set this to be immediately - after this header structure (without any - alignment). */ - /* 6*/ le16 usa_count; /* See NTFS_RECORD definition in layout.h. */ +typedef struct { +/*Ofs*/ +/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */ +/* 0*/ NTFS_RECORD_TYPES magic;/* The magic is "RSTR". */ +/* 4*/ le16 usa_ofs; /* See NTFS_RECORD definition in layout.h. + When creating, set this to be immediately + after this header structure (without any + alignment). */ +/* 6*/ le16 usa_count; /* See NTFS_RECORD definition in layout.h. */ - /* 8*/ leLSN chkdsk_lsn; /* The last log file sequence number found by - chkdsk. Only used when the magic is changed - to "CHKD". Otherwise this is zero. */ - /* 16*/ le32 system_page_size; /* Byte size of system pages when the log file - was created, has to be >= 512 and a power of - 2. Use this to calculate the required size - of the usa (usa_count) and add it to usa_ofs. - Then verify that the result is less than the - value of the restart_area_offset. */ - /* 20*/ le32 log_page_size; /* Byte size of log file pages, has to be >= - 512 and a power of 2. The default is 4096 - and is used when the system page size is - between 4096 and 8192. Otherwise this is - set to the system page size instead. */ - /* 24*/ le16 restart_area_offset;/* Byte offset from the start of this header to - the RESTART_AREA. Value has to be aligned - to 8-byte boundary. When creating, set this - to be after the usa. */ - /* 26*/ sle16 minor_ver; /* Log file minor version. Only check if major - version is 1. */ - /* 28*/ sle16 major_ver; /* Log file major version. We only support - version 1.1. */ - /* sizeof() = 30 (0x1e) bytes */ -} __attribute__( ( __packed__ ) ) RESTART_PAGE_HEADER; +/* 8*/ leLSN chkdsk_lsn; /* The last log file sequence number found by + chkdsk. Only used when the magic is changed + to "CHKD". Otherwise this is zero. */ +/* 16*/ le32 system_page_size; /* Byte size of system pages when the log file + was created, has to be >= 512 and a power of + 2. Use this to calculate the required size + of the usa (usa_count) and add it to usa_ofs. + Then verify that the result is less than the + value of the restart_area_offset. */ +/* 20*/ le32 log_page_size; /* Byte size of log file pages, has to be >= + 512 and a power of 2. The default is 4096 + and is used when the system page size is + between 4096 and 8192. Otherwise this is + set to the system page size instead. */ +/* 24*/ le16 restart_area_offset;/* Byte offset from the start of this header to + the RESTART_AREA. Value has to be aligned + to 8-byte boundary. When creating, set this + to be after the usa. */ +/* 26*/ sle16 minor_ver; /* Log file minor version. Only check if major + version is 1. */ +/* 28*/ sle16 major_ver; /* Log file major version. We only support + version 1.1. */ +/* sizeof() = 30 (0x1e) bytes */ +} __attribute__((__packed__)) RESTART_PAGE_HEADER; /* * Constant for the log client indices meaning that there are no client records * in this particular client array. Also inside the client records themselves, * this means that there are no client records preceding or following this one. */ -#define LOGFILE_NO_CLIENT const_cpu_to_le16(0xffff) -#define LOGFILE_NO_CLIENT_CPU 0xffff +#define LOGFILE_NO_CLIENT const_cpu_to_le16(0xffff) +#define LOGFILE_NO_CLIENT_CPU 0xffff /* * These are the so far known RESTART_AREA_* flags (16-bit) which contain * information about the log file in which they are present. */ -enum -{ - RESTART_VOLUME_IS_CLEAN = const_cpu_to_le16( 0x0002 ), - RESTART_SPACE_FILLER = 0xffff, /* gcc: Force enum bit width to 16. */ -} __attribute__( ( __packed__ ) ); +enum { + RESTART_VOLUME_IS_CLEAN = const_cpu_to_le16(0x0002), + RESTART_SPACE_FILLER = 0xffff, /* gcc: Force enum bit width to 16. */ +} __attribute__((__packed__)); typedef le16 RESTART_AREA_FLAGS; @@ -125,141 +122,139 @@ typedef le16 RESTART_AREA_FLAGS; * RESTART_PAGE_HEADER to the restart_area_offset value found in it. * See notes at restart_area_offset above. */ -typedef struct -{ - /*Ofs*/ - /* 0*/ - leLSN current_lsn; /* The current, i.e. last LSN inside the log - when the restart area was last written. - This happens often but what is the interval? - Is it just fixed time or is it every time a - check point is written or something else? - On create set to 0. */ - /* 8*/ le16 log_clients; /* Number of log client records in the array of - log client records which follows this - restart area. Must be 1. */ - /* 10*/ le16 client_free_list; /* The index of the first free log client record - in the array of log client records. - LOGFILE_NO_CLIENT means that there are no - free log client records in the array. - If != LOGFILE_NO_CLIENT, check that - log_clients > client_free_list. On Win2k - and presumably earlier, on a clean volume - this is != LOGFILE_NO_CLIENT, and it should - be 0, i.e. the first (and only) client - record is free and thus the logfile is - closed and hence clean. A dirty volume - would have left the logfile open and hence - this would be LOGFILE_NO_CLIENT. On WinXP - and presumably later, the logfile is always - open, even on clean shutdown so this should - always be LOGFILE_NO_CLIENT. */ - /* 12*/ le16 client_in_use_list;/* The index of the first in-use log client - record in the array of log client records. - LOGFILE_NO_CLIENT means that there are no - in-use log client records in the array. If - != LOGFILE_NO_CLIENT check that log_clients - > client_in_use_list. On Win2k and - presumably earlier, on a clean volume this - is LOGFILE_NO_CLIENT, i.e. there are no - client records in use and thus the logfile - is closed and hence clean. A dirty volume - would have left the logfile open and hence - this would be != LOGFILE_NO_CLIENT, and it - should be 0, i.e. the first (and only) - client record is in use. On WinXP and - presumably later, the logfile is always - open, even on clean shutdown so this should - always be 0. */ - /* 14*/ RESTART_AREA_FLAGS flags;/* Flags modifying LFS behaviour. On Win2k - and presumably earlier this is always 0. On - WinXP and presumably later, if the logfile - was shutdown cleanly, the second bit, - RESTART_VOLUME_IS_CLEAN, is set. This bit - is cleared when the volume is mounted by - WinXP and set when the volume is dismounted, - thus if the logfile is dirty, this bit is - clear. Thus we don't need to check the - Windows version to determine if the logfile - is clean. Instead if the logfile is closed, - we know it must be clean. If it is open and - this bit is set, we also know it must be - clean. If on the other hand the logfile is - open and this bit is clear, we can be almost - certain that the logfile is dirty. */ - /* 16*/ le32 seq_number_bits; /* How many bits to use for the sequence - number. This is calculated as 67 - the - number of bits required to store the logfile - size in bytes and this can be used in with - the specified file_size as a consistency - check. */ - /* 20*/ le16 restart_area_length;/* Length of the restart area including the - client array. Following checks required if - version matches. Otherwise, skip them. - restart_area_offset + restart_area_length - has to be <= system_page_size. Also, - restart_area_length has to be >= - client_array_offset + (log_clients * - sizeof(log client record)). */ - /* 22*/ le16 client_array_offset;/* Offset from the start of this record to - the first log client record if versions are - matched. When creating, set this to be - after this restart area structure, aligned - to 8-bytes boundary. If the versions do not - match, this is ignored and the offset is - assumed to be (sizeof(RESTART_AREA) + 7) & - ~7, i.e. rounded up to first 8-byte - boundary. Either way, client_array_offset - has to be aligned to an 8-byte boundary. - Also, restart_area_offset + - client_array_offset has to be <= 510. - Finally, client_array_offset + (log_clients - * sizeof(log client record)) has to be <= - system_page_size. On Win2k and presumably - earlier, this is 0x30, i.e. immediately - following this record. On WinXP and - presumably later, this is 0x40, i.e. there - are 16 extra bytes between this record and - the client array. This probably means that - the RESTART_AREA record is actually bigger - in WinXP and later. */ - /* 24*/ sle64 file_size; /* Usable byte size of the log file. If the - restart_area_offset + the offset of the - file_size are > 510 then corruption has - occurred. This is the very first check when - starting with the restart_area as if it - fails it means that some of the above values - will be corrupted by the multi sector - transfer protection. The file_size has to - be rounded down to be a multiple of the - log_page_size in the RESTART_PAGE_HEADER and - then it has to be at least big enough to - store the two restart pages and 48 (0x30) - log record pages. */ - /* 32*/ le32 last_lsn_data_length;/* Length of data of last LSN, not including - the log record header. On create set to - 0. */ - /* 36*/ le16 log_record_header_length;/* Byte size of the log record header. - If the version matches then check that the - value of log_record_header_length is a - multiple of 8, i.e. - (log_record_header_length + 7) & ~7 == - log_record_header_length. When creating set - it to sizeof(LOG_RECORD_HEADER), aligned to - 8 bytes. */ - /* 38*/ le16 log_page_data_offset;/* Offset to the start of data in a log record - page. Must be a multiple of 8. On create - set it to immediately after the update - sequence array of the log record page. */ - /* 40*/ le32 restart_log_open_count;/* A counter that gets incremented every - time the logfile is restarted which happens - at mount time when the logfile is opened. - When creating set to a random value. Win2k - sets it to the low 32 bits of the current - system time in NTFS format (see time.h). */ - /* 44*/ le32 reserved; /* Reserved/alignment to 8-byte boundary. */ - /* sizeof() = 48 (0x30) bytes */ -} __attribute__( ( __packed__ ) ) RESTART_AREA; +typedef struct { +/*Ofs*/ +/* 0*/ leLSN current_lsn; /* The current, i.e. last LSN inside the log + when the restart area was last written. + This happens often but what is the interval? + Is it just fixed time or is it every time a + check point is written or something else? + On create set to 0. */ +/* 8*/ le16 log_clients; /* Number of log client records in the array of + log client records which follows this + restart area. Must be 1. */ +/* 10*/ le16 client_free_list; /* The index of the first free log client record + in the array of log client records. + LOGFILE_NO_CLIENT means that there are no + free log client records in the array. + If != LOGFILE_NO_CLIENT, check that + log_clients > client_free_list. On Win2k + and presumably earlier, on a clean volume + this is != LOGFILE_NO_CLIENT, and it should + be 0, i.e. the first (and only) client + record is free and thus the logfile is + closed and hence clean. A dirty volume + would have left the logfile open and hence + this would be LOGFILE_NO_CLIENT. On WinXP + and presumably later, the logfile is always + open, even on clean shutdown so this should + always be LOGFILE_NO_CLIENT. */ +/* 12*/ le16 client_in_use_list;/* The index of the first in-use log client + record in the array of log client records. + LOGFILE_NO_CLIENT means that there are no + in-use log client records in the array. If + != LOGFILE_NO_CLIENT check that log_clients + > client_in_use_list. On Win2k and + presumably earlier, on a clean volume this + is LOGFILE_NO_CLIENT, i.e. there are no + client records in use and thus the logfile + is closed and hence clean. A dirty volume + would have left the logfile open and hence + this would be != LOGFILE_NO_CLIENT, and it + should be 0, i.e. the first (and only) + client record is in use. On WinXP and + presumably later, the logfile is always + open, even on clean shutdown so this should + always be 0. */ +/* 14*/ RESTART_AREA_FLAGS flags;/* Flags modifying LFS behaviour. On Win2k + and presumably earlier this is always 0. On + WinXP and presumably later, if the logfile + was shutdown cleanly, the second bit, + RESTART_VOLUME_IS_CLEAN, is set. This bit + is cleared when the volume is mounted by + WinXP and set when the volume is dismounted, + thus if the logfile is dirty, this bit is + clear. Thus we don't need to check the + Windows version to determine if the logfile + is clean. Instead if the logfile is closed, + we know it must be clean. If it is open and + this bit is set, we also know it must be + clean. If on the other hand the logfile is + open and this bit is clear, we can be almost + certain that the logfile is dirty. */ +/* 16*/ le32 seq_number_bits; /* How many bits to use for the sequence + number. This is calculated as 67 - the + number of bits required to store the logfile + size in bytes and this can be used in with + the specified file_size as a consistency + check. */ +/* 20*/ le16 restart_area_length;/* Length of the restart area including the + client array. Following checks required if + version matches. Otherwise, skip them. + restart_area_offset + restart_area_length + has to be <= system_page_size. Also, + restart_area_length has to be >= + client_array_offset + (log_clients * + sizeof(log client record)). */ +/* 22*/ le16 client_array_offset;/* Offset from the start of this record to + the first log client record if versions are + matched. When creating, set this to be + after this restart area structure, aligned + to 8-bytes boundary. If the versions do not + match, this is ignored and the offset is + assumed to be (sizeof(RESTART_AREA) + 7) & + ~7, i.e. rounded up to first 8-byte + boundary. Either way, client_array_offset + has to be aligned to an 8-byte boundary. + Also, restart_area_offset + + client_array_offset has to be <= 510. + Finally, client_array_offset + (log_clients + * sizeof(log client record)) has to be <= + system_page_size. On Win2k and presumably + earlier, this is 0x30, i.e. immediately + following this record. On WinXP and + presumably later, this is 0x40, i.e. there + are 16 extra bytes between this record and + the client array. This probably means that + the RESTART_AREA record is actually bigger + in WinXP and later. */ +/* 24*/ sle64 file_size; /* Usable byte size of the log file. If the + restart_area_offset + the offset of the + file_size are > 510 then corruption has + occurred. This is the very first check when + starting with the restart_area as if it + fails it means that some of the above values + will be corrupted by the multi sector + transfer protection. The file_size has to + be rounded down to be a multiple of the + log_page_size in the RESTART_PAGE_HEADER and + then it has to be at least big enough to + store the two restart pages and 48 (0x30) + log record pages. */ +/* 32*/ le32 last_lsn_data_length;/* Length of data of last LSN, not including + the log record header. On create set to + 0. */ +/* 36*/ le16 log_record_header_length;/* Byte size of the log record header. + If the version matches then check that the + value of log_record_header_length is a + multiple of 8, i.e. + (log_record_header_length + 7) & ~7 == + log_record_header_length. When creating set + it to sizeof(LOG_RECORD_HEADER), aligned to + 8 bytes. */ +/* 38*/ le16 log_page_data_offset;/* Offset to the start of data in a log record + page. Must be a multiple of 8. On create + set it to immediately after the update + sequence array of the log record page. */ +/* 40*/ le32 restart_log_open_count;/* A counter that gets incremented every + time the logfile is restarted which happens + at mount time when the logfile is opened. + When creating set to a random value. Win2k + sets it to the low 32 bits of the current + system time in NTFS format (see time.h). */ +/* 44*/ le32 reserved; /* Reserved/alignment to 8-byte boundary. */ +/* sizeof() = 48 (0x30) bytes */ +} __attribute__((__packed__)) RESTART_AREA; /** * struct LOG_CLIENT_RECORD - Log client record. @@ -267,42 +262,40 @@ typedef struct * The offset of this record is found by adding the offset of the * RESTART_AREA to the client_array_offset value found in it. */ -typedef struct -{ - /*Ofs*/ - /* 0*/ - leLSN oldest_lsn; /* Oldest LSN needed by this client. On create - set to 0. */ - /* 8*/ leLSN client_restart_lsn;/* LSN at which this client needs to restart - the volume, i.e. the current position within - the log file. At present, if clean this - should = current_lsn in restart area but it - probably also = current_lsn when dirty most - of the time. At create set to 0. */ - /* 16*/ le16 prev_client; /* The offset to the previous log client record - in the array of log client records. - LOGFILE_NO_CLIENT means there is no previous - client record, i.e. this is the first one. - This is always LOGFILE_NO_CLIENT. */ - /* 18*/ le16 next_client; /* The offset to the next log client record in - the array of log client records. - LOGFILE_NO_CLIENT means there are no next - client records, i.e. this is the last one. - This is always LOGFILE_NO_CLIENT. */ - /* 20*/ le16 seq_number; /* On Win2k and presumably earlier, this is set - to zero every time the logfile is restarted - and it is incremented when the logfile is - closed at dismount time. Thus it is 0 when - dirty and 1 when clean. On WinXP and - presumably later, this is always 0. */ - /* 22*/ u8 reserved[6]; /* Reserved/alignment. */ - /* 28*/ le32 client_name_length;/* Length of client name in bytes. Should - always be 8. */ - /* 32*/ ntfschar client_name[64];/* Name of the client in Unicode. Should - always be "NTFS" with the remaining bytes - set to 0. */ - /* sizeof() = 160 (0xa0) bytes */ -} __attribute__( ( __packed__ ) ) LOG_CLIENT_RECORD; +typedef struct { +/*Ofs*/ +/* 0*/ leLSN oldest_lsn; /* Oldest LSN needed by this client. On create + set to 0. */ +/* 8*/ leLSN client_restart_lsn;/* LSN at which this client needs to restart + the volume, i.e. the current position within + the log file. At present, if clean this + should = current_lsn in restart area but it + probably also = current_lsn when dirty most + of the time. At create set to 0. */ +/* 16*/ le16 prev_client; /* The offset to the previous log client record + in the array of log client records. + LOGFILE_NO_CLIENT means there is no previous + client record, i.e. this is the first one. + This is always LOGFILE_NO_CLIENT. */ +/* 18*/ le16 next_client; /* The offset to the next log client record in + the array of log client records. + LOGFILE_NO_CLIENT means there are no next + client records, i.e. this is the last one. + This is always LOGFILE_NO_CLIENT. */ +/* 20*/ le16 seq_number; /* On Win2k and presumably earlier, this is set + to zero every time the logfile is restarted + and it is incremented when the logfile is + closed at dismount time. Thus it is 0 when + dirty and 1 when clean. On WinXP and + presumably later, this is always 0. */ +/* 22*/ u8 reserved[6]; /* Reserved/alignment. */ +/* 28*/ le32 client_name_length;/* Length of client name in bytes. Should + always be 8. */ +/* 32*/ ntfschar client_name[64];/* Name of the client in Unicode. Should + always be "NTFS" with the remaining bytes + set to 0. */ +/* sizeof() = 160 (0xa0) bytes */ +} __attribute__((__packed__)) LOG_CLIENT_RECORD; /** * struct RECORD_PAGE_HEADER - Log page record page header. @@ -312,98 +305,90 @@ typedef struct * following update sequence array and then aligned to 8 byte boundary, but is * this specified anywhere?). */ -typedef struct -{ - /* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */ - NTFS_RECORD_TYPES magic;/* Usually the magic is "RCRD". */ - u16 usa_ofs; /* See NTFS_RECORD definition in layout.h. - When creating, set this to be immediately - after this header structure (without any - alignment). */ - u16 usa_count; /* See NTFS_RECORD definition in layout.h. */ +typedef struct { +/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */ + NTFS_RECORD_TYPES magic;/* Usually the magic is "RCRD". */ + u16 usa_ofs; /* See NTFS_RECORD definition in layout.h. + When creating, set this to be immediately + after this header structure (without any + alignment). */ + u16 usa_count; /* See NTFS_RECORD definition in layout.h. */ - union - { - LSN last_lsn; - s64 file_offset; - } __attribute__( ( __packed__ ) ) copy; - u32 flags; - u16 page_count; - u16 page_position; - union - { - struct - { - u16 next_record_offset; - u8 reserved[6]; - LSN last_end_lsn; - } __attribute__( ( __packed__ ) ) packed; - } __attribute__( ( __packed__ ) ) header; -} __attribute__( ( __packed__ ) ) RECORD_PAGE_HEADER; + union { + LSN last_lsn; + s64 file_offset; + } __attribute__((__packed__)) copy; + u32 flags; + u16 page_count; + u16 page_position; + union { + struct { + u16 next_record_offset; + u8 reserved[6]; + LSN last_end_lsn; + } __attribute__((__packed__)) packed; + } __attribute__((__packed__)) header; +} __attribute__((__packed__)) RECORD_PAGE_HEADER; /** * enum LOG_RECORD_FLAGS - Possible 16-bit flags for log records. * * (Or is it log record pages?) */ -typedef enum -{ - LOG_RECORD_MULTI_PAGE = const_cpu_to_le16( 0x0001 ), /* ??? */ - LOG_RECORD_SIZE_PLACE_HOLDER = 0xffff, - /* This has nothing to do with the log record. It is only so - gcc knows to make the flags 16-bit. */ -} __attribute__( ( __packed__ ) ) LOG_RECORD_FLAGS; +typedef enum { + LOG_RECORD_MULTI_PAGE = const_cpu_to_le16(0x0001), /* ??? */ + LOG_RECORD_SIZE_PLACE_HOLDER = 0xffff, + /* This has nothing to do with the log record. It is only so + gcc knows to make the flags 16-bit. */ +} __attribute__((__packed__)) LOG_RECORD_FLAGS; /** * struct LOG_CLIENT_ID - The log client id structure identifying a log client. */ -typedef struct -{ - u16 seq_number; - u16 client_index; -} __attribute__( ( __packed__ ) ) LOG_CLIENT_ID; +typedef struct { + u16 seq_number; + u16 client_index; +} __attribute__((__packed__)) LOG_CLIENT_ID; /** * struct LOG_RECORD - Log record header. * * Each log record seems to have a constant size of 0x70 bytes. */ -typedef struct -{ - LSN this_lsn; - LSN client_previous_lsn; - LSN client_undo_next_lsn; - u32 client_data_length; - LOG_CLIENT_ID client_id; - u32 record_type; - u32 transaction_id; - u16 flags; - u16 reserved_or_alignment[3]; - /* Now are at ofs 0x30 into struct. */ - u16 redo_operation; - u16 undo_operation; - u16 redo_offset; - u16 redo_length; - u16 undo_offset; - u16 undo_length; - u16 target_attribute; - u16 lcns_to_follow; /* Number of lcn_list entries - following this entry. */ - /* Now at ofs 0x40. */ - u16 record_offset; - u16 attribute_offset; - u32 alignment_or_reserved; - VCN target_vcn; - /* Now at ofs 0x50. */ - struct - { /* Only present if lcns_to_follow - is not 0. */ - LCN lcn; - } __attribute__( ( __packed__ ) ) lcn_list[0]; -} __attribute__( ( __packed__ ) ) LOG_RECORD; +typedef struct { + LSN this_lsn; + LSN client_previous_lsn; + LSN client_undo_next_lsn; + u32 client_data_length; + LOG_CLIENT_ID client_id; + u32 record_type; + u32 transaction_id; + u16 flags; + u16 reserved_or_alignment[3]; +/* Now are at ofs 0x30 into struct. */ + u16 redo_operation; + u16 undo_operation; + u16 redo_offset; + u16 redo_length; + u16 undo_offset; + u16 undo_length; + u16 target_attribute; + u16 lcns_to_follow; /* Number of lcn_list entries + following this entry. */ +/* Now at ofs 0x40. */ + u16 record_offset; + u16 attribute_offset; + u32 alignment_or_reserved; + VCN target_vcn; +/* Now at ofs 0x50. */ + struct { /* Only present if lcns_to_follow + is not 0. */ + LCN lcn; + } __attribute__((__packed__)) lcn_list[0]; +} __attribute__((__packed__)) LOG_RECORD; -extern BOOL ntfs_check_logfile( ntfs_attr *log_na, RESTART_PAGE_HEADER **rp ); -extern BOOL ntfs_is_logfile_clean( ntfs_attr *log_na, RESTART_PAGE_HEADER *rp ); -extern int ntfs_empty_logfile( ntfs_attr *na ); +extern BOOL ntfs_check_logfile(ntfs_attr *log_na, RESTART_PAGE_HEADER **rp); +extern BOOL ntfs_is_logfile_clean(ntfs_attr *log_na, RESTART_PAGE_HEADER *rp); +extern int ntfs_empty_logfile(ntfs_attr *na); #endif /* defined _NTFS_LOGFILE_H */ diff --git a/source/libntfs/logging.c b/source/libntfs/logging.c index 15e572a8..385bcaa6 100644 --- a/source/libntfs/logging.c +++ b/source/libntfs/logging.c @@ -63,35 +63,33 @@ static int tab; /** * struct ntfs_logging - Control info for the logging system - * @levels: Bitfield of logging levels - * @flags: Flags which affect the output style - * @handler: Function to perform the actual logging + * @levels: Bitfield of logging levels + * @flags: Flags which affect the output style + * @handler: Function to perform the actual logging */ -struct ntfs_logging -{ - u32 levels; - u32 flags; - ntfs_log_handler *handler BROKEN_GCC_FORMAT_ATTRIBUTE; +struct ntfs_logging { + u32 levels; + u32 flags; + ntfs_log_handler *handler BROKEN_GCC_FORMAT_ATTRIBUTE; }; /** * ntfs_log * This struct controls all the logging within the library and tools. */ -static struct ntfs_logging ntfs_log = -{ +static struct ntfs_logging ntfs_log = { #ifdef DEBUG - NTFS_LOG_LEVEL_DEBUG | NTFS_LOG_LEVEL_TRACE | NTFS_LOG_LEVEL_ENTER | - NTFS_LOG_LEVEL_LEAVE | + NTFS_LOG_LEVEL_DEBUG | NTFS_LOG_LEVEL_TRACE | NTFS_LOG_LEVEL_ENTER | + NTFS_LOG_LEVEL_LEAVE | #endif - NTFS_LOG_LEVEL_INFO | NTFS_LOG_LEVEL_QUIET | NTFS_LOG_LEVEL_WARNING | - NTFS_LOG_LEVEL_ERROR | NTFS_LOG_LEVEL_PERROR | NTFS_LOG_LEVEL_CRITICAL | - NTFS_LOG_LEVEL_PROGRESS, - NTFS_LOG_FLAG_ONLYNAME, + NTFS_LOG_LEVEL_INFO | NTFS_LOG_LEVEL_QUIET | NTFS_LOG_LEVEL_WARNING | + NTFS_LOG_LEVEL_ERROR | NTFS_LOG_LEVEL_PERROR | NTFS_LOG_LEVEL_CRITICAL | + NTFS_LOG_LEVEL_PROGRESS, + NTFS_LOG_FLAG_ONLYNAME, #ifdef DEBUG - ntfs_log_handler_outerr + ntfs_log_handler_outerr #else - ntfs_log_handler_null + ntfs_log_handler_null #endif }; @@ -103,43 +101,43 @@ static struct ntfs_logging ntfs_log = * * Returns: Log levels in a 32-bit field */ -u32 ntfs_log_get_levels( void ) +u32 ntfs_log_get_levels(void) { - return ntfs_log.levels; + return ntfs_log.levels; } /** * ntfs_log_set_levels - Enable extra logging levels - * @levels: 32-bit field of log levels to set + * @levels: 32-bit field of log levels to set * * Enable one or more logging levels. * The logging levels are named: NTFS_LOG_LEVEL_*. * * Returns: Log levels that were enabled before the call */ -u32 ntfs_log_set_levels( u32 levels ) +u32 ntfs_log_set_levels(u32 levels) { - u32 old; - old = ntfs_log.levels; - ntfs_log.levels |= levels; - return old; + u32 old; + old = ntfs_log.levels; + ntfs_log.levels |= levels; + return old; } /** * ntfs_log_clear_levels - Disable some logging levels - * @levels: 32-bit field of log levels to clear + * @levels: 32-bit field of log levels to clear * * Disable one or more logging levels. * The logging levels are named: NTFS_LOG_LEVEL_*. * * Returns: Log levels that were enabled before the call */ -u32 ntfs_log_clear_levels( u32 levels ) +u32 ntfs_log_clear_levels(u32 levels) { - u32 old; - old = ntfs_log.levels; - ntfs_log.levels &= ( ~levels ); - return old; + u32 old; + old = ntfs_log.levels; + ntfs_log.levels &= (~levels); + return old; } @@ -150,167 +148,163 @@ u32 ntfs_log_clear_levels( u32 levels ) * * Returns: Logging flags in a 32-bit field */ -u32 ntfs_log_get_flags( void ) +u32 ntfs_log_get_flags(void) { - return ntfs_log.flags; + return ntfs_log.flags; } /** * ntfs_log_set_flags - Enable extra logging style flags - * @flags: 32-bit field of logging flags to set + * @flags: 32-bit field of logging flags to set * * Enable one or more logging flags. * The log flags are named: NTFS_LOG_LEVEL_*. * * Returns: Logging flags that were enabled before the call */ -u32 ntfs_log_set_flags( u32 flags ) +u32 ntfs_log_set_flags(u32 flags) { - u32 old; - old = ntfs_log.flags; - ntfs_log.flags |= flags; - return old; + u32 old; + old = ntfs_log.flags; + ntfs_log.flags |= flags; + return old; } /** * ntfs_log_clear_flags - Disable some logging styles - * @flags: 32-bit field of logging flags to clear + * @flags: 32-bit field of logging flags to clear * * Disable one or more logging flags. * The log flags are named: NTFS_LOG_LEVEL_*. * * Returns: Logging flags that were enabled before the call */ -u32 ntfs_log_clear_flags( u32 flags ) +u32 ntfs_log_clear_flags(u32 flags) { - u32 old; - old = ntfs_log.flags; - ntfs_log.flags &= ( ~flags ); - return old; + u32 old; + old = ntfs_log.flags; + ntfs_log.flags &= (~flags); + return old; } /** * ntfs_log_get_stream - Default output streams for logging levels - * @level: Log level + * @level: Log level * * By default, urgent messages are sent to "stderr". * Other messages are sent to "stdout". * * Returns: "string" Prefix to be used */ -static FILE * ntfs_log_get_stream( u32 level ) +static FILE * ntfs_log_get_stream(u32 level) { - FILE *stream; + FILE *stream; - switch ( level ) - { - case NTFS_LOG_LEVEL_INFO: - case NTFS_LOG_LEVEL_QUIET: - case NTFS_LOG_LEVEL_PROGRESS: - case NTFS_LOG_LEVEL_VERBOSE: - stream = stdout; - break; + switch (level) { + case NTFS_LOG_LEVEL_INFO: + case NTFS_LOG_LEVEL_QUIET: + case NTFS_LOG_LEVEL_PROGRESS: + case NTFS_LOG_LEVEL_VERBOSE: + stream = stdout; + break; - case NTFS_LOG_LEVEL_DEBUG: - case NTFS_LOG_LEVEL_TRACE: - case NTFS_LOG_LEVEL_ENTER: - case NTFS_LOG_LEVEL_LEAVE: - case NTFS_LOG_LEVEL_WARNING: - case NTFS_LOG_LEVEL_ERROR: - case NTFS_LOG_LEVEL_CRITICAL: - case NTFS_LOG_LEVEL_PERROR: - default: - stream = stderr; - break; - } + case NTFS_LOG_LEVEL_DEBUG: + case NTFS_LOG_LEVEL_TRACE: + case NTFS_LOG_LEVEL_ENTER: + case NTFS_LOG_LEVEL_LEAVE: + case NTFS_LOG_LEVEL_WARNING: + case NTFS_LOG_LEVEL_ERROR: + case NTFS_LOG_LEVEL_CRITICAL: + case NTFS_LOG_LEVEL_PERROR: + default: + stream = stderr; + break; + } - return stream; + return stream; } /** * ntfs_log_get_prefix - Default prefixes for logging levels - * @level: Log level to be prefixed + * @level: Log level to be prefixed * * Prefixing the logging output can make it easier to parse. * * Returns: "string" Prefix to be used */ -static const char * ntfs_log_get_prefix( u32 level ) +static const char * ntfs_log_get_prefix(u32 level) { - const char *prefix; + const char *prefix; - switch ( level ) - { - case NTFS_LOG_LEVEL_DEBUG: - prefix = "DEBUG: "; - break; - case NTFS_LOG_LEVEL_TRACE: - prefix = "TRACE: "; - break; - case NTFS_LOG_LEVEL_QUIET: - prefix = "QUIET: "; - break; - case NTFS_LOG_LEVEL_INFO: - prefix = "INFO: "; - break; - case NTFS_LOG_LEVEL_VERBOSE: - prefix = "VERBOSE: "; - break; - case NTFS_LOG_LEVEL_PROGRESS: - prefix = "PROGRESS: "; - break; - case NTFS_LOG_LEVEL_WARNING: - prefix = "WARNING: "; - break; - case NTFS_LOG_LEVEL_ERROR: - prefix = "ERROR: "; - break; - case NTFS_LOG_LEVEL_PERROR: - prefix = "ERROR: "; - break; - case NTFS_LOG_LEVEL_CRITICAL: - prefix = "CRITICAL: "; - break; - default: - prefix = ""; - break; - } + switch (level) { + case NTFS_LOG_LEVEL_DEBUG: + prefix = "DEBUG: "; + break; + case NTFS_LOG_LEVEL_TRACE: + prefix = "TRACE: "; + break; + case NTFS_LOG_LEVEL_QUIET: + prefix = "QUIET: "; + break; + case NTFS_LOG_LEVEL_INFO: + prefix = "INFO: "; + break; + case NTFS_LOG_LEVEL_VERBOSE: + prefix = "VERBOSE: "; + break; + case NTFS_LOG_LEVEL_PROGRESS: + prefix = "PROGRESS: "; + break; + case NTFS_LOG_LEVEL_WARNING: + prefix = "WARNING: "; + break; + case NTFS_LOG_LEVEL_ERROR: + prefix = "ERROR: "; + break; + case NTFS_LOG_LEVEL_PERROR: + prefix = "ERROR: "; + break; + case NTFS_LOG_LEVEL_CRITICAL: + prefix = "CRITICAL: "; + break; + default: + prefix = ""; + break; + } - return prefix; + return prefix; } /** * ntfs_log_set_handler - Provide an alternate logging handler - * @handler: function to perform the logging + * @handler: function to perform the logging * * This alternate handler will be called for all future logging requests. * If no @handler is specified, logging will revert to the default handler. */ -void ntfs_log_set_handler( ntfs_log_handler *handler ) +void ntfs_log_set_handler(ntfs_log_handler *handler) { - if ( handler ) - { - ntfs_log.handler = handler; + if (handler) { + ntfs_log.handler = handler; #ifdef HAVE_SYSLOG_H - if ( handler == ntfs_log_handler_syslog ) - openlog( "ntfs-3g", LOG_PID, LOG_USER ); + if (handler == ntfs_log_handler_syslog) + openlog("ntfs-3g", LOG_PID, LOG_USER); #endif - } - else - ntfs_log.handler = ntfs_log_handler_null; + } else + ntfs_log.handler = ntfs_log_handler_null; } /** * ntfs_log_redirect - Pass on the request to the real handler - * @function: Function in which the log line occurred - * @file: File in which the log line occurred - * @line: Line number on which the log line occurred - * @level: Level at which the line is logged - * @data: User specified data, possibly specific to a handler - * @format: printf-style formatting string - * @...: Arguments to be formatted + * @function: Function in which the log line occurred + * @file: File in which the log line occurred + * @line: Line number on which the log line occurred + * @level: Level at which the line is logged + * @data: User specified data, possibly specific to a handler + * @format: printf-style formatting string + * @...: Arguments to be formatted * * This is just a redirector function. The arguments are simply passed to the * main logging handler (as defined in the global logging struct @ntfs_log). @@ -319,35 +313,35 @@ void ntfs_log_set_handler( ntfs_log_handler *handler ) * 0 Message wasn't logged * num Number of output characters */ -int ntfs_log_redirect( const char *function, const char *file, - int line, u32 level, void *data, const char *format, ... ) +int ntfs_log_redirect(const char *function, const char *file, + int line, u32 level, void *data, const char *format, ...) { - int olderr = errno; - int ret; - va_list args; + int olderr = errno; + int ret; + va_list args; - if ( !( ntfs_log.levels & level ) ) /* Don't log this message */ - return 0; + if (!(ntfs_log.levels & level)) /* Don't log this message */ + return 0; - va_start( args, format ); - errno = olderr; - ret = ntfs_log.handler( function, file, line, level, data, format, args ); - va_end( args ); + va_start(args, format); + errno = olderr; + ret = ntfs_log.handler(function, file, line, level, data, format, args); + va_end(args); - errno = olderr; - return ret; + errno = olderr; + return ret; } /** * ntfs_log_handler_syslog - syslog logging handler - * @function: Function in which the log line occurred - * @file: File in which the log line occurred - * @line: Line number on which the log line occurred - * @level: Level at which the line is logged - * @data: User specified data, possibly specific to a handler - * @format: printf-style formatting string - * @args: Arguments to be formatted + * @function: Function in which the log line occurred + * @file: File in which the log line occurred + * @line: Line number on which the log line occurred + * @level: Level at which the line is logged + * @data: User specified data, possibly specific to a handler + * @format: printf-style formatting string + * @args: Arguments to be formatted * * A simple syslog logging handler. Ignores colors. * @@ -359,52 +353,50 @@ int ntfs_log_redirect( const char *function, const char *file, #ifdef HAVE_SYSLOG_H -#define LOG_LINE_LEN 512 +#define LOG_LINE_LEN 512 -int ntfs_log_handler_syslog( const char *function __attribute__( ( unused ) ), - const char *file __attribute__( ( unused ) ), - int line __attribute__( ( unused ) ), u32 level, - void *data __attribute__( ( unused ) ), - const char *format, va_list args ) +int ntfs_log_handler_syslog(const char *function __attribute__((unused)), + const char *file __attribute__((unused)), + int line __attribute__((unused)), u32 level, + void *data __attribute__((unused)), + const char *format, va_list args) { - char logbuf[LOG_LINE_LEN]; - int ret, olderr = errno; + char logbuf[LOG_LINE_LEN]; + int ret, olderr = errno; #ifndef DEBUG - if ( ( level & NTFS_LOG_LEVEL_PERROR ) && errno == ENOSPC ) - return 1; -#endif - ret = vsnprintf( logbuf, LOG_LINE_LEN, format, args ); - if ( ret < 0 ) - { - vsyslog( LOG_NOTICE, format, args ); - ret = 1; - goto out; - } - - if ( ( LOG_LINE_LEN > ret + 3 ) && ( level & NTFS_LOG_LEVEL_PERROR ) ) - { - strncat( logbuf, ": ", LOG_LINE_LEN - ret - 1 ); - strncat( logbuf, strerror( olderr ), LOG_LINE_LEN - ( ret + 3 ) ); - ret = strlen( logbuf ); - } - - syslog( LOG_NOTICE, "%s", logbuf ); + if ((level & NTFS_LOG_LEVEL_PERROR) && errno == ENOSPC) + return 1; +#endif + ret = vsnprintf(logbuf, LOG_LINE_LEN, format, args); + if (ret < 0) { + vsyslog(LOG_NOTICE, format, args); + ret = 1; + goto out; + } + + if ((LOG_LINE_LEN > ret + 3) && (level & NTFS_LOG_LEVEL_PERROR)) { + strncat(logbuf, ": ", LOG_LINE_LEN - ret - 1); + strncat(logbuf, strerror(olderr), LOG_LINE_LEN - (ret + 3)); + ret = strlen(logbuf); + } + + syslog(LOG_NOTICE, "%s", logbuf); out: - errno = olderr; - return ret; + errno = olderr; + return ret; } #endif /** * ntfs_log_handler_fprintf - Basic logging handler - * @function: Function in which the log line occurred - * @file: File in which the log line occurred - * @line: Line number on which the log line occurred - * @level: Level at which the line is logged - * @data: User specified data, possibly specific to a handler - * @format: printf-style formatting string - * @args: Arguments to be formatted + * @function: Function in which the log line occurred + * @file: File in which the log line occurred + * @line: Line number on which the log line occurred + * @level: Level at which the line is logged + * @data: User specified data, possibly specific to a handler + * @format: printf-style formatting string + * @args: Arguments to be formatted * * A simple logging handler. This is where the log line is finally displayed. * It is more likely that you will want to set the handler to either @@ -417,93 +409,92 @@ out: * 0 Message wasn't logged * num Number of output characters */ -int ntfs_log_handler_fprintf( const char *function, const char *file, - int line, u32 level, void *data, const char *format, va_list args ) +int ntfs_log_handler_fprintf(const char *function, const char *file, + int line, u32 level, void *data, const char *format, va_list args) { #ifdef DEBUG - int i; + int i; #endif - int ret = 0; - int olderr = errno; - FILE *stream; + int ret = 0; + int olderr = errno; + FILE *stream; - if ( !data ) /* Interpret data as a FILE stream. */ - return 0; /* If it's NULL, we can't do anything. */ - stream = ( FILE* )data; + if (!data) /* Interpret data as a FILE stream. */ + return 0; /* If it's NULL, we can't do anything. */ + stream = (FILE*)data; #ifdef DEBUG - if ( level == NTFS_LOG_LEVEL_LEAVE ) - { - if ( tab ) - tab--; - return 0; - } + if (level == NTFS_LOG_LEVEL_LEAVE) { + if (tab) + tab--; + return 0; + } + + for (i = 0; i < tab; i++) + ret += fprintf(stream, " "); +#endif + if ((ntfs_log.flags & NTFS_LOG_FLAG_ONLYNAME) && + (strchr(file, PATH_SEP))) /* Abbreviate the filename */ + file = strrchr(file, PATH_SEP) + 1; - for ( i = 0; i < tab; i++ ) - ret += fprintf( stream, " " ); -#endif - if ( ( ntfs_log.flags & NTFS_LOG_FLAG_ONLYNAME ) && - ( strchr( file, PATH_SEP ) ) ) /* Abbreviate the filename */ - file = strrchr( file, PATH_SEP ) + 1; + if (ntfs_log.flags & NTFS_LOG_FLAG_PREFIX) /* Prefix the output */ + ret += fprintf(stream, "%s", ntfs_log_get_prefix(level)); - if ( ntfs_log.flags & NTFS_LOG_FLAG_PREFIX ) /* Prefix the output */ - ret += fprintf( stream, "%s", ntfs_log_get_prefix( level ) ); + if (ntfs_log.flags & NTFS_LOG_FLAG_FILENAME) /* Source filename */ + ret += fprintf(stream, "%s ", file); - if ( ntfs_log.flags & NTFS_LOG_FLAG_FILENAME ) /* Source filename */ - ret += fprintf( stream, "%s ", file ); + if (ntfs_log.flags & NTFS_LOG_FLAG_LINE) /* Source line number */ + ret += fprintf(stream, "(%d) ", line); - if ( ntfs_log.flags & NTFS_LOG_FLAG_LINE ) /* Source line number */ - ret += fprintf( stream, "(%d) ", line ); + if ((ntfs_log.flags & NTFS_LOG_FLAG_FUNCTION) || /* Source function */ + (level & NTFS_LOG_LEVEL_TRACE) || (level & NTFS_LOG_LEVEL_ENTER)) + ret += fprintf(stream, "%s(): ", function); - if ( ( ntfs_log.flags & NTFS_LOG_FLAG_FUNCTION ) || /* Source function */ - ( level & NTFS_LOG_LEVEL_TRACE ) || ( level & NTFS_LOG_LEVEL_ENTER ) ) - ret += fprintf( stream, "%s(): ", function ); + ret += vfprintf(stream, format, args); - ret += vfprintf( stream, format, args ); - - if ( level & NTFS_LOG_LEVEL_PERROR ) - ret += fprintf( stream, ": %s\n", strerror( olderr ) ); + if (level & NTFS_LOG_LEVEL_PERROR) + ret += fprintf(stream, ": %s\n", strerror(olderr)); #ifdef DEBUG - if ( level == NTFS_LOG_LEVEL_ENTER ) - tab++; -#endif - fflush( stream ); - errno = olderr; - return ret; + if (level == NTFS_LOG_LEVEL_ENTER) + tab++; +#endif + fflush(stream); + errno = olderr; + return ret; } /** * ntfs_log_handler_null - Null logging handler (no output) - * @function: Function in which the log line occurred - * @file: File in which the log line occurred - * @line: Line number on which the log line occurred - * @level: Level at which the line is logged - * @data: User specified data, possibly specific to a handler - * @format: printf-style formatting string - * @args: Arguments to be formatted + * @function: Function in which the log line occurred + * @file: File in which the log line occurred + * @line: Line number on which the log line occurred + * @level: Level at which the line is logged + * @data: User specified data, possibly specific to a handler + * @format: printf-style formatting string + * @args: Arguments to be formatted * * This handler produces no output. It provides a way to temporarily disable * logging, without having to change the levels and flags. * * Returns: 0 Message wasn't logged */ -int ntfs_log_handler_null( const char *function __attribute__( ( unused ) ), const char *file __attribute__( ( unused ) ), - int line __attribute__( ( unused ) ), u32 level __attribute__( ( unused ) ), void *data __attribute__( ( unused ) ), - const char *format __attribute__( ( unused ) ), va_list args __attribute__( ( unused ) ) ) +int ntfs_log_handler_null(const char *function __attribute__((unused)), const char *file __attribute__((unused)), + int line __attribute__((unused)), u32 level __attribute__((unused)), void *data __attribute__((unused)), + const char *format __attribute__((unused)), va_list args __attribute__((unused))) { - return 0; + return 0; } /** * ntfs_log_handler_stdout - All logs go to stdout - * @function: Function in which the log line occurred - * @file: File in which the log line occurred - * @line: Line number on which the log line occurred - * @level: Level at which the line is logged - * @data: User specified data, possibly specific to a handler - * @format: printf-style formatting string - * @args: Arguments to be formatted + * @function: Function in which the log line occurred + * @file: File in which the log line occurred + * @line: Line number on which the log line occurred + * @level: Level at which the line is logged + * @data: User specified data, possibly specific to a handler + * @format: printf-style formatting string + * @args: Arguments to be formatted * * Display a log message to stdout. * @@ -516,24 +507,24 @@ int ntfs_log_handler_null( const char *function __attribute__( ( unused ) ), con * 0 Message wasn't logged * num Number of output characters */ -int ntfs_log_handler_stdout( const char *function, const char *file, - int line, u32 level, void *data, const char *format, va_list args ) +int ntfs_log_handler_stdout(const char *function, const char *file, + int line, u32 level, void *data, const char *format, va_list args) { - if ( !data ) - data = stdout; + if (!data) + data = stdout; - return ntfs_log_handler_fprintf( function, file, line, level, data, format, args ); + return ntfs_log_handler_fprintf(function, file, line, level, data, format, args); } /** * ntfs_log_handler_outerr - Logs go to stdout/stderr depending on level - * @function: Function in which the log line occurred - * @file: File in which the log line occurred - * @line: Line number on which the log line occurred - * @level: Level at which the line is logged - * @data: User specified data, possibly specific to a handler - * @format: printf-style formatting string - * @args: Arguments to be formatted + * @function: Function in which the log line occurred + * @file: File in which the log line occurred + * @line: Line number on which the log line occurred + * @level: Level at which the line is logged + * @data: User specified data, possibly specific to a handler + * @format: printf-style formatting string + * @args: Arguments to be formatted * * Display a log message. The output stream will be determined by the log * level. @@ -547,24 +538,24 @@ int ntfs_log_handler_stdout( const char *function, const char *file, * 0 Message wasn't logged * num Number of output characters */ -int ntfs_log_handler_outerr( const char *function, const char *file, - int line, u32 level, void *data, const char *format, va_list args ) +int ntfs_log_handler_outerr(const char *function, const char *file, + int line, u32 level, void *data, const char *format, va_list args) { - if ( !data ) - data = ntfs_log_get_stream( level ); + if (!data) + data = ntfs_log_get_stream(level); - return ntfs_log_handler_fprintf( function, file, line, level, data, format, args ); + return ntfs_log_handler_fprintf(function, file, line, level, data, format, args); } /** * ntfs_log_handler_stderr - All logs go to stderr - * @function: Function in which the log line occurred - * @file: File in which the log line occurred - * @line: Line number on which the log line occurred - * @level: Level at which the line is logged - * @data: User specified data, possibly specific to a handler - * @format: printf-style formatting string - * @args: Arguments to be formatted + * @function: Function in which the log line occurred + * @file: File in which the log line occurred + * @line: Line number on which the log line occurred + * @level: Level at which the line is logged + * @data: User specified data, possibly specific to a handler + * @format: printf-style formatting string + * @args: Arguments to be formatted * * Display a log message to stderr. * @@ -577,19 +568,19 @@ int ntfs_log_handler_outerr( const char *function, const char *file, * 0 Message wasn't logged * num Number of output characters */ -int ntfs_log_handler_stderr( const char *function, const char *file, - int line, u32 level, void *data, const char *format, va_list args ) +int ntfs_log_handler_stderr(const char *function, const char *file, + int line, u32 level, void *data, const char *format, va_list args) { - if ( !data ) - data = stderr; + if (!data) + data = stderr; - return ntfs_log_handler_fprintf( function, file, line, level, data, format, args ); + return ntfs_log_handler_fprintf(function, file, line, level, data, format, args); } /** * ntfs_log_parse_option - Act upon command line options - * @option: Option flag + * @option: Option flag * * Delegate some of the work of parsing the command line. All the options begin * with "--log-". Options cause log levels to be enabled in @ntfs_log (the @@ -600,30 +591,23 @@ int ntfs_log_handler_stderr( const char *function, const char *file, * Returns: TRUE Option understood * FALSE Invalid log option */ -BOOL ntfs_log_parse_option( const char *option ) +BOOL ntfs_log_parse_option(const char *option) { - if ( strcmp( option, "--log-debug" ) == 0 ) - { - ntfs_log_set_levels( NTFS_LOG_LEVEL_DEBUG ); - return TRUE; - } - else if ( strcmp( option, "--log-verbose" ) == 0 ) - { - ntfs_log_set_levels( NTFS_LOG_LEVEL_VERBOSE ); - return TRUE; - } - else if ( strcmp( option, "--log-quiet" ) == 0 ) - { - ntfs_log_clear_levels( NTFS_LOG_LEVEL_QUIET ); - return TRUE; - } - else if ( strcmp( option, "--log-trace" ) == 0 ) - { - ntfs_log_set_levels( NTFS_LOG_LEVEL_TRACE ); - return TRUE; - } + if (strcmp(option, "--log-debug") == 0) { + ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG); + return TRUE; + } else if (strcmp(option, "--log-verbose") == 0) { + ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); + return TRUE; + } else if (strcmp(option, "--log-quiet") == 0) { + ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); + return TRUE; + } else if (strcmp(option, "--log-trace") == 0) { + ntfs_log_set_levels(NTFS_LOG_LEVEL_TRACE); + return TRUE; + } - ntfs_log_debug( "Unknown logging option '%s'\n", option ); - return FALSE; + ntfs_log_debug("Unknown logging option '%s'\n", option); + return FALSE; } diff --git a/source/libntfs/logging.h b/source/libntfs/logging.h index 39c82e46..401f5c97 100644 --- a/source/libntfs/logging.h +++ b/source/libntfs/logging.h @@ -34,58 +34,58 @@ #include "types.h" /* Function prototype for the logging handlers */ -typedef int ( ntfs_log_handler )( const char *function, const char *file, int line, - u32 level, void *data, const char *format, va_list args ); +typedef int (ntfs_log_handler)(const char *function, const char *file, int line, + u32 level, void *data, const char *format, va_list args); /* Set the logging handler from one of the functions, below. */ -void ntfs_log_set_handler( ntfs_log_handler *handler - __attribute__( ( format( printf, 6, 0 ) ) ) ); +void ntfs_log_set_handler(ntfs_log_handler *handler + __attribute__((format(printf, 6, 0)))); /* Logging handlers */ -ntfs_log_handler ntfs_log_handler_syslog __attribute__( ( format( printf, 6, 0 ) ) ); -ntfs_log_handler ntfs_log_handler_fprintf __attribute__( ( format( printf, 6, 0 ) ) ); -ntfs_log_handler ntfs_log_handler_null __attribute__( ( format( printf, 6, 0 ) ) ); -ntfs_log_handler ntfs_log_handler_stdout __attribute__( ( format( printf, 6, 0 ) ) ); -ntfs_log_handler ntfs_log_handler_outerr __attribute__( ( format( printf, 6, 0 ) ) ); -ntfs_log_handler ntfs_log_handler_stderr __attribute__( ( format( printf, 6, 0 ) ) ); +ntfs_log_handler ntfs_log_handler_syslog __attribute__((format(printf, 6, 0))); +ntfs_log_handler ntfs_log_handler_fprintf __attribute__((format(printf, 6, 0))); +ntfs_log_handler ntfs_log_handler_null __attribute__((format(printf, 6, 0))); +ntfs_log_handler ntfs_log_handler_stdout __attribute__((format(printf, 6, 0))); +ntfs_log_handler ntfs_log_handler_outerr __attribute__((format(printf, 6, 0))); +ntfs_log_handler ntfs_log_handler_stderr __attribute__((format(printf, 6, 0))); /* Enable/disable certain log levels */ -u32 ntfs_log_set_levels( u32 levels ); -u32 ntfs_log_clear_levels( u32 levels ); -u32 ntfs_log_get_levels( void ); +u32 ntfs_log_set_levels(u32 levels); +u32 ntfs_log_clear_levels(u32 levels); +u32 ntfs_log_get_levels(void); /* Enable/disable certain log flags */ -u32 ntfs_log_set_flags( u32 flags ); -u32 ntfs_log_clear_flags( u32 flags ); -u32 ntfs_log_get_flags( void ); +u32 ntfs_log_set_flags(u32 flags); +u32 ntfs_log_clear_flags(u32 flags); +u32 ntfs_log_get_flags(void); /* Turn command-line options into logging flags */ -BOOL ntfs_log_parse_option( const char *option ); +BOOL ntfs_log_parse_option(const char *option); -int ntfs_log_redirect( const char *function, const char *file, int line, - u32 level, void *data, const char *format, ... ) -__attribute__( ( format( printf, 6, 7 ) ) ); +int ntfs_log_redirect(const char *function, const char *file, int line, + u32 level, void *data, const char *format, ...) + __attribute__((format(printf, 6, 7))); /* Logging levels - Determine what gets logged */ -#define NTFS_LOG_LEVEL_DEBUG (1 << 0) /* x = 42 */ -#define NTFS_LOG_LEVEL_TRACE (1 << 1) /* Entering function x() */ -#define NTFS_LOG_LEVEL_QUIET (1 << 2) /* Quietable output */ -#define NTFS_LOG_LEVEL_INFO (1 << 3) /* Volume needs defragmenting */ -#define NTFS_LOG_LEVEL_VERBOSE (1 << 4) /* Forced to continue */ -#define NTFS_LOG_LEVEL_PROGRESS (1 << 5) /* 54% complete */ -#define NTFS_LOG_LEVEL_WARNING (1 << 6) /* You should backup before starting */ -#define NTFS_LOG_LEVEL_ERROR (1 << 7) /* Operation failed, no damage done */ -#define NTFS_LOG_LEVEL_PERROR (1 << 8) /* Message : standard error description */ -#define NTFS_LOG_LEVEL_CRITICAL (1 << 9) /* Operation failed,damage may have occurred */ -#define NTFS_LOG_LEVEL_ENTER (1 << 10) /* Enter a function */ -#define NTFS_LOG_LEVEL_LEAVE (1 << 11) /* Leave a function */ +#define NTFS_LOG_LEVEL_DEBUG (1 << 0) /* x = 42 */ +#define NTFS_LOG_LEVEL_TRACE (1 << 1) /* Entering function x() */ +#define NTFS_LOG_LEVEL_QUIET (1 << 2) /* Quietable output */ +#define NTFS_LOG_LEVEL_INFO (1 << 3) /* Volume needs defragmenting */ +#define NTFS_LOG_LEVEL_VERBOSE (1 << 4) /* Forced to continue */ +#define NTFS_LOG_LEVEL_PROGRESS (1 << 5) /* 54% complete */ +#define NTFS_LOG_LEVEL_WARNING (1 << 6) /* You should backup before starting */ +#define NTFS_LOG_LEVEL_ERROR (1 << 7) /* Operation failed, no damage done */ +#define NTFS_LOG_LEVEL_PERROR (1 << 8) /* Message : standard error description */ +#define NTFS_LOG_LEVEL_CRITICAL (1 << 9) /* Operation failed,damage may have occurred */ +#define NTFS_LOG_LEVEL_ENTER (1 << 10) /* Enter a function */ +#define NTFS_LOG_LEVEL_LEAVE (1 << 11) /* Leave a function */ /* Logging style flags - Manage the style of the output */ -#define NTFS_LOG_FLAG_PREFIX (1 << 0) /* Prefix messages with "ERROR: ", etc */ -#define NTFS_LOG_FLAG_FILENAME (1 << 1) /* Show the file origin of the message */ -#define NTFS_LOG_FLAG_LINE (1 << 2) /* Show the line number of the message */ -#define NTFS_LOG_FLAG_FUNCTION (1 << 3) /* Show the function name containing the message */ -#define NTFS_LOG_FLAG_ONLYNAME (1 << 4) /* Only display the filename, not the pathname */ +#define NTFS_LOG_FLAG_PREFIX (1 << 0) /* Prefix messages with "ERROR: ", etc */ +#define NTFS_LOG_FLAG_FILENAME (1 << 1) /* Show the file origin of the message */ +#define NTFS_LOG_FLAG_LINE (1 << 2) /* Show the line number of the message */ +#define NTFS_LOG_FLAG_FUNCTION (1 << 3) /* Show the function name containing the message */ +#define NTFS_LOG_FLAG_ONLYNAME (1 << 4) /* Only display the filename, not the pathname */ /* Macros to simplify logging. One for each level defined above. * Note, ntfs_log_debug/trace have effect only if DEBUG is defined. diff --git a/source/libntfs/mem_allocate.h b/source/libntfs/mem_allocate.h index e87afbe4..600e5a93 100644 --- a/source/libntfs/mem_allocate.h +++ b/source/libntfs/mem_allocate.h @@ -24,23 +24,20 @@ #include -static inline void* ntfs_alloc ( size_t size ) -{ - return malloc( size ); +static inline void* ntfs_alloc (size_t size) { + return malloc(size); } -static inline void* ntfs_align ( size_t size ) -{ -#ifdef __wii__ - return memalign( 32, size ); -#else - return malloc( size ); -#endif +static inline void* ntfs_align (size_t size) { + #ifdef __wii__ + return memalign(32, size); + #else + return malloc(size); + #endif } -static inline void ntfs_free ( void* mem ) -{ - free( mem ); +static inline void ntfs_free (void* mem) { + free(mem); } #endif /* _MEM_ALLOCATE_H */ diff --git a/source/libntfs/mft.c b/source/libntfs/mft.c index e61609f3..e93c6646 100644 --- a/source/libntfs/mft.c +++ b/source/libntfs/mft.c @@ -59,10 +59,10 @@ /** * ntfs_mft_records_read - read records from the mft from disk - * @vol: volume to read from - * @mref: starting mft record number to read - * @count: number of mft records to read - * @b: output data buffer + * @vol: volume to read from + * @mref: starting mft record number to read + * @count: number of mft records to read + * @b: output data buffer * * Read @count mft records starting at @mref from volume @vol into buffer * @b. Return 0 on success or -1 on error, with errno set to the error @@ -78,53 +78,50 @@ * * NOTE: @b has to be at least of size @count * vol->mft_record_size. */ -int ntfs_mft_records_read( const ntfs_volume *vol, const MFT_REF mref, - const s64 count, MFT_RECORD *b ) +int ntfs_mft_records_read(const ntfs_volume *vol, const MFT_REF mref, + const s64 count, MFT_RECORD *b) { - s64 br; - VCN m; + s64 br; + VCN m; - ntfs_log_trace( "inode %llu\n", ( unsigned long long )MREF( mref ) ); - - if ( !vol || !vol->mft_na || !b || count < 0 ) - { - errno = EINVAL; - ntfs_log_perror( "%s: b=%p count=%lld mft=%llu", __FUNCTION__, - b, ( long long )count, ( unsigned long long )MREF( mref ) ); - return -1; - } - m = MREF( mref ); - /* Refuse to read non-allocated mft records. */ - if ( m + count > vol->mft_na->initialized_size >> - vol->mft_record_size_bits ) - { - errno = ESPIPE; - ntfs_log_perror( "Trying to read non-allocated mft records " - "(%lld > %lld)", ( long long )m + count, - ( long long )vol->mft_na->initialized_size >> - vol->mft_record_size_bits ); - return -1; - } - br = ntfs_attr_mst_pread( vol->mft_na, m << vol->mft_record_size_bits, - count, vol->mft_record_size, b ); - if ( br != count ) - { - if ( br != -1 ) - errno = EIO; - ntfs_log_perror( "Failed to read of MFT, mft=%llu count=%lld " - "br=%lld", ( long long )m, ( long long )count, - ( long long )br ); - return -1; - } - return 0; + ntfs_log_trace("inode %llu\n", (unsigned long long)MREF(mref)); + + if (!vol || !vol->mft_na || !b || count < 0) { + errno = EINVAL; + ntfs_log_perror("%s: b=%p count=%lld mft=%llu", __FUNCTION__, + b, (long long)count, (unsigned long long)MREF(mref)); + return -1; + } + m = MREF(mref); + /* Refuse to read non-allocated mft records. */ + if (m + count > vol->mft_na->initialized_size >> + vol->mft_record_size_bits) { + errno = ESPIPE; + ntfs_log_perror("Trying to read non-allocated mft records " + "(%lld > %lld)", (long long)m + count, + (long long)vol->mft_na->initialized_size >> + vol->mft_record_size_bits); + return -1; + } + br = ntfs_attr_mst_pread(vol->mft_na, m << vol->mft_record_size_bits, + count, vol->mft_record_size, b); + if (br != count) { + if (br != -1) + errno = EIO; + ntfs_log_perror("Failed to read of MFT, mft=%llu count=%lld " + "br=%lld", (long long)m, (long long)count, + (long long)br); + return -1; + } + return 0; } /** * ntfs_mft_records_write - write mft records to disk - * @vol: volume to write to - * @mref: starting mft record number to write - * @count: number of mft records to write - * @b: data buffer containing the mft records to write + * @vol: volume to write to + * @mref: starting mft record number to write + * @count: number of mft records to write + * @b: data buffer containing the mft records to write * * Write @count mft records starting at @mref from data buffer @b to volume * @vol. Return 0 on success or -1 on error, with errno set to the error code. @@ -143,125 +140,115 @@ int ntfs_mft_records_read( const ntfs_volume *vol, const MFT_REF mref, * record was successfully written, we write the appropriate mft records from * the copied buffer to the mft mirror, too. */ -int ntfs_mft_records_write( const ntfs_volume *vol, const MFT_REF mref, - const s64 count, MFT_RECORD *b ) +int ntfs_mft_records_write(const ntfs_volume *vol, const MFT_REF mref, + const s64 count, MFT_RECORD *b) { - s64 bw; - VCN m; - void *bmirr = NULL; - int cnt = 0, res = 0; + s64 bw; + VCN m; + void *bmirr = NULL; + int cnt = 0, res = 0; - if ( !vol || !vol->mft_na || vol->mftmirr_size <= 0 || !b || count < 0 ) - { - errno = EINVAL; - return -1; - } - m = MREF( mref ); - /* Refuse to write non-allocated mft records. */ - if ( m + count > vol->mft_na->initialized_size >> - vol->mft_record_size_bits ) - { - errno = ESPIPE; - ntfs_log_perror( "Trying to write non-allocated mft records " - "(%lld > %lld)", ( long long )m + count, - ( long long )vol->mft_na->initialized_size >> - vol->mft_record_size_bits ); - return -1; - } - if ( m < vol->mftmirr_size ) - { - if ( !vol->mftmirr_na ) - { - errno = EINVAL; - return -1; - } - cnt = vol->mftmirr_size - m; - if ( cnt > count ) - cnt = count; - bmirr = ntfs_malloc( cnt * vol->mft_record_size ); - if ( !bmirr ) - return -1; - memcpy( bmirr, b, cnt * vol->mft_record_size ); - } - bw = ntfs_attr_mst_pwrite( vol->mft_na, m << vol->mft_record_size_bits, - count, vol->mft_record_size, b ); - if ( bw != count ) - { - if ( bw != -1 ) - errno = EIO; - if ( bw >= 0 ) - ntfs_log_debug( "Error: partial write while writing $Mft " - "record(s)!\n" ); - else - ntfs_log_perror( "Error writing $Mft record(s)" ); - res = errno; - } - if ( bmirr && bw > 0 ) - { - if ( bw < cnt ) - cnt = bw; - bw = ntfs_attr_mst_pwrite( vol->mftmirr_na, - m << vol->mft_record_size_bits, cnt, - vol->mft_record_size, bmirr ); - if ( bw != cnt ) - { - if ( bw != -1 ) - errno = EIO; - ntfs_log_debug( "Error: failed to sync $MFTMirr! Run " - "chkdsk.\n" ); - res = errno; - } - } - free( bmirr ); - if ( !res ) - return res; - errno = res; - return -1; + if (!vol || !vol->mft_na || vol->mftmirr_size <= 0 || !b || count < 0) { + errno = EINVAL; + return -1; + } + m = MREF(mref); + /* Refuse to write non-allocated mft records. */ + if (m + count > vol->mft_na->initialized_size >> + vol->mft_record_size_bits) { + errno = ESPIPE; + ntfs_log_perror("Trying to write non-allocated mft records " + "(%lld > %lld)", (long long)m + count, + (long long)vol->mft_na->initialized_size >> + vol->mft_record_size_bits); + return -1; + } + if (m < vol->mftmirr_size) { + if (!vol->mftmirr_na) { + errno = EINVAL; + return -1; + } + cnt = vol->mftmirr_size - m; + if (cnt > count) + cnt = count; + bmirr = ntfs_malloc(cnt * vol->mft_record_size); + if (!bmirr) + return -1; + memcpy(bmirr, b, cnt * vol->mft_record_size); + } + bw = ntfs_attr_mst_pwrite(vol->mft_na, m << vol->mft_record_size_bits, + count, vol->mft_record_size, b); + if (bw != count) { + if (bw != -1) + errno = EIO; + if (bw >= 0) + ntfs_log_debug("Error: partial write while writing $Mft " + "record(s)!\n"); + else + ntfs_log_perror("Error writing $Mft record(s)"); + res = errno; + } + if (bmirr && bw > 0) { + if (bw < cnt) + cnt = bw; + bw = ntfs_attr_mst_pwrite(vol->mftmirr_na, + m << vol->mft_record_size_bits, cnt, + vol->mft_record_size, bmirr); + if (bw != cnt) { + if (bw != -1) + errno = EIO; + ntfs_log_debug("Error: failed to sync $MFTMirr! Run " + "chkdsk.\n"); + res = errno; + } + } + free(bmirr); + if (!res) + return res; + errno = res; + return -1; } -int ntfs_mft_record_check( const ntfs_volume *vol, const MFT_REF mref, - MFT_RECORD *m ) -{ - ATTR_RECORD *a; - int ret = -1; - - if ( !ntfs_is_file_record( m->magic ) ) - { - ntfs_log_error( "Record %llu has no FILE magic (0x%x)\n", - ( unsigned long long )MREF( mref ), *( le32 * )m ); - goto err_out; - } - - if ( le32_to_cpu( m->bytes_allocated ) != vol->mft_record_size ) - { - ntfs_log_error( "Record %llu has corrupt allocation size " - "(%u <> %u)\n", ( unsigned long long )MREF( mref ), - vol->mft_record_size, - le32_to_cpu( m->bytes_allocated ) ); - goto err_out; - } - - a = ( ATTR_RECORD * )( ( char * )m + le16_to_cpu( m->attrs_offset ) ); - if ( p2n( a ) < p2n( m ) || ( char * )a > ( char * )m + vol->mft_record_size ) - { - ntfs_log_error( "Record %llu is corrupt\n", - ( unsigned long long )MREF( mref ) ); - goto err_out; - } - - ret = 0; +int ntfs_mft_record_check(const ntfs_volume *vol, const MFT_REF mref, + MFT_RECORD *m) +{ + ATTR_RECORD *a; + int ret = -1; + + if (!ntfs_is_file_record(m->magic)) { + ntfs_log_error("Record %llu has no FILE magic (0x%x)\n", + (unsigned long long)MREF(mref), *(le32 *)m); + goto err_out; + } + + if (le32_to_cpu(m->bytes_allocated) != vol->mft_record_size) { + ntfs_log_error("Record %llu has corrupt allocation size " + "(%u <> %u)\n", (unsigned long long)MREF(mref), + vol->mft_record_size, + le32_to_cpu(m->bytes_allocated)); + goto err_out; + } + + a = (ATTR_RECORD *)((char *)m + le16_to_cpu(m->attrs_offset)); + if (p2n(a) < p2n(m) || (char *)a > (char *)m + vol->mft_record_size) { + ntfs_log_error("Record %llu is corrupt\n", + (unsigned long long)MREF(mref)); + goto err_out; + } + + ret = 0; err_out: - if ( ret ) - errno = EIO; - return ret; + if (ret) + errno = EIO; + return ret; } /** * ntfs_file_record_read - read a FILE record from the mft from disk - * @vol: volume to read from - * @mref: mft reference specifying mft record to read - * @mrec: address of pointer in which to return the mft record - * @attr: address of pointer in which to return the first attribute + * @vol: volume to read from + * @mref: mft reference specifying mft record to read + * @mrec: address of pointer in which to return the mft record + * @attr: address of pointer in which to return the first attribute * * Read a FILE record from the mft of @vol from the storage medium. @mref * specifies the mft record to read, including the sequence number, which can @@ -285,56 +272,53 @@ err_out: * Note: Caller has to free *@mrec when finished. * * Note: We do not check if the mft record is flagged in use. The caller can - * check if desired. + * check if desired. */ -int ntfs_file_record_read( const ntfs_volume *vol, const MFT_REF mref, - MFT_RECORD **mrec, ATTR_RECORD **attr ) +int ntfs_file_record_read(const ntfs_volume *vol, const MFT_REF mref, + MFT_RECORD **mrec, ATTR_RECORD **attr) { - MFT_RECORD *m; + MFT_RECORD *m; - if ( !vol || !mrec ) - { - errno = EINVAL; - ntfs_log_perror( "%s: mrec=%p", __FUNCTION__, mrec ); - return -1; - } + if (!vol || !mrec) { + errno = EINVAL; + ntfs_log_perror("%s: mrec=%p", __FUNCTION__, mrec); + return -1; + } + + m = *mrec; + if (!m) { + m = ntfs_malloc(vol->mft_record_size); + if (!m) + return -1; + } + if (ntfs_mft_record_read(vol, mref, m)) + goto err_out; - m = *mrec; - if ( !m ) - { - m = ntfs_malloc( vol->mft_record_size ); - if ( !m ) - return -1; - } - if ( ntfs_mft_record_read( vol, mref, m ) ) - goto err_out; - - if ( ntfs_mft_record_check( vol, mref, m ) ) - goto err_out; - - if ( MSEQNO( mref ) && MSEQNO( mref ) != le16_to_cpu( m->sequence_number ) ) - { - ntfs_log_error( "Record %llu has wrong SeqNo (%d <> %d)\n", - ( unsigned long long )MREF( mref ), MSEQNO( mref ), - le16_to_cpu( m->sequence_number ) ); - errno = EIO; - goto err_out; - } - *mrec = m; - if ( attr ) - *attr = ( ATTR_RECORD* )( ( char* )m + le16_to_cpu( m->attrs_offset ) ); - return 0; + if (ntfs_mft_record_check(vol, mref, m)) + goto err_out; + + if (MSEQNO(mref) && MSEQNO(mref) != le16_to_cpu(m->sequence_number)) { + ntfs_log_error("Record %llu has wrong SeqNo (%d <> %d)\n", + (unsigned long long)MREF(mref), MSEQNO(mref), + le16_to_cpu(m->sequence_number)); + errno = EIO; + goto err_out; + } + *mrec = m; + if (attr) + *attr = (ATTR_RECORD*)((char*)m + le16_to_cpu(m->attrs_offset)); + return 0; err_out: - if ( m != *mrec ) - free( m ); - return -1; + if (m != *mrec) + free(m); + return -1; } /** * ntfs_mft_record_layout - layout an mft record into a memory buffer - * @vol: volume to which the mft record will belong - * @mref: mft reference specifying the mft record number - * @mrec: destination buffer of size >= @vol->mft_record_size bytes + * @vol: volume to which the mft record will belong + * @mref: mft reference specifying the mft record number + * @mrec: destination buffer of size >= @vol->mft_record_size bytes * * Layout an empty, unused mft record with the mft reference @mref into the * buffer @m. The volume @vol is needed because the mft record structure was @@ -343,110 +327,106 @@ err_out: * * On success return 0 and on error return -1 with errno set to the error code. */ -int ntfs_mft_record_layout( const ntfs_volume *vol, const MFT_REF mref, - MFT_RECORD *mrec ) +int ntfs_mft_record_layout(const ntfs_volume *vol, const MFT_REF mref, + MFT_RECORD *mrec) { - ATTR_RECORD *a; + ATTR_RECORD *a; - if ( !vol || !mrec ) - { - errno = EINVAL; - ntfs_log_perror( "%s: mrec=%p", __FUNCTION__, mrec ); - return -1; - } - /* Aligned to 2-byte boundary. */ - if ( vol->major_ver < 3 || ( vol->major_ver == 3 && !vol->minor_ver ) ) - mrec->usa_ofs = cpu_to_le16( ( sizeof( MFT_RECORD_OLD ) + 1 ) & ~1 ); - else - { - /* Abort if mref is > 32 bits. */ - if ( MREF( mref ) & 0x0000ffff00000000ull ) - { - errno = ERANGE; - ntfs_log_perror( "Mft reference exceeds 32 bits" ); - return -1; - } - mrec->usa_ofs = cpu_to_le16( ( sizeof( MFT_RECORD ) + 1 ) & ~1 ); - /* - * Set the NTFS 3.1+ specific fields while we know that the - * volume version is 3.1+. - */ - mrec->reserved = cpu_to_le16( 0 ); - mrec->mft_record_number = cpu_to_le32( MREF( mref ) ); - } - mrec->magic = magic_FILE; - if ( vol->mft_record_size >= NTFS_BLOCK_SIZE ) - mrec->usa_count = cpu_to_le16( vol->mft_record_size / - NTFS_BLOCK_SIZE + 1 ); - else - { - mrec->usa_count = cpu_to_le16( 1 ); - ntfs_log_error( "Sector size is bigger than MFT record size. " - "Setting usa_count to 1. If Windows chkdsk " - "reports this as corruption, please email %s " - "stating that you saw this message and that " - "the file system created was corrupt. " - "Thank you.\n", NTFS_DEV_LIST ); - } - /* Set the update sequence number to 1. */ - *( u16* )( ( u8* )mrec + le16_to_cpu( mrec->usa_ofs ) ) = cpu_to_le16( 1 ); - mrec->lsn = cpu_to_le64( 0ull ); - mrec->sequence_number = cpu_to_le16( 1 ); - mrec->link_count = cpu_to_le16( 0 ); - /* Aligned to 8-byte boundary. */ - mrec->attrs_offset = cpu_to_le16( ( le16_to_cpu( mrec->usa_ofs ) + - ( le16_to_cpu( mrec->usa_count ) << 1 ) + 7 ) & ~7 ); - mrec->flags = cpu_to_le16( 0 ); - /* - * Using attrs_offset plus eight bytes (for the termination attribute), - * aligned to 8-byte boundary. - */ - mrec->bytes_in_use = cpu_to_le32( ( le16_to_cpu( mrec->attrs_offset ) + 8 + - 7 ) & ~7 ); - mrec->bytes_allocated = cpu_to_le32( vol->mft_record_size ); - mrec->base_mft_record = cpu_to_le64( ( MFT_REF )0 ); - mrec->next_attr_instance = cpu_to_le16( 0 ); - a = ( ATTR_RECORD* )( ( u8* )mrec + le16_to_cpu( mrec->attrs_offset ) ); - a->type = AT_END; - a->length = cpu_to_le32( 0 ); - /* Finally, clear the unused part of the mft record. */ - memset( ( u8* )a + 8, 0, vol->mft_record_size - ( ( u8* )a + 8 - ( u8* )mrec ) ); - return 0; + if (!vol || !mrec) { + errno = EINVAL; + ntfs_log_perror("%s: mrec=%p", __FUNCTION__, mrec); + return -1; + } + /* Aligned to 2-byte boundary. */ + if (vol->major_ver < 3 || (vol->major_ver == 3 && !vol->minor_ver)) + mrec->usa_ofs = cpu_to_le16((sizeof(MFT_RECORD_OLD) + 1) & ~1); + else { + /* Abort if mref is > 32 bits. */ + if (MREF(mref) & 0x0000ffff00000000ull) { + errno = ERANGE; + ntfs_log_perror("Mft reference exceeds 32 bits"); + return -1; + } + mrec->usa_ofs = cpu_to_le16((sizeof(MFT_RECORD) + 1) & ~1); + /* + * Set the NTFS 3.1+ specific fields while we know that the + * volume version is 3.1+. + */ + mrec->reserved = cpu_to_le16(0); + mrec->mft_record_number = cpu_to_le32(MREF(mref)); + } + mrec->magic = magic_FILE; + if (vol->mft_record_size >= NTFS_BLOCK_SIZE) + mrec->usa_count = cpu_to_le16(vol->mft_record_size / + NTFS_BLOCK_SIZE + 1); + else { + mrec->usa_count = cpu_to_le16(1); + ntfs_log_error("Sector size is bigger than MFT record size. " + "Setting usa_count to 1. If Windows chkdsk " + "reports this as corruption, please email %s " + "stating that you saw this message and that " + "the file system created was corrupt. " + "Thank you.\n", NTFS_DEV_LIST); + } + /* Set the update sequence number to 1. */ + *(u16*)((u8*)mrec + le16_to_cpu(mrec->usa_ofs)) = cpu_to_le16(1); + mrec->lsn = cpu_to_le64(0ull); + mrec->sequence_number = cpu_to_le16(1); + mrec->link_count = cpu_to_le16(0); + /* Aligned to 8-byte boundary. */ + mrec->attrs_offset = cpu_to_le16((le16_to_cpu(mrec->usa_ofs) + + (le16_to_cpu(mrec->usa_count) << 1) + 7) & ~7); + mrec->flags = cpu_to_le16(0); + /* + * Using attrs_offset plus eight bytes (for the termination attribute), + * aligned to 8-byte boundary. + */ + mrec->bytes_in_use = cpu_to_le32((le16_to_cpu(mrec->attrs_offset) + 8 + + 7) & ~7); + mrec->bytes_allocated = cpu_to_le32(vol->mft_record_size); + mrec->base_mft_record = cpu_to_le64((MFT_REF)0); + mrec->next_attr_instance = cpu_to_le16(0); + a = (ATTR_RECORD*)((u8*)mrec + le16_to_cpu(mrec->attrs_offset)); + a->type = AT_END; + a->length = cpu_to_le32(0); + /* Finally, clear the unused part of the mft record. */ + memset((u8*)a + 8, 0, vol->mft_record_size - ((u8*)a + 8 - (u8*)mrec)); + return 0; } /** * ntfs_mft_record_format - format an mft record on an ntfs volume - * @vol: volume on which to format the mft record - * @mref: mft reference specifying mft record to format + * @vol: volume on which to format the mft record + * @mref: mft reference specifying mft record to format * * Format the mft record with the mft reference @mref in $MFT/$DATA, i.e. lay * out an empty, unused mft record in memory and write it to the volume @vol. * * On success return 0 and on error return -1 with errno set to the error code. */ -int ntfs_mft_record_format( const ntfs_volume *vol, const MFT_REF mref ) +int ntfs_mft_record_format(const ntfs_volume *vol, const MFT_REF mref) { - MFT_RECORD *m; - int ret = -1; + MFT_RECORD *m; + int ret = -1; - ntfs_log_enter( "Entering\n" ); - - m = ntfs_calloc( vol->mft_record_size ); - if ( !m ) - goto out; - - if ( ntfs_mft_record_layout( vol, mref, m ) ) - goto free_m; - - if ( ntfs_mft_record_write( vol, mref, m ) ) - goto free_m; - - ret = 0; + ntfs_log_enter("Entering\n"); + + m = ntfs_calloc(vol->mft_record_size); + if (!m) + goto out; + + if (ntfs_mft_record_layout(vol, mref, m)) + goto free_m; + + if (ntfs_mft_record_write(vol, mref, m)) + goto free_m; + + ret = 0; free_m: - free( m ); -out: - ntfs_log_leave( "\n" ); - return ret; + free(m); +out: + ntfs_log_leave("\n"); + return ret; } static const char *es = " Leaving inconsistent metadata. Run chkdsk."; @@ -459,16 +439,16 @@ static const char *es = " Leaving inconsistent metadata. Run chkdsk."; * * Returns: */ -static inline unsigned int ntfs_ffz( unsigned int word ) +static inline unsigned int ntfs_ffz(unsigned int word) { - return ffs( ~word ) - 1; + return ffs(~word) - 1; } -static int ntfs_is_mft( ntfs_inode *ni ) +static int ntfs_is_mft(ntfs_inode *ni) { - if ( ni && ni->mft_no == FILE_MFT ) - return 1; - return 0; + if (ni && ni->mft_no == FILE_MFT) + return 1; + return 0; } #ifndef PAGE_SIZE @@ -479,8 +459,8 @@ static int ntfs_is_mft( ntfs_inode *ni ) /** * ntfs_mft_bitmap_find_free_rec - find a free mft record in the mft bitmap - * @vol: volume on which to search for a free mft record - * @base_ni: open base inode if allocating an extent mft record or NULL + * @vol: volume on which to search for a free mft record + * @base_ni: open base inode if allocating an extent mft record or NULL * * Search for a free mft record in the mft bitmap attribute on the ntfs volume * @vol. @@ -494,396 +474,370 @@ static int ntfs_is_mft( ntfs_inode *ni ) * error code. An error code of ENOSPC means that there are no free mft * records in the currently initialized mft bitmap. */ -static int ntfs_mft_bitmap_find_free_rec( ntfs_volume *vol, ntfs_inode *base_ni ) +static int ntfs_mft_bitmap_find_free_rec(ntfs_volume *vol, ntfs_inode *base_ni) { - s64 pass_end, ll, data_pos, pass_start, ofs, bit; - ntfs_attr *mftbmp_na; - u8 *buf, *byte; - unsigned int size; - u8 pass, b; - int ret = -1; + s64 pass_end, ll, data_pos, pass_start, ofs, bit; + ntfs_attr *mftbmp_na; + u8 *buf, *byte; + unsigned int size; + u8 pass, b; + int ret = -1; - ntfs_log_enter( "Entering\n" ); - - mftbmp_na = vol->mftbmp_na; - /* - * Set the end of the pass making sure we do not overflow the mft - * bitmap. - */ - size = PAGE_SIZE; - pass_end = vol->mft_na->allocated_size >> vol->mft_record_size_bits; - ll = mftbmp_na->initialized_size << 3; - if ( pass_end > ll ) - pass_end = ll; - pass = 1; - if ( !base_ni ) - data_pos = vol->mft_data_pos; - else - data_pos = base_ni->mft_no + 1; - if ( data_pos < RESERVED_MFT_RECORDS ) - data_pos = RESERVED_MFT_RECORDS; - if ( data_pos >= pass_end ) - { - data_pos = RESERVED_MFT_RECORDS; - pass = 2; - /* This happens on a freshly formatted volume. */ - if ( data_pos >= pass_end ) - { - errno = ENOSPC; - goto leave; - } - } - if ( ntfs_is_mft( base_ni ) ) - { - data_pos = 0; - pass = 2; - } - pass_start = data_pos; - buf = ntfs_malloc( PAGE_SIZE ); - if ( !buf ) - goto leave; - - ntfs_log_debug( "Starting bitmap search: pass %u, pass_start 0x%llx, " - "pass_end 0x%llx, data_pos 0x%llx.\n", pass, - ( long long )pass_start, ( long long )pass_end, - ( long long )data_pos ); + ntfs_log_enter("Entering\n"); + + mftbmp_na = vol->mftbmp_na; + /* + * Set the end of the pass making sure we do not overflow the mft + * bitmap. + */ + size = PAGE_SIZE; + pass_end = vol->mft_na->allocated_size >> vol->mft_record_size_bits; + ll = mftbmp_na->initialized_size << 3; + if (pass_end > ll) + pass_end = ll; + pass = 1; + if (!base_ni) + data_pos = vol->mft_data_pos; + else + data_pos = base_ni->mft_no + 1; + if (data_pos < RESERVED_MFT_RECORDS) + data_pos = RESERVED_MFT_RECORDS; + if (data_pos >= pass_end) { + data_pos = RESERVED_MFT_RECORDS; + pass = 2; + /* This happens on a freshly formatted volume. */ + if (data_pos >= pass_end) { + errno = ENOSPC; + goto leave; + } + } + if (ntfs_is_mft(base_ni)) { + data_pos = 0; + pass = 2; + } + pass_start = data_pos; + buf = ntfs_malloc(PAGE_SIZE); + if (!buf) + goto leave; + + ntfs_log_debug("Starting bitmap search: pass %u, pass_start 0x%llx, " + "pass_end 0x%llx, data_pos 0x%llx.\n", pass, + (long long)pass_start, (long long)pass_end, + (long long)data_pos); #ifdef DEBUG - byte = NULL; - b = 0; + byte = NULL; + b = 0; #endif - /* Loop until a free mft record is found. */ - for ( ; pass <= 2; size = PAGE_SIZE ) - { - /* Cap size to pass_end. */ - ofs = data_pos >> 3; - ll = ( ( pass_end + 7 ) >> 3 ) - ofs; - if ( size > ll ) - size = ll; - ll = ntfs_attr_pread( mftbmp_na, ofs, size, buf ); - if ( ll < 0 ) - { - ntfs_log_perror( "Failed to read $MFT bitmap" ); - free( buf ); - goto leave; - } - ntfs_log_debug( "Read 0x%llx bytes.\n", ( long long )ll ); - /* If we read at least one byte, search @buf for a zero bit. */ - if ( ll ) - { - size = ll << 3; - bit = data_pos & 7; - data_pos &= ~7ull; - ntfs_log_debug( "Before inner for loop: size 0x%x, " - "data_pos 0x%llx, bit 0x%llx, " - "*byte 0x%hhx, b %u.\n", size, - ( long long )data_pos, ( long long )bit, - byte ? *byte : -1, b ); - for ( ; bit < size && data_pos + bit < pass_end; - bit &= ~7ull, bit += 8 ) - { - /* - * If we're extending $MFT and running out of the first - * mft record (base record) then give up searching since - * no guarantee that the found record will be accessible. - */ - if ( ntfs_is_mft( base_ni ) && bit > 400 ) - goto out; - - byte = buf + ( bit >> 3 ); - if ( *byte == 0xff ) - continue; - - /* Note: ffz() result must be zero based. */ - b = ntfs_ffz( ( unsigned long ) * byte ); - if ( b < 8 && b >= ( bit & 7 ) ) - { - free( buf ); - ret = data_pos + ( bit & ~7ull ) + b; - goto leave; - } - } - ntfs_log_debug( "After inner for loop: size 0x%x, " - "data_pos 0x%llx, bit 0x%llx, " - "*byte 0x%hhx, b %u.\n", size, - ( long long )data_pos, ( long long )bit, - byte ? *byte : -1, b ); - data_pos += size; - /* - * If the end of the pass has not been reached yet, - * continue searching the mft bitmap for a zero bit. - */ - if ( data_pos < pass_end ) - continue; - } - /* Do the next pass. */ - pass++; - if ( pass == 2 ) - { - /* - * Starting the second pass, in which we scan the first - * part of the zone which we omitted earlier. - */ - pass_end = pass_start; - data_pos = pass_start = RESERVED_MFT_RECORDS; - ntfs_log_debug( "pass %i, pass_start 0x%llx, pass_end " - "0x%llx.\n", pass, ( long long )pass_start, - ( long long )pass_end ); - if ( data_pos >= pass_end ) - break; - } - } - /* No free mft records in currently initialized mft bitmap. */ -out: - free( buf ); - errno = ENOSPC; + /* Loop until a free mft record is found. */ + for (; pass <= 2; size = PAGE_SIZE) { + /* Cap size to pass_end. */ + ofs = data_pos >> 3; + ll = ((pass_end + 7) >> 3) - ofs; + if (size > ll) + size = ll; + ll = ntfs_attr_pread(mftbmp_na, ofs, size, buf); + if (ll < 0) { + ntfs_log_perror("Failed to read $MFT bitmap"); + free(buf); + goto leave; + } + ntfs_log_debug("Read 0x%llx bytes.\n", (long long)ll); + /* If we read at least one byte, search @buf for a zero bit. */ + if (ll) { + size = ll << 3; + bit = data_pos & 7; + data_pos &= ~7ull; + ntfs_log_debug("Before inner for loop: size 0x%x, " + "data_pos 0x%llx, bit 0x%llx, " + "*byte 0x%hhx, b %u.\n", size, + (long long)data_pos, (long long)bit, + byte ? *byte : -1, b); + for (; bit < size && data_pos + bit < pass_end; + bit &= ~7ull, bit += 8) { + /* + * If we're extending $MFT and running out of the first + * mft record (base record) then give up searching since + * no guarantee that the found record will be accessible. + */ + if (ntfs_is_mft(base_ni) && bit > 400) + goto out; + + byte = buf + (bit >> 3); + if (*byte == 0xff) + continue; + + /* Note: ffz() result must be zero based. */ + b = ntfs_ffz((unsigned long)*byte); + if (b < 8 && b >= (bit & 7)) { + free(buf); + ret = data_pos + (bit & ~7ull) + b; + goto leave; + } + } + ntfs_log_debug("After inner for loop: size 0x%x, " + "data_pos 0x%llx, bit 0x%llx, " + "*byte 0x%hhx, b %u.\n", size, + (long long)data_pos, (long long)bit, + byte ? *byte : -1, b); + data_pos += size; + /* + * If the end of the pass has not been reached yet, + * continue searching the mft bitmap for a zero bit. + */ + if (data_pos < pass_end) + continue; + } + /* Do the next pass. */ + pass++; + if (pass == 2) { + /* + * Starting the second pass, in which we scan the first + * part of the zone which we omitted earlier. + */ + pass_end = pass_start; + data_pos = pass_start = RESERVED_MFT_RECORDS; + ntfs_log_debug("pass %i, pass_start 0x%llx, pass_end " + "0x%llx.\n", pass, (long long)pass_start, + (long long)pass_end); + if (data_pos >= pass_end) + break; + } + } + /* No free mft records in currently initialized mft bitmap. */ +out: + free(buf); + errno = ENOSPC; leave: - ntfs_log_leave( "\n" ); - return ret; + ntfs_log_leave("\n"); + return ret; } -static int ntfs_mft_attr_extend( ntfs_attr *na ) +static int ntfs_mft_attr_extend(ntfs_attr *na) { - int ret = STATUS_ERROR; - ntfs_log_enter( "Entering\n" ); + int ret = STATUS_ERROR; + ntfs_log_enter("Entering\n"); - if ( !NInoAttrList( na->ni ) ) - { - if ( ntfs_inode_add_attrlist( na->ni ) ) - { - ntfs_log_perror( "%s: Can not add attrlist #3", __FUNCTION__ ); - goto out; - } - /* We can't sync the $MFT inode since its runlist is bogus. */ - ret = STATUS_KEEP_SEARCHING; - goto out; - } + if (!NInoAttrList(na->ni)) { + if (ntfs_inode_add_attrlist(na->ni)) { + ntfs_log_perror("%s: Can not add attrlist #3", __FUNCTION__); + goto out; + } + /* We can't sync the $MFT inode since its runlist is bogus. */ + ret = STATUS_KEEP_SEARCHING; + goto out; + } - if ( ntfs_attr_update_mapping_pairs( na, 0 ) ) - { - ntfs_log_perror( "%s: MP update failed", __FUNCTION__ ); - goto out; - } - - ret = STATUS_OK; -out: - ntfs_log_leave( "\n" ); - return ret; + if (ntfs_attr_update_mapping_pairs(na, 0)) { + ntfs_log_perror("%s: MP update failed", __FUNCTION__); + goto out; + } + + ret = STATUS_OK; +out: + ntfs_log_leave("\n"); + return ret; } /** * ntfs_mft_bitmap_extend_allocation_i - see ntfs_mft_bitmap_extend_allocation */ -static int ntfs_mft_bitmap_extend_allocation_i( ntfs_volume *vol ) +static int ntfs_mft_bitmap_extend_allocation_i(ntfs_volume *vol) { - LCN lcn; - s64 ll = 0; /* silence compiler warning */ - ntfs_attr *mftbmp_na; - runlist_element *rl, *rl2 = NULL; /* silence compiler warning */ - ntfs_attr_search_ctx *ctx; - MFT_RECORD *m = NULL; /* silence compiler warning */ - ATTR_RECORD *a = NULL; /* silence compiler warning */ - int err, mp_size; - int ret = STATUS_ERROR; - u32 old_alen = 0; /* silence compiler warning */ - BOOL mp_rebuilt = FALSE; - BOOL update_mp = FALSE; + LCN lcn; + s64 ll = 0; /* silence compiler warning */ + ntfs_attr *mftbmp_na; + runlist_element *rl, *rl2 = NULL; /* silence compiler warning */ + ntfs_attr_search_ctx *ctx; + MFT_RECORD *m = NULL; /* silence compiler warning */ + ATTR_RECORD *a = NULL; /* silence compiler warning */ + int err, mp_size; + int ret = STATUS_ERROR; + u32 old_alen = 0; /* silence compiler warning */ + BOOL mp_rebuilt = FALSE; + BOOL update_mp = FALSE; - mftbmp_na = vol->mftbmp_na; - /* - * Determine the last lcn of the mft bitmap. The allocated size of the - * mft bitmap cannot be zero so we are ok to do this. - */ - rl = ntfs_attr_find_vcn( mftbmp_na, ( mftbmp_na->allocated_size - 1 ) >> - vol->cluster_size_bits ); - if ( !rl || !rl->length || rl->lcn < 0 ) - { - ntfs_log_error( "Failed to determine last allocated " - "cluster of mft bitmap attribute.\n" ); - if ( rl ) - errno = EIO; - return STATUS_ERROR; - } - lcn = rl->lcn + rl->length; + mftbmp_na = vol->mftbmp_na; + /* + * Determine the last lcn of the mft bitmap. The allocated size of the + * mft bitmap cannot be zero so we are ok to do this. + */ + rl = ntfs_attr_find_vcn(mftbmp_na, (mftbmp_na->allocated_size - 1) >> + vol->cluster_size_bits); + if (!rl || !rl->length || rl->lcn < 0) { + ntfs_log_error("Failed to determine last allocated " + "cluster of mft bitmap attribute.\n"); + if (rl) + errno = EIO; + return STATUS_ERROR; + } + lcn = rl->lcn + rl->length; + + rl2 = ntfs_cluster_alloc(vol, rl[1].vcn, 1, lcn, DATA_ZONE); + if (!rl2) { + ntfs_log_error("Failed to allocate a cluster for " + "the mft bitmap.\n"); + return STATUS_ERROR; + } + rl = ntfs_runlists_merge(mftbmp_na->rl, rl2); + if (!rl) { + err = errno; + ntfs_log_error("Failed to merge runlists for mft " + "bitmap.\n"); + if (ntfs_cluster_free_from_rl(vol, rl2)) + ntfs_log_error("Failed to deallocate " + "cluster.%s\n", es); + free(rl2); + errno = err; + return STATUS_ERROR; + } + mftbmp_na->rl = rl; + ntfs_log_debug("Adding one run to mft bitmap.\n"); + /* Find the last run in the new runlist. */ + for (; rl[1].length; rl++) + ; + /* + * Update the attribute record as well. Note: @rl is the last + * (non-terminator) runlist element of mft bitmap. + */ + ctx = ntfs_attr_get_search_ctx(mftbmp_na->ni, NULL); + if (!ctx) + goto undo_alloc; - rl2 = ntfs_cluster_alloc( vol, rl[1].vcn, 1, lcn, DATA_ZONE ); - if ( !rl2 ) - { - ntfs_log_error( "Failed to allocate a cluster for " - "the mft bitmap.\n" ); - return STATUS_ERROR; - } - rl = ntfs_runlists_merge( mftbmp_na->rl, rl2 ); - if ( !rl ) - { - err = errno; - ntfs_log_error( "Failed to merge runlists for mft " - "bitmap.\n" ); - if ( ntfs_cluster_free_from_rl( vol, rl2 ) ) - ntfs_log_error( "Failed to deallocate " - "cluster.%s\n", es ); - free( rl2 ); - errno = err; - return STATUS_ERROR; - } - mftbmp_na->rl = rl; - ntfs_log_debug( "Adding one run to mft bitmap.\n" ); - /* Find the last run in the new runlist. */ - for ( ; rl[1].length; rl++ ) - ; - /* - * Update the attribute record as well. Note: @rl is the last - * (non-terminator) runlist element of mft bitmap. - */ - ctx = ntfs_attr_get_search_ctx( mftbmp_na->ni, NULL ); - if ( !ctx ) - goto undo_alloc; - - if ( ntfs_attr_lookup( mftbmp_na->type, mftbmp_na->name, - mftbmp_na->name_len, 0, rl[1].vcn, NULL, 0, ctx ) ) - { - ntfs_log_error( "Failed to find last attribute extent of " - "mft bitmap attribute.\n" ); - goto undo_alloc; - } - m = ctx->mrec; - a = ctx->attr; - ll = sle64_to_cpu( a->lowest_vcn ); - rl2 = ntfs_attr_find_vcn( mftbmp_na, ll ); - if ( !rl2 || !rl2->length ) - { - ntfs_log_error( "Failed to determine previous last " - "allocated cluster of mft bitmap attribute.\n" ); - if ( rl2 ) - errno = EIO; - goto undo_alloc; - } - /* Get the size for the new mapping pairs array for this extent. */ - mp_size = ntfs_get_size_for_mapping_pairs( vol, rl2, ll, INT_MAX ); - if ( mp_size <= 0 ) - { - ntfs_log_error( "Get size for mapping pairs failed for " - "mft bitmap attribute extent.\n" ); - goto undo_alloc; - } - /* Expand the attribute record if necessary. */ - old_alen = le32_to_cpu( a->length ); - if ( ntfs_attr_record_resize( m, a, mp_size + - le16_to_cpu( a->mapping_pairs_offset ) ) ) - { - ntfs_log_info( "extending $MFT bitmap\n" ); - ret = ntfs_mft_attr_extend( vol->mftbmp_na ); - if ( ret == STATUS_OK ) - goto ok; - if ( ret == STATUS_ERROR ) - { - ntfs_log_perror( "%s: ntfs_mft_attr_extend failed", __FUNCTION__ ); - update_mp = TRUE; - } - goto undo_alloc; - } - mp_rebuilt = TRUE; - /* Generate the mapping pairs array directly into the attr record. */ - if ( ntfs_mapping_pairs_build( vol, ( u8* )a + - le16_to_cpu( a->mapping_pairs_offset ), mp_size, rl2, ll, - NULL ) ) - { - ntfs_log_error( "Failed to build mapping pairs array for " - "mft bitmap attribute.\n" ); - errno = EIO; - goto undo_alloc; - } - /* Update the highest_vcn. */ - a->highest_vcn = cpu_to_sle64( rl[1].vcn - 1 ); - /* - * We now have extended the mft bitmap allocated_size by one cluster. - * Reflect this in the ntfs_attr structure and the attribute record. - */ - if ( a->lowest_vcn ) - { - /* - * We are not in the first attribute extent, switch to it, but - * first ensure the changes will make it to disk later. - */ - ntfs_inode_mark_dirty( ctx->ntfs_ino ); - ntfs_attr_reinit_search_ctx( ctx ); - if ( ntfs_attr_lookup( mftbmp_na->type, mftbmp_na->name, - mftbmp_na->name_len, 0, 0, NULL, 0, ctx ) ) - { - ntfs_log_error( "Failed to find first attribute " - "extent of mft bitmap attribute.\n" ); - goto restore_undo_alloc; - } - a = ctx->attr; - } + if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name, + mftbmp_na->name_len, 0, rl[1].vcn, NULL, 0, ctx)) { + ntfs_log_error("Failed to find last attribute extent of " + "mft bitmap attribute.\n"); + goto undo_alloc; + } + m = ctx->mrec; + a = ctx->attr; + ll = sle64_to_cpu(a->lowest_vcn); + rl2 = ntfs_attr_find_vcn(mftbmp_na, ll); + if (!rl2 || !rl2->length) { + ntfs_log_error("Failed to determine previous last " + "allocated cluster of mft bitmap attribute.\n"); + if (rl2) + errno = EIO; + goto undo_alloc; + } + /* Get the size for the new mapping pairs array for this extent. */ + mp_size = ntfs_get_size_for_mapping_pairs(vol, rl2, ll, INT_MAX); + if (mp_size <= 0) { + ntfs_log_error("Get size for mapping pairs failed for " + "mft bitmap attribute extent.\n"); + goto undo_alloc; + } + /* Expand the attribute record if necessary. */ + old_alen = le32_to_cpu(a->length); + if (ntfs_attr_record_resize(m, a, mp_size + + le16_to_cpu(a->mapping_pairs_offset))) { + ntfs_log_info("extending $MFT bitmap\n"); + ret = ntfs_mft_attr_extend(vol->mftbmp_na); + if (ret == STATUS_OK) + goto ok; + if (ret == STATUS_ERROR) { + ntfs_log_perror("%s: ntfs_mft_attr_extend failed", __FUNCTION__); + update_mp = TRUE; + } + goto undo_alloc; + } + mp_rebuilt = TRUE; + /* Generate the mapping pairs array directly into the attr record. */ + if (ntfs_mapping_pairs_build(vol, (u8*)a + + le16_to_cpu(a->mapping_pairs_offset), mp_size, rl2, ll, + NULL)) { + ntfs_log_error("Failed to build mapping pairs array for " + "mft bitmap attribute.\n"); + errno = EIO; + goto undo_alloc; + } + /* Update the highest_vcn. */ + a->highest_vcn = cpu_to_sle64(rl[1].vcn - 1); + /* + * We now have extended the mft bitmap allocated_size by one cluster. + * Reflect this in the ntfs_attr structure and the attribute record. + */ + if (a->lowest_vcn) { + /* + * We are not in the first attribute extent, switch to it, but + * first ensure the changes will make it to disk later. + */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name, + mftbmp_na->name_len, 0, 0, NULL, 0, ctx)) { + ntfs_log_error("Failed to find first attribute " + "extent of mft bitmap attribute.\n"); + goto restore_undo_alloc; + } + a = ctx->attr; + } ok: - mftbmp_na->allocated_size += vol->cluster_size; - a->allocated_size = cpu_to_sle64( mftbmp_na->allocated_size ); - /* Ensure the changes make it to disk. */ - ntfs_inode_mark_dirty( ctx->ntfs_ino ); - ntfs_attr_put_search_ctx( ctx ); - return STATUS_OK; + mftbmp_na->allocated_size += vol->cluster_size; + a->allocated_size = cpu_to_sle64(mftbmp_na->allocated_size); + /* Ensure the changes make it to disk. */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + return STATUS_OK; restore_undo_alloc: - err = errno; - ntfs_attr_reinit_search_ctx( ctx ); - if ( ntfs_attr_lookup( mftbmp_na->type, mftbmp_na->name, - mftbmp_na->name_len, 0, rl[1].vcn, NULL, 0, ctx ) ) - { - ntfs_log_error( "Failed to find last attribute extent of " - "mft bitmap attribute.%s\n", es ); - ntfs_attr_put_search_ctx( ctx ); - mftbmp_na->allocated_size += vol->cluster_size; - /* - * The only thing that is now wrong is ->allocated_size of the - * base attribute extent which chkdsk should be able to fix. - */ - errno = err; - return STATUS_ERROR; - } - m = ctx->mrec; - a = ctx->attr; - a->highest_vcn = cpu_to_sle64( rl[1].vcn - 2 ); - errno = err; + err = errno; + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name, + mftbmp_na->name_len, 0, rl[1].vcn, NULL, 0, ctx)) { + ntfs_log_error("Failed to find last attribute extent of " + "mft bitmap attribute.%s\n", es); + ntfs_attr_put_search_ctx(ctx); + mftbmp_na->allocated_size += vol->cluster_size; + /* + * The only thing that is now wrong is ->allocated_size of the + * base attribute extent which chkdsk should be able to fix. + */ + errno = err; + return STATUS_ERROR; + } + m = ctx->mrec; + a = ctx->attr; + a->highest_vcn = cpu_to_sle64(rl[1].vcn - 2); + errno = err; undo_alloc: - err = errno; + err = errno; - /* Remove the last run from the runlist. */ - lcn = rl->lcn; - rl->lcn = rl[1].lcn; - rl->length = 0; - - /* FIXME: use an ntfs_cluster_free_* function */ - if ( ntfs_bitmap_clear_bit( vol->lcnbmp_na, lcn ) ) - ntfs_log_error( "Failed to free cluster.%s\n", es ); - else - vol->free_clusters++; - if ( mp_rebuilt ) - { - if ( ntfs_mapping_pairs_build( vol, ( u8* )a + - le16_to_cpu( a->mapping_pairs_offset ), - old_alen - le16_to_cpu( a->mapping_pairs_offset ), - rl2, ll, NULL ) ) - ntfs_log_error( "Failed to restore mapping " - "pairs array.%s\n", es ); - if ( ntfs_attr_record_resize( m, a, old_alen ) ) - ntfs_log_error( "Failed to restore attribute " - "record.%s\n", es ); - ntfs_inode_mark_dirty( ctx->ntfs_ino ); - } - if ( update_mp ) - { - if ( ntfs_attr_update_mapping_pairs( vol->mftbmp_na, 0 ) ) - ntfs_log_perror( "%s: MP update failed", __FUNCTION__ ); - } - if ( ctx ) - ntfs_attr_put_search_ctx( ctx ); - errno = err; - return ret; + /* Remove the last run from the runlist. */ + lcn = rl->lcn; + rl->lcn = rl[1].lcn; + rl->length = 0; + + /* FIXME: use an ntfs_cluster_free_* function */ + if (ntfs_bitmap_clear_bit(vol->lcnbmp_na, lcn)) + ntfs_log_error("Failed to free cluster.%s\n", es); + else + vol->free_clusters++; + if (mp_rebuilt) { + if (ntfs_mapping_pairs_build(vol, (u8*)a + + le16_to_cpu(a->mapping_pairs_offset), + old_alen - le16_to_cpu(a->mapping_pairs_offset), + rl2, ll, NULL)) + ntfs_log_error("Failed to restore mapping " + "pairs array.%s\n", es); + if (ntfs_attr_record_resize(m, a, old_alen)) + ntfs_log_error("Failed to restore attribute " + "record.%s\n", es); + ntfs_inode_mark_dirty(ctx->ntfs_ino); + } + if (update_mp) { + if (ntfs_attr_update_mapping_pairs(vol->mftbmp_na, 0)) + ntfs_log_perror("%s: MP update failed", __FUNCTION__); + } + if (ctx) + ntfs_attr_put_search_ctx(ctx); + errno = err; + return ret; } /** * ntfs_mft_bitmap_extend_allocation - extend mft bitmap attribute by a cluster - * @vol: volume on which to extend the mft bitmap attribute + * @vol: volume on which to extend the mft bitmap attribute * * Extend the mft bitmap attribute on the ntfs volume @vol by one cluster. * @@ -892,18 +846,18 @@ undo_alloc: * * Return 0 on success and -1 on error with errno set to the error code. */ -static int ntfs_mft_bitmap_extend_allocation( ntfs_volume *vol ) +static int ntfs_mft_bitmap_extend_allocation(ntfs_volume *vol) { - int ret; - - ntfs_log_enter( "Entering\n" ); - ret = ntfs_mft_bitmap_extend_allocation_i( vol ); - ntfs_log_leave( "\n" ); - return ret; + int ret; + + ntfs_log_enter("Entering\n"); + ret = ntfs_mft_bitmap_extend_allocation_i(vol); + ntfs_log_leave("\n"); + return ret; } /** * ntfs_mft_bitmap_extend_initialized - extend mft bitmap initialized data - * @vol: volume on which to extend the mft bitmap attribute + * @vol: volume on which to extend the mft bitmap attribute * * Extend the initialized portion of the mft bitmap attribute on the ntfs * volume @vol by 8 bytes. @@ -913,96 +867,91 @@ static int ntfs_mft_bitmap_extend_allocation( ntfs_volume *vol ) * * Return 0 on success and -1 on error with errno set to the error code. */ -static int ntfs_mft_bitmap_extend_initialized( ntfs_volume *vol ) +static int ntfs_mft_bitmap_extend_initialized(ntfs_volume *vol) { - s64 old_data_size, old_initialized_size, ll; - ntfs_attr *mftbmp_na; - ntfs_attr_search_ctx *ctx; - ATTR_RECORD *a; - int err; - int ret = -1; + s64 old_data_size, old_initialized_size, ll; + ntfs_attr *mftbmp_na; + ntfs_attr_search_ctx *ctx; + ATTR_RECORD *a; + int err; + int ret = -1; - ntfs_log_enter( "Entering\n" ); + ntfs_log_enter("Entering\n"); + + mftbmp_na = vol->mftbmp_na; + ctx = ntfs_attr_get_search_ctx(mftbmp_na->ni, NULL); + if (!ctx) + goto out; - mftbmp_na = vol->mftbmp_na; - ctx = ntfs_attr_get_search_ctx( mftbmp_na->ni, NULL ); - if ( !ctx ) - goto out; + if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name, + mftbmp_na->name_len, 0, 0, NULL, 0, ctx)) { + ntfs_log_error("Failed to find first attribute extent of " + "mft bitmap attribute.\n"); + err = errno; + goto put_err_out; + } + a = ctx->attr; + old_data_size = mftbmp_na->data_size; + old_initialized_size = mftbmp_na->initialized_size; + mftbmp_na->initialized_size += 8; + a->initialized_size = cpu_to_sle64(mftbmp_na->initialized_size); + if (mftbmp_na->initialized_size > mftbmp_na->data_size) { + mftbmp_na->data_size = mftbmp_na->initialized_size; + a->data_size = cpu_to_sle64(mftbmp_na->data_size); + } + /* Ensure the changes make it to disk. */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + /* Initialize the mft bitmap attribute value with zeroes. */ + ll = 0; + ll = ntfs_attr_pwrite(mftbmp_na, old_initialized_size, 8, &ll); + if (ll == 8) { + ntfs_log_debug("Wrote eight initialized bytes to mft bitmap.\n"); + vol->free_mft_records += (8 * 8); + ret = 0; + goto out; + } + ntfs_log_error("Failed to write to mft bitmap.\n"); + err = errno; + if (ll >= 0) + err = EIO; + /* Try to recover from the error. */ + ctx = ntfs_attr_get_search_ctx(mftbmp_na->ni, NULL); + if (!ctx) + goto err_out; - if ( ntfs_attr_lookup( mftbmp_na->type, mftbmp_na->name, - mftbmp_na->name_len, 0, 0, NULL, 0, ctx ) ) - { - ntfs_log_error( "Failed to find first attribute extent of " - "mft bitmap attribute.\n" ); - err = errno; - goto put_err_out; - } - a = ctx->attr; - old_data_size = mftbmp_na->data_size; - old_initialized_size = mftbmp_na->initialized_size; - mftbmp_na->initialized_size += 8; - a->initialized_size = cpu_to_sle64( mftbmp_na->initialized_size ); - if ( mftbmp_na->initialized_size > mftbmp_na->data_size ) - { - mftbmp_na->data_size = mftbmp_na->initialized_size; - a->data_size = cpu_to_sle64( mftbmp_na->data_size ); - } - /* Ensure the changes make it to disk. */ - ntfs_inode_mark_dirty( ctx->ntfs_ino ); - ntfs_attr_put_search_ctx( ctx ); - /* Initialize the mft bitmap attribute value with zeroes. */ - ll = 0; - ll = ntfs_attr_pwrite( mftbmp_na, old_initialized_size, 8, &ll ); - if ( ll == 8 ) - { - ntfs_log_debug( "Wrote eight initialized bytes to mft bitmap.\n" ); - vol->free_mft_records += ( 8 * 8 ); - ret = 0; - goto out; - } - ntfs_log_error( "Failed to write to mft bitmap.\n" ); - err = errno; - if ( ll >= 0 ) - err = EIO; - /* Try to recover from the error. */ - ctx = ntfs_attr_get_search_ctx( mftbmp_na->ni, NULL ); - if ( !ctx ) - goto err_out; - - if ( ntfs_attr_lookup( mftbmp_na->type, mftbmp_na->name, - mftbmp_na->name_len, 0, 0, NULL, 0, ctx ) ) - { - ntfs_log_error( "Failed to find first attribute extent of " - "mft bitmap attribute.%s\n", es ); + if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name, + mftbmp_na->name_len, 0, 0, NULL, 0, ctx)) { + ntfs_log_error("Failed to find first attribute extent of " + "mft bitmap attribute.%s\n", es); put_err_out: - ntfs_attr_put_search_ctx( ctx ); - goto err_out; - } - a = ctx->attr; - mftbmp_na->initialized_size = old_initialized_size; - a->initialized_size = cpu_to_sle64( old_initialized_size ); - if ( mftbmp_na->data_size != old_data_size ) - { - mftbmp_na->data_size = old_data_size; - a->data_size = cpu_to_sle64( old_data_size ); - } - ntfs_inode_mark_dirty( ctx->ntfs_ino ); - ntfs_attr_put_search_ctx( ctx ); - ntfs_log_debug( "Restored status of mftbmp: allocated_size 0x%llx, " - "data_size 0x%llx, initialized_size 0x%llx.\n", - ( long long )mftbmp_na->allocated_size, - ( long long )mftbmp_na->data_size, - ( long long )mftbmp_na->initialized_size ); + ntfs_attr_put_search_ctx(ctx); + goto err_out; + } + a = ctx->attr; + mftbmp_na->initialized_size = old_initialized_size; + a->initialized_size = cpu_to_sle64(old_initialized_size); + if (mftbmp_na->data_size != old_data_size) { + mftbmp_na->data_size = old_data_size; + a->data_size = cpu_to_sle64(old_data_size); + } + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + ntfs_log_debug("Restored status of mftbmp: allocated_size 0x%llx, " + "data_size 0x%llx, initialized_size 0x%llx.\n", + (long long)mftbmp_na->allocated_size, + (long long)mftbmp_na->data_size, + (long long)mftbmp_na->initialized_size); err_out: - errno = err; + errno = err; out: - ntfs_log_leave( "\n" ); - return ret; + ntfs_log_leave("\n"); + return ret; } /** * ntfs_mft_data_extend_allocation - extend mft data attribute - * @vol: volume on which to extend the mft data attribute + * @vol: volume on which to extend the mft data attribute * * Extend the mft data attribute on the ntfs volume @vol by 16 mft records * worth of clusters or if not enough space for this by one mft record worth @@ -1013,579 +962,549 @@ out: * * Return 0 on success and -1 on error with errno set to the error code. */ -static int ntfs_mft_data_extend_allocation( ntfs_volume *vol ) +static int ntfs_mft_data_extend_allocation(ntfs_volume *vol) { - LCN lcn; - VCN old_last_vcn; - s64 min_nr, nr, ll = 0; /* silence compiler warning */ - ntfs_attr *mft_na; - runlist_element *rl, *rl2; - ntfs_attr_search_ctx *ctx; - MFT_RECORD *m = NULL; /* silence compiler warning */ - ATTR_RECORD *a = NULL; /* silence compiler warning */ - int err, mp_size; - int ret = STATUS_ERROR; - u32 old_alen = 0; /* silence compiler warning */ - BOOL mp_rebuilt = FALSE; - BOOL update_mp = FALSE; + LCN lcn; + VCN old_last_vcn; + s64 min_nr, nr, ll = 0; /* silence compiler warning */ + ntfs_attr *mft_na; + runlist_element *rl, *rl2; + ntfs_attr_search_ctx *ctx; + MFT_RECORD *m = NULL; /* silence compiler warning */ + ATTR_RECORD *a = NULL; /* silence compiler warning */ + int err, mp_size; + int ret = STATUS_ERROR; + u32 old_alen = 0; /* silence compiler warning */ + BOOL mp_rebuilt = FALSE; + BOOL update_mp = FALSE; - ntfs_log_enter( "Extending mft data allocation.\n" ); + ntfs_log_enter("Extending mft data allocation.\n"); + + mft_na = vol->mft_na; + /* + * Determine the preferred allocation location, i.e. the last lcn of + * the mft data attribute. The allocated size of the mft data + * attribute cannot be zero so we are ok to do this. + */ + rl = ntfs_attr_find_vcn(mft_na, + (mft_na->allocated_size - 1) >> vol->cluster_size_bits); + + if (!rl || !rl->length || rl->lcn < 0) { + ntfs_log_error("Failed to determine last allocated " + "cluster of mft data attribute.\n"); + if (rl) + errno = EIO; + goto out; + } + + lcn = rl->lcn + rl->length; + ntfs_log_debug("Last lcn of mft data attribute is 0x%llx.\n", (long long)lcn); + /* Minimum allocation is one mft record worth of clusters. */ + min_nr = vol->mft_record_size >> vol->cluster_size_bits; + if (!min_nr) + min_nr = 1; + /* Want to allocate 16 mft records worth of clusters. */ + nr = vol->mft_record_size << 4 >> vol->cluster_size_bits; + if (!nr) + nr = min_nr; + + old_last_vcn = rl[1].vcn; + do { + rl2 = ntfs_cluster_alloc(vol, old_last_vcn, nr, lcn, MFT_ZONE); + if (rl2) + break; + if (errno != ENOSPC || nr == min_nr) { + ntfs_log_perror("Failed to allocate (%lld) clusters " + "for $MFT", (long long)nr); + goto out; + } + /* + * There is not enough space to do the allocation, but there + * might be enough space to do a minimal allocation so try that + * before failing. + */ + nr = min_nr; + ntfs_log_debug("Retrying mft data allocation with minimal cluster " + "count %lli.\n", (long long)nr); + } while (1); + + ntfs_log_debug("Allocated %lld clusters.\n", (long long)nr); + + rl = ntfs_runlists_merge(mft_na->rl, rl2); + if (!rl) { + err = errno; + ntfs_log_error("Failed to merge runlists for mft data " + "attribute.\n"); + if (ntfs_cluster_free_from_rl(vol, rl2)) + ntfs_log_error("Failed to deallocate clusters " + "from the mft data attribute.%s\n", es); + free(rl2); + errno = err; + goto out; + } + mft_na->rl = rl; + + /* Find the last run in the new runlist. */ + for (; rl[1].length; rl++) + ; + /* Update the attribute record as well. */ + ctx = ntfs_attr_get_search_ctx(mft_na->ni, NULL); + if (!ctx) + goto undo_alloc; - mft_na = vol->mft_na; - /* - * Determine the preferred allocation location, i.e. the last lcn of - * the mft data attribute. The allocated size of the mft data - * attribute cannot be zero so we are ok to do this. - */ - rl = ntfs_attr_find_vcn( mft_na, - ( mft_na->allocated_size - 1 ) >> vol->cluster_size_bits ); - - if ( !rl || !rl->length || rl->lcn < 0 ) - { - ntfs_log_error( "Failed to determine last allocated " - "cluster of mft data attribute.\n" ); - if ( rl ) - errno = EIO; - goto out; - } - - lcn = rl->lcn + rl->length; - ntfs_log_debug( "Last lcn of mft data attribute is 0x%llx.\n", ( long long )lcn ); - /* Minimum allocation is one mft record worth of clusters. */ - min_nr = vol->mft_record_size >> vol->cluster_size_bits; - if ( !min_nr ) - min_nr = 1; - /* Want to allocate 16 mft records worth of clusters. */ - nr = vol->mft_record_size << 4 >> vol->cluster_size_bits; - if ( !nr ) - nr = min_nr; - - old_last_vcn = rl[1].vcn; - do - { - rl2 = ntfs_cluster_alloc( vol, old_last_vcn, nr, lcn, MFT_ZONE ); - if ( rl2 ) - break; - if ( errno != ENOSPC || nr == min_nr ) - { - ntfs_log_perror( "Failed to allocate (%lld) clusters " - "for $MFT", ( long long )nr ); - goto out; - } - /* - * There is not enough space to do the allocation, but there - * might be enough space to do a minimal allocation so try that - * before failing. - */ - nr = min_nr; - ntfs_log_debug( "Retrying mft data allocation with minimal cluster " - "count %lli.\n", ( long long )nr ); - } - while ( 1 ); - - ntfs_log_debug( "Allocated %lld clusters.\n", ( long long )nr ); - - rl = ntfs_runlists_merge( mft_na->rl, rl2 ); - if ( !rl ) - { - err = errno; - ntfs_log_error( "Failed to merge runlists for mft data " - "attribute.\n" ); - if ( ntfs_cluster_free_from_rl( vol, rl2 ) ) - ntfs_log_error( "Failed to deallocate clusters " - "from the mft data attribute.%s\n", es ); - free( rl2 ); - errno = err; - goto out; - } - mft_na->rl = rl; - - /* Find the last run in the new runlist. */ - for ( ; rl[1].length; rl++ ) - ; - /* Update the attribute record as well. */ - ctx = ntfs_attr_get_search_ctx( mft_na->ni, NULL ); - if ( !ctx ) - goto undo_alloc; - - if ( ntfs_attr_lookup( mft_na->type, mft_na->name, mft_na->name_len, 0, - rl[1].vcn, NULL, 0, ctx ) ) - { - ntfs_log_error( "Failed to find last attribute extent of " - "mft data attribute.\n" ); - goto undo_alloc; - } - m = ctx->mrec; - a = ctx->attr; - ll = sle64_to_cpu( a->lowest_vcn ); - rl2 = ntfs_attr_find_vcn( mft_na, ll ); - if ( !rl2 || !rl2->length ) - { - ntfs_log_error( "Failed to determine previous last " - "allocated cluster of mft data attribute.\n" ); - if ( rl2 ) - errno = EIO; - goto undo_alloc; - } - /* Get the size for the new mapping pairs array for this extent. */ - mp_size = ntfs_get_size_for_mapping_pairs( vol, rl2, ll, INT_MAX ); - if ( mp_size <= 0 ) - { - ntfs_log_error( "Get size for mapping pairs failed for " - "mft data attribute extent.\n" ); - goto undo_alloc; - } - /* Expand the attribute record if necessary. */ - old_alen = le32_to_cpu( a->length ); - if ( ntfs_attr_record_resize( m, a, - mp_size + le16_to_cpu( a->mapping_pairs_offset ) ) ) - { - ret = ntfs_mft_attr_extend( vol->mft_na ); - if ( ret == STATUS_OK ) - goto ok; - if ( ret == STATUS_ERROR ) - { - ntfs_log_perror( "%s: ntfs_mft_attr_extend failed", __FUNCTION__ ); - update_mp = TRUE; - } - goto undo_alloc; - } - mp_rebuilt = TRUE; - /* - * Generate the mapping pairs array directly into the attribute record. - */ - if ( ntfs_mapping_pairs_build( vol, - ( u8* )a + le16_to_cpu( a->mapping_pairs_offset ), mp_size, - rl2, ll, NULL ) ) - { - ntfs_log_error( "Failed to build mapping pairs array of " - "mft data attribute.\n" ); - errno = EIO; - goto undo_alloc; - } - /* Update the highest_vcn. */ - a->highest_vcn = cpu_to_sle64( rl[1].vcn - 1 ); - /* - * We now have extended the mft data allocated_size by nr clusters. - * Reflect this in the ntfs_attr structure and the attribute record. - * @rl is the last (non-terminator) runlist element of mft data - * attribute. - */ - if ( a->lowest_vcn ) - { - /* - * We are not in the first attribute extent, switch to it, but - * first ensure the changes will make it to disk later. - */ - ntfs_inode_mark_dirty( ctx->ntfs_ino ); - ntfs_attr_reinit_search_ctx( ctx ); - if ( ntfs_attr_lookup( mft_na->type, mft_na->name, - mft_na->name_len, 0, 0, NULL, 0, ctx ) ) - { - ntfs_log_error( "Failed to find first attribute " - "extent of mft data attribute.\n" ); - goto restore_undo_alloc; - } - a = ctx->attr; - } + if (ntfs_attr_lookup(mft_na->type, mft_na->name, mft_na->name_len, 0, + rl[1].vcn, NULL, 0, ctx)) { + ntfs_log_error("Failed to find last attribute extent of " + "mft data attribute.\n"); + goto undo_alloc; + } + m = ctx->mrec; + a = ctx->attr; + ll = sle64_to_cpu(a->lowest_vcn); + rl2 = ntfs_attr_find_vcn(mft_na, ll); + if (!rl2 || !rl2->length) { + ntfs_log_error("Failed to determine previous last " + "allocated cluster of mft data attribute.\n"); + if (rl2) + errno = EIO; + goto undo_alloc; + } + /* Get the size for the new mapping pairs array for this extent. */ + mp_size = ntfs_get_size_for_mapping_pairs(vol, rl2, ll, INT_MAX); + if (mp_size <= 0) { + ntfs_log_error("Get size for mapping pairs failed for " + "mft data attribute extent.\n"); + goto undo_alloc; + } + /* Expand the attribute record if necessary. */ + old_alen = le32_to_cpu(a->length); + if (ntfs_attr_record_resize(m, a, + mp_size + le16_to_cpu(a->mapping_pairs_offset))) { + ret = ntfs_mft_attr_extend(vol->mft_na); + if (ret == STATUS_OK) + goto ok; + if (ret == STATUS_ERROR) { + ntfs_log_perror("%s: ntfs_mft_attr_extend failed", __FUNCTION__); + update_mp = TRUE; + } + goto undo_alloc; + } + mp_rebuilt = TRUE; + /* + * Generate the mapping pairs array directly into the attribute record. + */ + if (ntfs_mapping_pairs_build(vol, + (u8*)a + le16_to_cpu(a->mapping_pairs_offset), mp_size, + rl2, ll, NULL)) { + ntfs_log_error("Failed to build mapping pairs array of " + "mft data attribute.\n"); + errno = EIO; + goto undo_alloc; + } + /* Update the highest_vcn. */ + a->highest_vcn = cpu_to_sle64(rl[1].vcn - 1); + /* + * We now have extended the mft data allocated_size by nr clusters. + * Reflect this in the ntfs_attr structure and the attribute record. + * @rl is the last (non-terminator) runlist element of mft data + * attribute. + */ + if (a->lowest_vcn) { + /* + * We are not in the first attribute extent, switch to it, but + * first ensure the changes will make it to disk later. + */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_lookup(mft_na->type, mft_na->name, + mft_na->name_len, 0, 0, NULL, 0, ctx)) { + ntfs_log_error("Failed to find first attribute " + "extent of mft data attribute.\n"); + goto restore_undo_alloc; + } + a = ctx->attr; + } ok: - mft_na->allocated_size += nr << vol->cluster_size_bits; - a->allocated_size = cpu_to_sle64( mft_na->allocated_size ); - /* Ensure the changes make it to disk. */ - ntfs_inode_mark_dirty( ctx->ntfs_ino ); - ntfs_attr_put_search_ctx( ctx ); - ret = STATUS_OK; + mft_na->allocated_size += nr << vol->cluster_size_bits; + a->allocated_size = cpu_to_sle64(mft_na->allocated_size); + /* Ensure the changes make it to disk. */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + ret = STATUS_OK; out: - ntfs_log_leave( "\n" ); - return ret; + ntfs_log_leave("\n"); + return ret; restore_undo_alloc: - err = errno; - ntfs_attr_reinit_search_ctx( ctx ); - if ( ntfs_attr_lookup( mft_na->type, mft_na->name, mft_na->name_len, 0, - rl[1].vcn, NULL, 0, ctx ) ) - { - ntfs_log_error( "Failed to find last attribute extent of " - "mft data attribute.%s\n", es ); - ntfs_attr_put_search_ctx( ctx ); - mft_na->allocated_size += nr << vol->cluster_size_bits; - /* - * The only thing that is now wrong is ->allocated_size of the - * base attribute extent which chkdsk should be able to fix. - */ - errno = err; - ret = STATUS_ERROR; - goto out; - } - m = ctx->mrec; - a = ctx->attr; - a->highest_vcn = cpu_to_sle64( old_last_vcn - 1 ); - errno = err; + err = errno; + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_lookup(mft_na->type, mft_na->name, mft_na->name_len, 0, + rl[1].vcn, NULL, 0, ctx)) { + ntfs_log_error("Failed to find last attribute extent of " + "mft data attribute.%s\n", es); + ntfs_attr_put_search_ctx(ctx); + mft_na->allocated_size += nr << vol->cluster_size_bits; + /* + * The only thing that is now wrong is ->allocated_size of the + * base attribute extent which chkdsk should be able to fix. + */ + errno = err; + ret = STATUS_ERROR; + goto out; + } + m = ctx->mrec; + a = ctx->attr; + a->highest_vcn = cpu_to_sle64(old_last_vcn - 1); + errno = err; undo_alloc: - err = errno; - if ( ntfs_cluster_free( vol, mft_na, old_last_vcn, -1 ) < 0 ) - ntfs_log_error( "Failed to free clusters from mft data " - "attribute.%s\n", es ); - if ( ntfs_rl_truncate( &mft_na->rl, old_last_vcn ) ) - ntfs_log_error( "Failed to truncate mft data attribute " - "runlist.%s\n", es ); - if ( mp_rebuilt ) - { - if ( ntfs_mapping_pairs_build( vol, ( u8* )a + - le16_to_cpu( a->mapping_pairs_offset ), - old_alen - le16_to_cpu( a->mapping_pairs_offset ), - rl2, ll, NULL ) ) - ntfs_log_error( "Failed to restore mapping pairs " - "array.%s\n", es ); - if ( ntfs_attr_record_resize( m, a, old_alen ) ) - ntfs_log_error( "Failed to restore attribute " - "record.%s\n", es ); - ntfs_inode_mark_dirty( ctx->ntfs_ino ); - } - if ( update_mp ) - { - if ( ntfs_attr_update_mapping_pairs( vol->mft_na, 0 ) ) - ntfs_log_perror( "%s: MP update failed", __FUNCTION__ ); - } - if ( ctx ) - ntfs_attr_put_search_ctx( ctx ); - errno = err; - goto out; + err = errno; + if (ntfs_cluster_free(vol, mft_na, old_last_vcn, -1) < 0) + ntfs_log_error("Failed to free clusters from mft data " + "attribute.%s\n", es); + if (ntfs_rl_truncate(&mft_na->rl, old_last_vcn)) + ntfs_log_error("Failed to truncate mft data attribute " + "runlist.%s\n", es); + if (mp_rebuilt) { + if (ntfs_mapping_pairs_build(vol, (u8*)a + + le16_to_cpu(a->mapping_pairs_offset), + old_alen - le16_to_cpu(a->mapping_pairs_offset), + rl2, ll, NULL)) + ntfs_log_error("Failed to restore mapping pairs " + "array.%s\n", es); + if (ntfs_attr_record_resize(m, a, old_alen)) + ntfs_log_error("Failed to restore attribute " + "record.%s\n", es); + ntfs_inode_mark_dirty(ctx->ntfs_ino); + } + if (update_mp) { + if (ntfs_attr_update_mapping_pairs(vol->mft_na, 0)) + ntfs_log_perror("%s: MP update failed", __FUNCTION__); + } + if (ctx) + ntfs_attr_put_search_ctx(ctx); + errno = err; + goto out; } -static int ntfs_mft_record_init( ntfs_volume *vol, s64 size ) +static int ntfs_mft_record_init(ntfs_volume *vol, s64 size) { - int ret = -1; - ntfs_attr *mft_na, *mftbmp_na; - s64 old_data_initialized, old_data_size; - ntfs_attr_search_ctx *ctx; - - ntfs_log_enter( "Entering\n" ); - - /* NOTE: Caller must sanity check vol, vol->mft_na and vol->mftbmp_na */ - - mft_na = vol->mft_na; - mftbmp_na = vol->mftbmp_na; - - /* - * The mft record is outside the initialized data. Extend the mft data - * attribute until it covers the allocated record. The loop is only - * actually traversed more than once when a freshly formatted volume - * is first written to so it optimizes away nicely in the common case. - */ - ntfs_log_debug( "Status of mft data before extension: " - "allocated_size 0x%llx, data_size 0x%llx, " - "initialized_size 0x%llx.\n", - ( long long )mft_na->allocated_size, - ( long long )mft_na->data_size, - ( long long )mft_na->initialized_size ); - while ( size > mft_na->allocated_size ) - { - if ( ntfs_mft_data_extend_allocation( vol ) == STATUS_ERROR ) - goto out; - ntfs_log_debug( "Status of mft data after allocation extension: " - "allocated_size 0x%llx, data_size 0x%llx, " - "initialized_size 0x%llx.\n", - ( long long )mft_na->allocated_size, - ( long long )mft_na->data_size, - ( long long )mft_na->initialized_size ); - } - - old_data_initialized = mft_na->initialized_size; - old_data_size = mft_na->data_size; - - /* - * Extend mft data initialized size (and data size of course) to reach - * the allocated mft record, formatting the mft records along the way. - * Note: We only modify the ntfs_attr structure as that is all that is - * needed by ntfs_mft_record_format(). We will update the attribute - * record itself in one fell swoop later on. - */ - while ( size > mft_na->initialized_size ) - { - s64 ll2 = mft_na->initialized_size >> vol->mft_record_size_bits; - mft_na->initialized_size += vol->mft_record_size; - if ( mft_na->initialized_size > mft_na->data_size ) - mft_na->data_size = mft_na->initialized_size; - ntfs_log_debug( "Initializing mft record 0x%llx.\n", ( long long )ll2 ); - if ( ntfs_mft_record_format( vol, ll2 ) < 0 ) - { - ntfs_log_perror( "Failed to format mft record" ); - goto undo_data_init; - } - } - - /* Update the mft data attribute record to reflect the new sizes. */ - ctx = ntfs_attr_get_search_ctx( mft_na->ni, NULL ); - if ( !ctx ) - goto undo_data_init; - - if ( ntfs_attr_lookup( mft_na->type, mft_na->name, mft_na->name_len, 0, - 0, NULL, 0, ctx ) ) - { - ntfs_log_error( "Failed to find first attribute extent of " - "mft data attribute.\n" ); - ntfs_attr_put_search_ctx( ctx ); - goto undo_data_init; - } - ctx->attr->initialized_size = cpu_to_sle64( mft_na->initialized_size ); - ctx->attr->data_size = cpu_to_sle64( mft_na->data_size ); - ctx->attr->allocated_size = cpu_to_sle64( mft_na->allocated_size ); - - /* Ensure the changes make it to disk. */ - ntfs_inode_mark_dirty( ctx->ntfs_ino ); - ntfs_attr_put_search_ctx( ctx ); - ntfs_log_debug( "Status of mft data after mft record initialization: " - "allocated_size 0x%llx, data_size 0x%llx, " - "initialized_size 0x%llx.\n", - ( long long )mft_na->allocated_size, - ( long long )mft_na->data_size, - ( long long )mft_na->initialized_size ); - - /* Sanity checks. */ - if ( mft_na->data_size > mft_na->allocated_size || - mft_na->initialized_size > mft_na->data_size ) - NTFS_BUG( "mft_na sanity checks failed" ); - - /* Sync MFT to minimize data loss if there won't be clean unmount. */ - if ( ntfs_inode_sync( mft_na->ni ) ) - goto undo_data_init; - - ret = 0; -out: - ntfs_log_leave( "\n" ); - return ret; + int ret = -1; + ntfs_attr *mft_na, *mftbmp_na; + s64 old_data_initialized, old_data_size; + ntfs_attr_search_ctx *ctx; + + ntfs_log_enter("Entering\n"); + + /* NOTE: Caller must sanity check vol, vol->mft_na and vol->mftbmp_na */ + + mft_na = vol->mft_na; + mftbmp_na = vol->mftbmp_na; + + /* + * The mft record is outside the initialized data. Extend the mft data + * attribute until it covers the allocated record. The loop is only + * actually traversed more than once when a freshly formatted volume + * is first written to so it optimizes away nicely in the common case. + */ + ntfs_log_debug("Status of mft data before extension: " + "allocated_size 0x%llx, data_size 0x%llx, " + "initialized_size 0x%llx.\n", + (long long)mft_na->allocated_size, + (long long)mft_na->data_size, + (long long)mft_na->initialized_size); + while (size > mft_na->allocated_size) { + if (ntfs_mft_data_extend_allocation(vol) == STATUS_ERROR) + goto out; + ntfs_log_debug("Status of mft data after allocation extension: " + "allocated_size 0x%llx, data_size 0x%llx, " + "initialized_size 0x%llx.\n", + (long long)mft_na->allocated_size, + (long long)mft_na->data_size, + (long long)mft_na->initialized_size); + } + + old_data_initialized = mft_na->initialized_size; + old_data_size = mft_na->data_size; + + /* + * Extend mft data initialized size (and data size of course) to reach + * the allocated mft record, formatting the mft records along the way. + * Note: We only modify the ntfs_attr structure as that is all that is + * needed by ntfs_mft_record_format(). We will update the attribute + * record itself in one fell swoop later on. + */ + while (size > mft_na->initialized_size) { + s64 ll2 = mft_na->initialized_size >> vol->mft_record_size_bits; + mft_na->initialized_size += vol->mft_record_size; + if (mft_na->initialized_size > mft_na->data_size) + mft_na->data_size = mft_na->initialized_size; + ntfs_log_debug("Initializing mft record 0x%llx.\n", (long long)ll2); + if (ntfs_mft_record_format(vol, ll2) < 0) { + ntfs_log_perror("Failed to format mft record"); + goto undo_data_init; + } + } + + /* Update the mft data attribute record to reflect the new sizes. */ + ctx = ntfs_attr_get_search_ctx(mft_na->ni, NULL); + if (!ctx) + goto undo_data_init; + if (ntfs_attr_lookup(mft_na->type, mft_na->name, mft_na->name_len, 0, + 0, NULL, 0, ctx)) { + ntfs_log_error("Failed to find first attribute extent of " + "mft data attribute.\n"); + ntfs_attr_put_search_ctx(ctx); + goto undo_data_init; + } + ctx->attr->initialized_size = cpu_to_sle64(mft_na->initialized_size); + ctx->attr->data_size = cpu_to_sle64(mft_na->data_size); + ctx->attr->allocated_size = cpu_to_sle64(mft_na->allocated_size); + + /* Ensure the changes make it to disk. */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + ntfs_log_debug("Status of mft data after mft record initialization: " + "allocated_size 0x%llx, data_size 0x%llx, " + "initialized_size 0x%llx.\n", + (long long)mft_na->allocated_size, + (long long)mft_na->data_size, + (long long)mft_na->initialized_size); + + /* Sanity checks. */ + if (mft_na->data_size > mft_na->allocated_size || + mft_na->initialized_size > mft_na->data_size) + NTFS_BUG("mft_na sanity checks failed"); + + /* Sync MFT to minimize data loss if there won't be clean unmount. */ + if (ntfs_inode_sync(mft_na->ni)) + goto undo_data_init; + + ret = 0; +out: + ntfs_log_leave("\n"); + return ret; + undo_data_init: - mft_na->initialized_size = old_data_initialized; - mft_na->data_size = old_data_size; - goto out; + mft_na->initialized_size = old_data_initialized; + mft_na->data_size = old_data_size; + goto out; } -static int ntfs_mft_rec_init( ntfs_volume *vol, s64 size ) +static int ntfs_mft_rec_init(ntfs_volume *vol, s64 size) { - int ret = -1; - ntfs_attr *mft_na, *mftbmp_na; - s64 old_data_initialized, old_data_size; - ntfs_attr_search_ctx *ctx; + int ret = -1; + ntfs_attr *mft_na, *mftbmp_na; + s64 old_data_initialized, old_data_size; + ntfs_attr_search_ctx *ctx; + + ntfs_log_enter("Entering\n"); + + mft_na = vol->mft_na; + mftbmp_na = vol->mftbmp_na; + + if (size > mft_na->allocated_size || size > mft_na->initialized_size) { + errno = EIO; + ntfs_log_perror("%s: unexpected $MFT sizes, see below", __FUNCTION__); + ntfs_log_error("$MFT: size=%lld allocated_size=%lld " + "data_size=%lld initialized_size=%lld\n", + (long long)size, + (long long)mft_na->allocated_size, + (long long)mft_na->data_size, + (long long)mft_na->initialized_size); + goto out; + } + + old_data_initialized = mft_na->initialized_size; + old_data_size = mft_na->data_size; + + /* Update the mft data attribute record to reflect the new sizes. */ + ctx = ntfs_attr_get_search_ctx(mft_na->ni, NULL); + if (!ctx) + goto undo_data_init; - ntfs_log_enter( "Entering\n" ); - - mft_na = vol->mft_na; - mftbmp_na = vol->mftbmp_na; - - if ( size > mft_na->allocated_size || size > mft_na->initialized_size ) - { - errno = EIO; - ntfs_log_perror( "%s: unexpected $MFT sizes, see below", __FUNCTION__ ); - ntfs_log_error( "$MFT: size=%lld allocated_size=%lld " - "data_size=%lld initialized_size=%lld\n", - ( long long )size, - ( long long )mft_na->allocated_size, - ( long long )mft_na->data_size, - ( long long )mft_na->initialized_size ); - goto out; - } - - old_data_initialized = mft_na->initialized_size; - old_data_size = mft_na->data_size; - - /* Update the mft data attribute record to reflect the new sizes. */ - ctx = ntfs_attr_get_search_ctx( mft_na->ni, NULL ); - if ( !ctx ) - goto undo_data_init; - - if ( ntfs_attr_lookup( mft_na->type, mft_na->name, mft_na->name_len, 0, - 0, NULL, 0, ctx ) ) - { - ntfs_log_error( "Failed to find first attribute extent of " - "mft data attribute.\n" ); - ntfs_attr_put_search_ctx( ctx ); - goto undo_data_init; - } - ctx->attr->initialized_size = cpu_to_sle64( mft_na->initialized_size ); - ctx->attr->data_size = cpu_to_sle64( mft_na->data_size ); - - /* CHECKME: ctx->attr->allocation_size is already ok? */ - - /* Ensure the changes make it to disk. */ - ntfs_inode_mark_dirty( ctx->ntfs_ino ); - ntfs_attr_put_search_ctx( ctx ); - - /* Sanity checks. */ - if ( mft_na->data_size > mft_na->allocated_size || - mft_na->initialized_size > mft_na->data_size ) - NTFS_BUG( "mft_na sanity checks failed" ); -out: - ntfs_log_leave( "\n" ); - return ret; + if (ntfs_attr_lookup(mft_na->type, mft_na->name, mft_na->name_len, 0, + 0, NULL, 0, ctx)) { + ntfs_log_error("Failed to find first attribute extent of " + "mft data attribute.\n"); + ntfs_attr_put_search_ctx(ctx); + goto undo_data_init; + } + ctx->attr->initialized_size = cpu_to_sle64(mft_na->initialized_size); + ctx->attr->data_size = cpu_to_sle64(mft_na->data_size); + /* CHECKME: ctx->attr->allocation_size is already ok? */ + + /* Ensure the changes make it to disk. */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + + /* Sanity checks. */ + if (mft_na->data_size > mft_na->allocated_size || + mft_na->initialized_size > mft_na->data_size) + NTFS_BUG("mft_na sanity checks failed"); +out: + ntfs_log_leave("\n"); + return ret; + undo_data_init: - mft_na->initialized_size = old_data_initialized; - mft_na->data_size = old_data_size; - goto out; + mft_na->initialized_size = old_data_initialized; + mft_na->data_size = old_data_size; + goto out; } -static ntfs_inode *ntfs_mft_rec_alloc( ntfs_volume *vol ) +static ntfs_inode *ntfs_mft_rec_alloc(ntfs_volume *vol) { - s64 ll, bit; - ntfs_attr *mft_na, *mftbmp_na; - MFT_RECORD *m; - ntfs_inode *ni = NULL; - ntfs_inode *base_ni; - int err; - le16 seq_no, usn; + s64 ll, bit; + ntfs_attr *mft_na, *mftbmp_na; + MFT_RECORD *m; + ntfs_inode *ni = NULL; + ntfs_inode *base_ni; + int err; + le16 seq_no, usn; - ntfs_log_enter( "Entering\n" ); + ntfs_log_enter("Entering\n"); - mft_na = vol->mft_na; - mftbmp_na = vol->mftbmp_na; + mft_na = vol->mft_na; + mftbmp_na = vol->mftbmp_na; - base_ni = mft_na->ni; + base_ni = mft_na->ni; - bit = ntfs_mft_bitmap_find_free_rec( vol, base_ni ); - if ( bit >= 0 ) - goto found_free_rec; + bit = ntfs_mft_bitmap_find_free_rec(vol, base_ni); + if (bit >= 0) + goto found_free_rec; - if ( errno != ENOSPC ) - goto out; - - errno = ENOSPC; - /* strerror() is intentionally used below, we want to log this error. */ - ntfs_log_error( "No free mft record for $MFT: %s\n", strerror( errno ) ); - goto err_out; + if (errno != ENOSPC) + goto out; + + errno = ENOSPC; + /* strerror() is intentionally used below, we want to log this error. */ + ntfs_log_error("No free mft record for $MFT: %s\n", strerror(errno)); + goto err_out; found_free_rec: - if ( ntfs_bitmap_set_bit( mftbmp_na, bit ) ) - { - ntfs_log_error( "Failed to allocate bit in mft bitmap #2\n" ); - goto err_out; - } + if (ntfs_bitmap_set_bit(mftbmp_na, bit)) { + ntfs_log_error("Failed to allocate bit in mft bitmap #2\n"); + goto err_out; + } + + ll = (bit + 1) << vol->mft_record_size_bits; + if (ll > mft_na->initialized_size) + if (ntfs_mft_rec_init(vol, ll) < 0) + goto undo_mftbmp_alloc; + /* + * We now have allocated and initialized the mft record. Need to read + * it from disk and re-format it, preserving the sequence number if it + * is not zero as well as the update sequence number if it is not zero + * or -1 (0xffff). + */ + m = ntfs_malloc(vol->mft_record_size); + if (!m) + goto undo_mftbmp_alloc; + + if (ntfs_mft_record_read(vol, bit, m)) { + free(m); + goto undo_mftbmp_alloc; + } + /* Sanity check that the mft record is really not in use. */ + if (ntfs_is_file_record(m->magic) && (m->flags & MFT_RECORD_IN_USE)) { + ntfs_log_error("Inode %lld is used but it wasn't marked in " + "$MFT bitmap. Fixed.\n", (long long)bit); + free(m); + goto undo_mftbmp_alloc; + } - ll = ( bit + 1 ) << vol->mft_record_size_bits; - if ( ll > mft_na->initialized_size ) - if ( ntfs_mft_rec_init( vol, ll ) < 0 ) - goto undo_mftbmp_alloc; - /* - * We now have allocated and initialized the mft record. Need to read - * it from disk and re-format it, preserving the sequence number if it - * is not zero as well as the update sequence number if it is not zero - * or -1 (0xffff). - */ - m = ntfs_malloc( vol->mft_record_size ); - if ( !m ) - goto undo_mftbmp_alloc; + seq_no = m->sequence_number; + usn = *(le16*)((u8*)m + le16_to_cpu(m->usa_ofs)); + if (ntfs_mft_record_layout(vol, bit, m)) { + ntfs_log_error("Failed to re-format mft record.\n"); + free(m); + goto undo_mftbmp_alloc; + } + if (seq_no) + m->sequence_number = seq_no; + seq_no = usn; + if (seq_no && seq_no != const_cpu_to_le16(0xffff)) + *(le16*)((u8*)m + le16_to_cpu(m->usa_ofs)) = usn; + /* Set the mft record itself in use. */ + m->flags |= MFT_RECORD_IN_USE; + /* Now need to open an ntfs inode for the mft record. */ + ni = ntfs_inode_allocate(vol); + if (!ni) { + ntfs_log_error("Failed to allocate buffer for inode.\n"); + free(m); + goto undo_mftbmp_alloc; + } + ni->mft_no = bit; + ni->mrec = m; + /* + * If we are allocating an extent mft record, make the opened inode an + * extent inode and attach it to the base inode. Also, set the base + * mft record reference in the extent inode. + */ + ni->nr_extents = -1; + ni->base_ni = base_ni; + m->base_mft_record = MK_LE_MREF(base_ni->mft_no, + le16_to_cpu(base_ni->mrec->sequence_number)); + /* + * Attach the extent inode to the base inode, reallocating + * memory if needed. + */ + if (!(base_ni->nr_extents & 3)) { + ntfs_inode **extent_nis; + int i; - if ( ntfs_mft_record_read( vol, bit, m ) ) - { - free( m ); - goto undo_mftbmp_alloc; - } - /* Sanity check that the mft record is really not in use. */ - if ( ntfs_is_file_record( m->magic ) && ( m->flags & MFT_RECORD_IN_USE ) ) - { - ntfs_log_error( "Inode %lld is used but it wasn't marked in " - "$MFT bitmap. Fixed.\n", ( long long )bit ); - free( m ); - goto undo_mftbmp_alloc; - } - - seq_no = m->sequence_number; - usn = *( le16* )( ( u8* )m + le16_to_cpu( m->usa_ofs ) ); - if ( ntfs_mft_record_layout( vol, bit, m ) ) - { - ntfs_log_error( "Failed to re-format mft record.\n" ); - free( m ); - goto undo_mftbmp_alloc; - } - if ( seq_no ) - m->sequence_number = seq_no; - seq_no = usn; - if ( seq_no && seq_no != const_cpu_to_le16( 0xffff ) ) - *( le16* )( ( u8* )m + le16_to_cpu( m->usa_ofs ) ) = usn; - /* Set the mft record itself in use. */ - m->flags |= MFT_RECORD_IN_USE; - /* Now need to open an ntfs inode for the mft record. */ - ni = ntfs_inode_allocate( vol ); - if ( !ni ) - { - ntfs_log_error( "Failed to allocate buffer for inode.\n" ); - free( m ); - goto undo_mftbmp_alloc; - } - ni->mft_no = bit; - ni->mrec = m; - /* - * If we are allocating an extent mft record, make the opened inode an - * extent inode and attach it to the base inode. Also, set the base - * mft record reference in the extent inode. - */ - ni->nr_extents = -1; - ni->base_ni = base_ni; - m->base_mft_record = MK_LE_MREF( base_ni->mft_no, - le16_to_cpu( base_ni->mrec->sequence_number ) ); - /* - * Attach the extent inode to the base inode, reallocating - * memory if needed. - */ - if ( !( base_ni->nr_extents & 3 ) ) - { - ntfs_inode **extent_nis; - int i; - - i = ( base_ni->nr_extents + 4 ) * sizeof( ntfs_inode * ); - extent_nis = ntfs_malloc( i ); - if ( !extent_nis ) - { - free( m ); - free( ni ); - goto undo_mftbmp_alloc; - } - if ( base_ni->nr_extents ) - { - memcpy( extent_nis, base_ni->extent_nis, - i - 4 * sizeof( ntfs_inode * ) ); - free( base_ni->extent_nis ); - } - base_ni->extent_nis = extent_nis; - } - base_ni->extent_nis[base_ni->nr_extents++] = ni; - - /* Make sure the allocated inode is written out to disk later. */ - ntfs_inode_mark_dirty( ni ); - /* Initialize time, allocated and data size in ntfs_inode struct. */ - ni->data_size = ni->allocated_size = 0; - ni->flags = 0; - ni->creation_time = ni->last_data_change_time = - ni->last_mft_change_time = - ni->last_access_time = ntfs_current_time(); - /* Update the default mft allocation position if it was used. */ - if ( !base_ni ) - vol->mft_data_pos = bit + 1; - /* Return the opened, allocated inode of the allocated mft record. */ - ntfs_log_error( "allocated %sinode %lld\n", - base_ni ? "extent " : "", ( long long )bit ); + i = (base_ni->nr_extents + 4) * sizeof(ntfs_inode *); + extent_nis = ntfs_malloc(i); + if (!extent_nis) { + free(m); + free(ni); + goto undo_mftbmp_alloc; + } + if (base_ni->nr_extents) { + memcpy(extent_nis, base_ni->extent_nis, + i - 4 * sizeof(ntfs_inode *)); + free(base_ni->extent_nis); + } + base_ni->extent_nis = extent_nis; + } + base_ni->extent_nis[base_ni->nr_extents++] = ni; + + /* Make sure the allocated inode is written out to disk later. */ + ntfs_inode_mark_dirty(ni); + /* Initialize time, allocated and data size in ntfs_inode struct. */ + ni->data_size = ni->allocated_size = 0; + ni->flags = 0; + ni->creation_time = ni->last_data_change_time = + ni->last_mft_change_time = + ni->last_access_time = ntfs_current_time(); + /* Update the default mft allocation position if it was used. */ + if (!base_ni) + vol->mft_data_pos = bit + 1; + /* Return the opened, allocated inode of the allocated mft record. */ + ntfs_log_error("allocated %sinode %lld\n", + base_ni ? "extent " : "", (long long)bit); out: - ntfs_log_leave( "\n" ); - return ni; + ntfs_log_leave("\n"); + return ni; undo_mftbmp_alloc: - err = errno; - if ( ntfs_bitmap_clear_bit( mftbmp_na, bit ) ) - ntfs_log_error( "Failed to clear bit in mft bitmap.%s\n", es ); - errno = err; + err = errno; + if (ntfs_bitmap_clear_bit(mftbmp_na, bit)) + ntfs_log_error("Failed to clear bit in mft bitmap.%s\n", es); + errno = err; err_out: - if ( !errno ) - errno = EIO; - ni = NULL; - goto out; + if (!errno) + errno = EIO; + ni = NULL; + goto out; } /** * ntfs_mft_record_alloc - allocate an mft record on an ntfs volume - * @vol: volume on which to allocate the mft record - * @base_ni: open base inode if allocating an extent mft record or NULL + * @vol: volume on which to allocate the mft record + * @base_ni: open base inode if allocating an extent mft record or NULL * * Allocate an mft record in $MFT/$DATA of an open ntfs volume @vol. * @@ -1639,12 +1558,12 @@ err_out: * superfluous bits are padded with zeroes. * * Thus, when we return successfully (return value non-zero), we will have: - * - initialized / extended the mft bitmap if necessary, - * - initialized / extended the mft data if necessary, - * - set the bit corresponding to the mft record being allocated in the - * mft bitmap, - * - open an ntfs_inode for the allocated mft record, and we will - * - return the ntfs_inode. + * - initialized / extended the mft bitmap if necessary, + * - initialized / extended the mft data if necessary, + * - set the bit corresponding to the mft record being allocated in the + * mft bitmap, + * - open an ntfs_inode for the allocated mft record, and we will + * - return the ntfs_inode. * * On error (return value zero), nothing will have changed. If we had changed * anything before the error occurred, we will have reverted back to the @@ -1665,245 +1584,230 @@ err_out: * when reading the bitmap but if we are careful, we should be able to avoid * all problems. */ -ntfs_inode *ntfs_mft_record_alloc( ntfs_volume *vol, ntfs_inode *base_ni ) +ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, ntfs_inode *base_ni) { - s64 ll, bit; - ntfs_attr *mft_na, *mftbmp_na; - MFT_RECORD *m; - ntfs_inode *ni = NULL; - int err; - le16 seq_no, usn; + s64 ll, bit; + ntfs_attr *mft_na, *mftbmp_na; + MFT_RECORD *m; + ntfs_inode *ni = NULL; + int err; + le16 seq_no, usn; - if ( base_ni ) - ntfs_log_enter( "Entering (allocating an extent mft record for " - "base mft record %lld).\n", - ( long long )base_ni->mft_no ); - else - ntfs_log_enter( "Entering (allocating a base mft record)\n" ); - if ( !vol || !vol->mft_na || !vol->mftbmp_na ) - { - errno = EINVAL; - goto out; - } + if (base_ni) + ntfs_log_enter("Entering (allocating an extent mft record for " + "base mft record %lld).\n", + (long long)base_ni->mft_no); + else + ntfs_log_enter("Entering (allocating a base mft record)\n"); + if (!vol || !vol->mft_na || !vol->mftbmp_na) { + errno = EINVAL; + goto out; + } + + if (ntfs_is_mft(base_ni)) { + ni = ntfs_mft_rec_alloc(vol); + goto out; + } - if ( ntfs_is_mft( base_ni ) ) - { - ni = ntfs_mft_rec_alloc( vol ); - goto out; - } + mft_na = vol->mft_na; + mftbmp_na = vol->mftbmp_na; +retry: + bit = ntfs_mft_bitmap_find_free_rec(vol, base_ni); + if (bit >= 0) { + ntfs_log_debug("found free record (#1) at %lld\n", + (long long)bit); + goto found_free_rec; + } + if (errno != ENOSPC) + goto out; + /* + * No free mft records left. If the mft bitmap already covers more + * than the currently used mft records, the next records are all free, + * so we can simply allocate the first unused mft record. + * Note: We also have to make sure that the mft bitmap at least covers + * the first 24 mft records as they are special and whilst they may not + * be in use, we do not allocate from them. + */ + ll = mft_na->initialized_size >> vol->mft_record_size_bits; + if (mftbmp_na->initialized_size << 3 > ll && + mftbmp_na->initialized_size > RESERVED_MFT_RECORDS / 8) { + bit = ll; + if (bit < RESERVED_MFT_RECORDS) + bit = RESERVED_MFT_RECORDS; + ntfs_log_debug("found free record (#2) at %lld\n", + (long long)bit); + goto found_free_rec; + } + /* + * The mft bitmap needs to be expanded until it covers the first unused + * mft record that we can allocate. + * Note: The smallest mft record we allocate is mft record 24. + */ + ntfs_log_debug("Status of mftbmp before extension: allocated_size 0x%llx, " + "data_size 0x%llx, initialized_size 0x%llx.\n", + (long long)mftbmp_na->allocated_size, + (long long)mftbmp_na->data_size, + (long long)mftbmp_na->initialized_size); + if (mftbmp_na->initialized_size + 8 > mftbmp_na->allocated_size) { - mft_na = vol->mft_na; - mftbmp_na = vol->mftbmp_na; -retry: - bit = ntfs_mft_bitmap_find_free_rec( vol, base_ni ); - if ( bit >= 0 ) - { - ntfs_log_debug( "found free record (#1) at %lld\n", - ( long long )bit ); - goto found_free_rec; - } - if ( errno != ENOSPC ) - goto out; - /* - * No free mft records left. If the mft bitmap already covers more - * than the currently used mft records, the next records are all free, - * so we can simply allocate the first unused mft record. - * Note: We also have to make sure that the mft bitmap at least covers - * the first 24 mft records as they are special and whilst they may not - * be in use, we do not allocate from them. - */ - ll = mft_na->initialized_size >> vol->mft_record_size_bits; - if ( mftbmp_na->initialized_size << 3 > ll && - mftbmp_na->initialized_size > RESERVED_MFT_RECORDS / 8 ) - { - bit = ll; - if ( bit < RESERVED_MFT_RECORDS ) - bit = RESERVED_MFT_RECORDS; - ntfs_log_debug( "found free record (#2) at %lld\n", - ( long long )bit ); - goto found_free_rec; - } - /* - * The mft bitmap needs to be expanded until it covers the first unused - * mft record that we can allocate. - * Note: The smallest mft record we allocate is mft record 24. - */ - ntfs_log_debug( "Status of mftbmp before extension: allocated_size 0x%llx, " - "data_size 0x%llx, initialized_size 0x%llx.\n", - ( long long )mftbmp_na->allocated_size, - ( long long )mftbmp_na->data_size, - ( long long )mftbmp_na->initialized_size ); - if ( mftbmp_na->initialized_size + 8 > mftbmp_na->allocated_size ) - { + int ret = ntfs_mft_bitmap_extend_allocation(vol); - int ret = ntfs_mft_bitmap_extend_allocation( vol ); + if (ret == STATUS_ERROR) + goto err_out; + if (ret == STATUS_KEEP_SEARCHING) { + ret = ntfs_mft_bitmap_extend_allocation(vol); + if (ret != STATUS_OK) + goto err_out; + } - if ( ret == STATUS_ERROR ) - goto err_out; - if ( ret == STATUS_KEEP_SEARCHING ) - { - ret = ntfs_mft_bitmap_extend_allocation( vol ); - if ( ret != STATUS_OK ) - goto err_out; - } - - ntfs_log_debug( "Status of mftbmp after allocation extension: " - "allocated_size 0x%llx, data_size 0x%llx, " - "initialized_size 0x%llx.\n", - ( long long )mftbmp_na->allocated_size, - ( long long )mftbmp_na->data_size, - ( long long )mftbmp_na->initialized_size ); - } - /* - * We now have sufficient allocated space, extend the initialized_size - * as well as the data_size if necessary and fill the new space with - * zeroes. - */ - bit = mftbmp_na->initialized_size << 3; - if ( ntfs_mft_bitmap_extend_initialized( vol ) ) - goto err_out; - ntfs_log_debug( "Status of mftbmp after initialized extension: " - "allocated_size 0x%llx, data_size 0x%llx, " - "initialized_size 0x%llx.\n", - ( long long )mftbmp_na->allocated_size, - ( long long )mftbmp_na->data_size, - ( long long )mftbmp_na->initialized_size ); - ntfs_log_debug( "found free record (#3) at %lld\n", ( long long )bit ); + ntfs_log_debug("Status of mftbmp after allocation extension: " + "allocated_size 0x%llx, data_size 0x%llx, " + "initialized_size 0x%llx.\n", + (long long)mftbmp_na->allocated_size, + (long long)mftbmp_na->data_size, + (long long)mftbmp_na->initialized_size); + } + /* + * We now have sufficient allocated space, extend the initialized_size + * as well as the data_size if necessary and fill the new space with + * zeroes. + */ + bit = mftbmp_na->initialized_size << 3; + if (ntfs_mft_bitmap_extend_initialized(vol)) + goto err_out; + ntfs_log_debug("Status of mftbmp after initialized extension: " + "allocated_size 0x%llx, data_size 0x%llx, " + "initialized_size 0x%llx.\n", + (long long)mftbmp_na->allocated_size, + (long long)mftbmp_na->data_size, + (long long)mftbmp_na->initialized_size); + ntfs_log_debug("found free record (#3) at %lld\n", (long long)bit); found_free_rec: - /* @bit is the found free mft record, allocate it in the mft bitmap. */ - if ( ntfs_bitmap_set_bit( mftbmp_na, bit ) ) - { - ntfs_log_error( "Failed to allocate bit in mft bitmap.\n" ); - goto err_out; - } + /* @bit is the found free mft record, allocate it in the mft bitmap. */ + if (ntfs_bitmap_set_bit(mftbmp_na, bit)) { + ntfs_log_error("Failed to allocate bit in mft bitmap.\n"); + goto err_out; + } + + /* The mft bitmap is now uptodate. Deal with mft data attribute now. */ + ll = (bit + 1) << vol->mft_record_size_bits; + if (ll > mft_na->initialized_size) + if (ntfs_mft_record_init(vol, ll) < 0) + goto undo_mftbmp_alloc; - /* The mft bitmap is now uptodate. Deal with mft data attribute now. */ - ll = ( bit + 1 ) << vol->mft_record_size_bits; - if ( ll > mft_na->initialized_size ) - if ( ntfs_mft_record_init( vol, ll ) < 0 ) - goto undo_mftbmp_alloc; + /* + * We now have allocated and initialized the mft record. Need to read + * it from disk and re-format it, preserving the sequence number if it + * is not zero as well as the update sequence number if it is not zero + * or -1 (0xffff). + */ + m = ntfs_malloc(vol->mft_record_size); + if (!m) + goto undo_mftbmp_alloc; + + if (ntfs_mft_record_read(vol, bit, m)) { + free(m); + goto undo_mftbmp_alloc; + } + /* Sanity check that the mft record is really not in use. */ + if (ntfs_is_file_record(m->magic) && (m->flags & MFT_RECORD_IN_USE)) { + ntfs_log_error("Inode %lld is used but it wasn't marked in " + "$MFT bitmap. Fixed.\n", (long long)bit); + free(m); + goto retry; + } + seq_no = m->sequence_number; + usn = *(le16*)((u8*)m + le16_to_cpu(m->usa_ofs)); + if (ntfs_mft_record_layout(vol, bit, m)) { + ntfs_log_error("Failed to re-format mft record.\n"); + free(m); + goto undo_mftbmp_alloc; + } + if (seq_no) + m->sequence_number = seq_no; + seq_no = usn; + if (seq_no && seq_no != const_cpu_to_le16(0xffff)) + *(le16*)((u8*)m + le16_to_cpu(m->usa_ofs)) = usn; + /* Set the mft record itself in use. */ + m->flags |= MFT_RECORD_IN_USE; + /* Now need to open an ntfs inode for the mft record. */ + ni = ntfs_inode_allocate(vol); + if (!ni) { + ntfs_log_error("Failed to allocate buffer for inode.\n"); + free(m); + goto undo_mftbmp_alloc; + } + ni->mft_no = bit; + ni->mrec = m; + /* + * If we are allocating an extent mft record, make the opened inode an + * extent inode and attach it to the base inode. Also, set the base + * mft record reference in the extent inode. + */ + if (base_ni) { + ni->nr_extents = -1; + ni->base_ni = base_ni; + m->base_mft_record = MK_LE_MREF(base_ni->mft_no, + le16_to_cpu(base_ni->mrec->sequence_number)); + /* + * Attach the extent inode to the base inode, reallocating + * memory if needed. + */ + if (!(base_ni->nr_extents & 3)) { + ntfs_inode **extent_nis; + int i; - /* - * We now have allocated and initialized the mft record. Need to read - * it from disk and re-format it, preserving the sequence number if it - * is not zero as well as the update sequence number if it is not zero - * or -1 (0xffff). - */ - m = ntfs_malloc( vol->mft_record_size ); - if ( !m ) - goto undo_mftbmp_alloc; - - if ( ntfs_mft_record_read( vol, bit, m ) ) - { - free( m ); - goto undo_mftbmp_alloc; - } - /* Sanity check that the mft record is really not in use. */ - if ( ntfs_is_file_record( m->magic ) && ( m->flags & MFT_RECORD_IN_USE ) ) - { - ntfs_log_error( "Inode %lld is used but it wasn't marked in " - "$MFT bitmap. Fixed.\n", ( long long )bit ); - free( m ); - goto retry; - } - seq_no = m->sequence_number; - usn = *( le16* )( ( u8* )m + le16_to_cpu( m->usa_ofs ) ); - if ( ntfs_mft_record_layout( vol, bit, m ) ) - { - ntfs_log_error( "Failed to re-format mft record.\n" ); - free( m ); - goto undo_mftbmp_alloc; - } - if ( seq_no ) - m->sequence_number = seq_no; - seq_no = usn; - if ( seq_no && seq_no != const_cpu_to_le16( 0xffff ) ) - *( le16* )( ( u8* )m + le16_to_cpu( m->usa_ofs ) ) = usn; - /* Set the mft record itself in use. */ - m->flags |= MFT_RECORD_IN_USE; - /* Now need to open an ntfs inode for the mft record. */ - ni = ntfs_inode_allocate( vol ); - if ( !ni ) - { - ntfs_log_error( "Failed to allocate buffer for inode.\n" ); - free( m ); - goto undo_mftbmp_alloc; - } - ni->mft_no = bit; - ni->mrec = m; - /* - * If we are allocating an extent mft record, make the opened inode an - * extent inode and attach it to the base inode. Also, set the base - * mft record reference in the extent inode. - */ - if ( base_ni ) - { - ni->nr_extents = -1; - ni->base_ni = base_ni; - m->base_mft_record = MK_LE_MREF( base_ni->mft_no, - le16_to_cpu( base_ni->mrec->sequence_number ) ); - /* - * Attach the extent inode to the base inode, reallocating - * memory if needed. - */ - if ( !( base_ni->nr_extents & 3 ) ) - { - ntfs_inode **extent_nis; - int i; - - i = ( base_ni->nr_extents + 4 ) * sizeof( ntfs_inode * ); - extent_nis = ntfs_malloc( i ); - if ( !extent_nis ) - { - free( m ); - free( ni ); - goto undo_mftbmp_alloc; - } - if ( base_ni->nr_extents ) - { - memcpy( extent_nis, base_ni->extent_nis, - i - 4 * sizeof( ntfs_inode * ) ); - free( base_ni->extent_nis ); - } - base_ni->extent_nis = extent_nis; - } - base_ni->extent_nis[base_ni->nr_extents++] = ni; - } - /* Make sure the allocated inode is written out to disk later. */ - ntfs_inode_mark_dirty( ni ); - /* Initialize time, allocated and data size in ntfs_inode struct. */ - ni->data_size = ni->allocated_size = 0; - ni->flags = 0; - ni->creation_time = ni->last_data_change_time = - ni->last_mft_change_time = - ni->last_access_time = ntfs_current_time(); - /* Update the default mft allocation position if it was used. */ - if ( !base_ni ) - vol->mft_data_pos = bit + 1; - /* Return the opened, allocated inode of the allocated mft record. */ - ntfs_log_debug( "allocated %sinode 0x%llx.\n", - base_ni ? "extent " : "", ( long long )bit ); - vol->free_mft_records--; + i = (base_ni->nr_extents + 4) * sizeof(ntfs_inode *); + extent_nis = ntfs_malloc(i); + if (!extent_nis) { + free(m); + free(ni); + goto undo_mftbmp_alloc; + } + if (base_ni->nr_extents) { + memcpy(extent_nis, base_ni->extent_nis, + i - 4 * sizeof(ntfs_inode *)); + free(base_ni->extent_nis); + } + base_ni->extent_nis = extent_nis; + } + base_ni->extent_nis[base_ni->nr_extents++] = ni; + } + /* Make sure the allocated inode is written out to disk later. */ + ntfs_inode_mark_dirty(ni); + /* Initialize time, allocated and data size in ntfs_inode struct. */ + ni->data_size = ni->allocated_size = 0; + ni->flags = 0; + ni->creation_time = ni->last_data_change_time = + ni->last_mft_change_time = + ni->last_access_time = ntfs_current_time(); + /* Update the default mft allocation position if it was used. */ + if (!base_ni) + vol->mft_data_pos = bit + 1; + /* Return the opened, allocated inode of the allocated mft record. */ + ntfs_log_debug("allocated %sinode 0x%llx.\n", + base_ni ? "extent " : "", (long long)bit); + vol->free_mft_records--; out: - ntfs_log_leave( "\n" ); - return ni; + ntfs_log_leave("\n"); + return ni; undo_mftbmp_alloc: - err = errno; - if ( ntfs_bitmap_clear_bit( mftbmp_na, bit ) ) - ntfs_log_error( "Failed to clear bit in mft bitmap.%s\n", es ); - errno = err; + err = errno; + if (ntfs_bitmap_clear_bit(mftbmp_na, bit)) + ntfs_log_error("Failed to clear bit in mft bitmap.%s\n", es); + errno = err; err_out: - if ( !errno ) - errno = EIO; - ni = NULL; - goto out; + if (!errno) + errno = EIO; + ni = NULL; + goto out; } /** * ntfs_mft_record_free - free an mft record on an ntfs volume - * @vol: volume on which to free the mft record - * @ni: open ntfs inode of the mft record to free + * @vol: volume on which to free the mft record + * @ni: open ntfs inode of the mft record to free * * Free the mft record of the open inode @ni on the mounted ntfs volume @vol. * Note that this function calls ntfs_inode_close() internally and hence you @@ -1911,101 +1815,95 @@ err_out: * * On success return 0 and on error return -1 with errno set to the error code. */ -int ntfs_mft_record_free( ntfs_volume *vol, ntfs_inode *ni ) +int ntfs_mft_record_free(ntfs_volume *vol, ntfs_inode *ni) { - u64 mft_no; - int err; - u16 seq_no; - le16 old_seq_no; + u64 mft_no; + int err; + u16 seq_no; + le16 old_seq_no; - ntfs_log_trace( "Entering for inode 0x%llx.\n", ( long long ) ni->mft_no ); + ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no); - if ( !vol || !vol->mftbmp_na || !ni ) - { - errno = EINVAL; - return -1; - } + if (!vol || !vol->mftbmp_na || !ni) { + errno = EINVAL; + return -1; + } - /* Cache the mft reference for later. */ - mft_no = ni->mft_no; + /* Cache the mft reference for later. */ + mft_no = ni->mft_no; - /* Mark the mft record as not in use. */ - ni->mrec->flags &= ~MFT_RECORD_IN_USE; + /* Mark the mft record as not in use. */ + ni->mrec->flags &= ~MFT_RECORD_IN_USE; - /* Increment the sequence number, skipping zero, if it is not zero. */ - old_seq_no = ni->mrec->sequence_number; - seq_no = le16_to_cpu( old_seq_no ); - if ( seq_no == 0xffff ) - seq_no = 1; - else if ( seq_no ) - seq_no++; - ni->mrec->sequence_number = cpu_to_le16( seq_no ); + /* Increment the sequence number, skipping zero, if it is not zero. */ + old_seq_no = ni->mrec->sequence_number; + seq_no = le16_to_cpu(old_seq_no); + if (seq_no == 0xffff) + seq_no = 1; + else if (seq_no) + seq_no++; + ni->mrec->sequence_number = cpu_to_le16(seq_no); - /* Set the inode dirty and write it out. */ - ntfs_inode_mark_dirty( ni ); - if ( ntfs_inode_sync( ni ) ) - { - err = errno; - goto sync_rollback; - } + /* Set the inode dirty and write it out. */ + ntfs_inode_mark_dirty(ni); + if (ntfs_inode_sync(ni)) { + err = errno; + goto sync_rollback; + } - /* Clear the bit in the $MFT/$BITMAP corresponding to this record. */ - if ( ntfs_bitmap_clear_bit( vol->mftbmp_na, mft_no ) ) - { - err = errno; - // FIXME: If ntfs_bitmap_clear_run() guarantees rollback on - // error, this could be changed to goto sync_rollback; - goto bitmap_rollback; - } + /* Clear the bit in the $MFT/$BITMAP corresponding to this record. */ + if (ntfs_bitmap_clear_bit(vol->mftbmp_na, mft_no)) { + err = errno; + // FIXME: If ntfs_bitmap_clear_run() guarantees rollback on + // error, this could be changed to goto sync_rollback; + goto bitmap_rollback; + } - /* Throw away the now freed inode. */ + /* Throw away the now freed inode. */ #if CACHE_NIDATA_SIZE - if ( !ntfs_inode_real_close( ni ) ) - { + if (!ntfs_inode_real_close(ni)) { #else - if ( !ntfs_inode_close( ni ) ) - { + if (!ntfs_inode_close(ni)) { #endif - vol->free_mft_records++; - return 0; - } - err = errno; + vol->free_mft_records++; + return 0; + } + err = errno; - /* Rollback what we did... */ + /* Rollback what we did... */ bitmap_rollback: - if ( ntfs_bitmap_set_bit( vol->mftbmp_na, mft_no ) ) - ntfs_log_debug( "Eeek! Rollback failed in ntfs_mft_record_free(). " - "Leaving inconsistent metadata!\n" ); + if (ntfs_bitmap_set_bit(vol->mftbmp_na, mft_no)) + ntfs_log_debug("Eeek! Rollback failed in ntfs_mft_record_free(). " + "Leaving inconsistent metadata!\n"); sync_rollback: - ni->mrec->flags |= MFT_RECORD_IN_USE; - ni->mrec->sequence_number = old_seq_no; - ntfs_inode_mark_dirty( ni ); - errno = err; - return -1; + ni->mrec->flags |= MFT_RECORD_IN_USE; + ni->mrec->sequence_number = old_seq_no; + ntfs_inode_mark_dirty(ni); + errno = err; + return -1; } /** * ntfs_mft_usn_dec - Decrement USN by one - * @mrec: pointer to an mft record + * @mrec: pointer to an mft record * * On success return 0 and on error return -1 with errno set. */ -int ntfs_mft_usn_dec( MFT_RECORD *mrec ) +int ntfs_mft_usn_dec(MFT_RECORD *mrec) { - u16 usn; - le16 *usnp; + u16 usn; + le16 *usnp; - if ( !mrec ) - { - errno = EINVAL; - return -1; - } - usnp = ( le16* )( ( char* )mrec + le16_to_cpu( mrec->usa_ofs ) ); - usn = le16_to_cpup( usnp ); - if ( usn-- <= 1 ) - usn = 0xfffe; - *usnp = cpu_to_le16( usn ); + if (!mrec) { + errno = EINVAL; + return -1; + } + usnp = (le16*)((char*)mrec + le16_to_cpu(mrec->usa_ofs)); + usn = le16_to_cpup(usnp); + if (usn-- <= 1) + usn = 0xfffe; + *usnp = cpu_to_le16(usn); - return 0; + return 0; } diff --git a/source/libntfs/mft.h b/source/libntfs/mft.h index 1402a766..bb15f0f3 100644 --- a/source/libntfs/mft.h +++ b/source/libntfs/mft.h @@ -29,14 +29,14 @@ #include "layout.h" #include "logging.h" -extern int ntfs_mft_records_read( const ntfs_volume *vol, const MFT_REF mref, - const s64 count, MFT_RECORD *b ); +extern int ntfs_mft_records_read(const ntfs_volume *vol, const MFT_REF mref, + const s64 count, MFT_RECORD *b); /** * ntfs_mft_record_read - read a record from the mft - * @vol: volume to read from - * @mref: mft record number to read - * @b: output data buffer + * @vol: volume to read from + * @mref: mft record number to read + * @b: output data buffer * * Read the mft record specified by @mref from volume @vol into buffer @b. * Return 0 on success or -1 on error, with errno set to the error code. @@ -47,31 +47,31 @@ extern int ntfs_mft_records_read( const ntfs_volume *vol, const MFT_REF mref, * * NOTE: @b has to be at least of size vol->mft_record_size. */ -static __inline__ int ntfs_mft_record_read( const ntfs_volume *vol, - const MFT_REF mref, MFT_RECORD *b ) +static __inline__ int ntfs_mft_record_read(const ntfs_volume *vol, + const MFT_REF mref, MFT_RECORD *b) { - int ret; - - ntfs_log_enter( "Entering for inode %lld\n", ( long long )MREF( mref ) ); - ret = ntfs_mft_records_read( vol, mref, 1, b ); - ntfs_log_leave( "\n" ); - return ret; + int ret; + + ntfs_log_enter("Entering for inode %lld\n", (long long)MREF(mref)); + ret = ntfs_mft_records_read(vol, mref, 1, b); + ntfs_log_leave("\n"); + return ret; } -extern int ntfs_mft_record_check( const ntfs_volume *vol, const MFT_REF mref, - MFT_RECORD *m ); +extern int ntfs_mft_record_check(const ntfs_volume *vol, const MFT_REF mref, + MFT_RECORD *m); -extern int ntfs_file_record_read( const ntfs_volume *vol, const MFT_REF mref, - MFT_RECORD **mrec, ATTR_RECORD **attr ); +extern int ntfs_file_record_read(const ntfs_volume *vol, const MFT_REF mref, + MFT_RECORD **mrec, ATTR_RECORD **attr); -extern int ntfs_mft_records_write( const ntfs_volume *vol, const MFT_REF mref, - const s64 count, MFT_RECORD *b ); +extern int ntfs_mft_records_write(const ntfs_volume *vol, const MFT_REF mref, + const s64 count, MFT_RECORD *b); /** * ntfs_mft_record_write - write an mft record to disk - * @vol: volume to write to - * @mref: mft record number to write - * @b: data buffer containing the mft record to write + * @vol: volume to write to + * @mref: mft record number to write + * @b: data buffer containing the mft record to write * * Write the mft record specified by @mref from buffer @b to volume @vol. * Return 0 on success or -1 on error, with errno set to the error code. @@ -82,20 +82,20 @@ extern int ntfs_mft_records_write( const ntfs_volume *vol, const MFT_REF mref, * * NOTE: @b has to be at least of size vol->mft_record_size. */ -static __inline__ int ntfs_mft_record_write( const ntfs_volume *vol, - const MFT_REF mref, MFT_RECORD *b ) +static __inline__ int ntfs_mft_record_write(const ntfs_volume *vol, + const MFT_REF mref, MFT_RECORD *b) { - int ret; - - ntfs_log_enter( "Entering for inode %lld\n", ( long long )MREF( mref ) ); - ret = ntfs_mft_records_write( vol, mref, 1, b ); - ntfs_log_leave( "\n" ); - return ret; + int ret; + + ntfs_log_enter("Entering for inode %lld\n", (long long)MREF(mref)); + ret = ntfs_mft_records_write(vol, mref, 1, b); + ntfs_log_leave("\n"); + return ret; } /** * ntfs_mft_record_get_data_size - return number of bytes used in mft record @b - * @m: mft record to get the data size of + * @m: mft record to get the data size of * * Takes the mft record @m and returns the number of bytes used in the record * or 0 on error (i.e. @m is not a valid mft record). Zero is not a valid size @@ -109,24 +109,24 @@ static __inline__ int ntfs_mft_record_write( const ntfs_volume *vol, * non-existent (don't know if Windows' NTFS driver/chkdsk wouldn't view this * as corruption in itself though). */ -static __inline__ u32 ntfs_mft_record_get_data_size( const MFT_RECORD *m ) +static __inline__ u32 ntfs_mft_record_get_data_size(const MFT_RECORD *m) { - if ( !m || !ntfs_is_mft_record( m->magic ) ) - return 0; - /* Get the number of used bytes and return it. */ - return le32_to_cpu( m->bytes_in_use ); + if (!m || !ntfs_is_mft_record(m->magic)) + return 0; + /* Get the number of used bytes and return it. */ + return le32_to_cpu(m->bytes_in_use); } -extern int ntfs_mft_record_layout( const ntfs_volume *vol, const MFT_REF mref, - MFT_RECORD *mrec ); +extern int ntfs_mft_record_layout(const ntfs_volume *vol, const MFT_REF mref, + MFT_RECORD *mrec); -extern int ntfs_mft_record_format( const ntfs_volume *vol, const MFT_REF mref ); +extern int ntfs_mft_record_format(const ntfs_volume *vol, const MFT_REF mref); -extern ntfs_inode *ntfs_mft_record_alloc( ntfs_volume *vol, ntfs_inode *base_ni ); +extern ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, ntfs_inode *base_ni); -extern int ntfs_mft_record_free( ntfs_volume *vol, ntfs_inode *ni ); +extern int ntfs_mft_record_free(ntfs_volume *vol, ntfs_inode *ni); -extern int ntfs_mft_usn_dec( MFT_RECORD *mrec ); +extern int ntfs_mft_usn_dec(MFT_RECORD *mrec); #endif /* defined _NTFS_MFT_H */ diff --git a/source/libntfs/misc.c b/source/libntfs/misc.c index cc68d264..b2e17cbf 100644 --- a/source/libntfs/misc.c +++ b/source/libntfs/misc.c @@ -1,6 +1,6 @@ /** * misc.c : miscellaneous : - * - dealing with errors in memory allocation + * - dealing with errors in memory allocation * * Copyright (c) 2008 Jean-Pierre Andre * @@ -37,25 +37,25 @@ /** * ntfs_calloc - * + * * Return a pointer to the allocated memory or NULL if the request fails. */ -void *ntfs_calloc( size_t size ) +void *ntfs_calloc(size_t size) { - void *p; - - p = calloc( 1, size ); - if ( !p ) - ntfs_log_perror( "Failed to calloc %lld bytes", ( long long )size ); - return p; + void *p; + + p = calloc(1, size); + if (!p) + ntfs_log_perror("Failed to calloc %lld bytes", (long long)size); + return p; } -void *ntfs_malloc( size_t size ) +void *ntfs_malloc(size_t size) { - void *p; - - p = malloc( size ); - if ( !p ) - ntfs_log_perror( "Failed to malloc %lld bytes", ( long long )size ); - return p; + void *p; + + p = malloc(size); + if (!p) + ntfs_log_perror("Failed to malloc %lld bytes", (long long)size); + return p; } diff --git a/source/libntfs/misc.h b/source/libntfs/misc.h index 288e3671..a03e964e 100644 --- a/source/libntfs/misc.h +++ b/source/libntfs/misc.h @@ -1,6 +1,6 @@ /* * misc.h : miscellaneous exports - * - memory allocation + * - memory allocation * * Copyright (c) 2008 Jean-Pierre Andre * @@ -23,8 +23,8 @@ #ifndef _NTFS_MISC_H_ #define _NTFS_MISC_H_ -void *ntfs_calloc( size_t size ); -void *ntfs_malloc( size_t size ); +void *ntfs_calloc(size_t size); +void *ntfs_malloc(size_t size); #endif /* _NTFS_MISC_H_ */ diff --git a/source/libntfs/mst.c b/source/libntfs/mst.c index 63ff08f5..470942d6 100644 --- a/source/libntfs/mst.c +++ b/source/libntfs/mst.c @@ -33,8 +33,8 @@ /** * ntfs_mst_post_read_fixup - deprotect multi sector transfer protected data - * @b: pointer to the data to deprotect - * @size: size in bytes of @b + * @b: pointer to the data to deprotect + * @size: size in bytes of @b * * Perform the necessary post read multi sector transfer fixups and detect the * presence of incomplete multi sector transfers. - In that case, overwrite the @@ -43,89 +43,85 @@ * * Return 0 on success and -1 on error, with errno set to the error code. The * following error codes are defined: - * EINVAL Invalid arguments or invalid NTFS record in buffer @b. - * EIO Multi sector transfer error was detected. Magic of the NTFS - * record in @b will have been set to "BAAD". + * EINVAL Invalid arguments or invalid NTFS record in buffer @b. + * EIO Multi sector transfer error was detected. Magic of the NTFS + * record in @b will have been set to "BAAD". */ -int ntfs_mst_post_read_fixup( NTFS_RECORD *b, const u32 size ) +int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size) { - u16 usa_ofs, usa_count, usn; - u16 *usa_pos, *data_pos; + u16 usa_ofs, usa_count, usn; + u16 *usa_pos, *data_pos; - ntfs_log_trace( "Entering\n" ); + ntfs_log_trace("Entering\n"); - /* Setup the variables. */ - usa_ofs = le16_to_cpu( b->usa_ofs ); - /* Decrement usa_count to get number of fixups. */ - usa_count = le16_to_cpu( b->usa_count ) - 1; - /* Size and alignment checks. */ - if ( size & ( NTFS_BLOCK_SIZE - 1 ) || usa_ofs & 1 || - ( u32 )( usa_ofs + ( usa_count * 2 ) ) > size || - ( size >> NTFS_BLOCK_SIZE_BITS ) != usa_count ) - { - errno = EINVAL; - ntfs_log_perror( "%s: magic: 0x%08x size: %d usa_ofs: %d " - "usa_count: %d", __FUNCTION__, *( le32 * )b, - size, usa_ofs, usa_count ); - return -1; - } - /* Position of usn in update sequence array. */ - usa_pos = ( u16* )b + usa_ofs / sizeof( u16 ); - /* - * The update sequence number which has to be equal to each of the - * u16 values before they are fixed up. Note no need to care for - * endianness since we are comparing and moving data for on disk - * structures which means the data is consistent. - If it is - * consistency the wrong endianness it doesn't make any difference. - */ - usn = *usa_pos; - /* - * Position in protected data of first u16 that needs fixing up. - */ - data_pos = ( u16* )b + NTFS_BLOCK_SIZE / sizeof( u16 ) - 1; - /* - * Check for incomplete multi sector transfer(s). - */ - while ( usa_count-- ) - { - if ( *data_pos != usn ) - { - /* - * Incomplete multi sector transfer detected! )-: - * Set the magic to "BAAD" and return failure. - * Note that magic_BAAD is already converted to le32. - */ - errno = EIO; - ntfs_log_perror( "Incomplete multi-sector transfer: " - "magic: 0x%08x size: %d usa_ofs: %d usa_count:" - " %d data: %d usn: %d", *( le32 * )b, size, - usa_ofs, usa_count, *data_pos, usn ); - b->magic = magic_BAAD; - return -1; - } - data_pos += NTFS_BLOCK_SIZE / sizeof( u16 ); - } - /* Re-setup the variables. */ - usa_count = le16_to_cpu( b->usa_count ) - 1; - data_pos = ( u16* )b + NTFS_BLOCK_SIZE / sizeof( u16 ) - 1; - /* Fixup all sectors. */ - while ( usa_count-- ) - { - /* - * Increment position in usa and restore original data from - * the usa into the data buffer. - */ - *data_pos = *( ++usa_pos ); - /* Increment position in data as well. */ - data_pos += NTFS_BLOCK_SIZE / sizeof( u16 ); - } - return 0; + /* Setup the variables. */ + usa_ofs = le16_to_cpu(b->usa_ofs); + /* Decrement usa_count to get number of fixups. */ + usa_count = le16_to_cpu(b->usa_count) - 1; + /* Size and alignment checks. */ + if (size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 || + (u32)(usa_ofs + (usa_count * 2)) > size || + (size >> NTFS_BLOCK_SIZE_BITS) != usa_count) { + errno = EINVAL; + ntfs_log_perror("%s: magic: 0x%08x size: %d usa_ofs: %d " + "usa_count: %d", __FUNCTION__, *(le32 *)b, + size, usa_ofs, usa_count); + return -1; + } + /* Position of usn in update sequence array. */ + usa_pos = (u16*)b + usa_ofs/sizeof(u16); + /* + * The update sequence number which has to be equal to each of the + * u16 values before they are fixed up. Note no need to care for + * endianness since we are comparing and moving data for on disk + * structures which means the data is consistent. - If it is + * consistency the wrong endianness it doesn't make any difference. + */ + usn = *usa_pos; + /* + * Position in protected data of first u16 that needs fixing up. + */ + data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; + /* + * Check for incomplete multi sector transfer(s). + */ + while (usa_count--) { + if (*data_pos != usn) { + /* + * Incomplete multi sector transfer detected! )-: + * Set the magic to "BAAD" and return failure. + * Note that magic_BAAD is already converted to le32. + */ + errno = EIO; + ntfs_log_perror("Incomplete multi-sector transfer: " + "magic: 0x%08x size: %d usa_ofs: %d usa_count:" + " %d data: %d usn: %d", *(le32 *)b, size, + usa_ofs, usa_count, *data_pos, usn); + b->magic = magic_BAAD; + return -1; + } + data_pos += NTFS_BLOCK_SIZE/sizeof(u16); + } + /* Re-setup the variables. */ + usa_count = le16_to_cpu(b->usa_count) - 1; + data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; + /* Fixup all sectors. */ + while (usa_count--) { + /* + * Increment position in usa and restore original data from + * the usa into the data buffer. + */ + *data_pos = *(++usa_pos); + /* Increment position in data as well. */ + data_pos += NTFS_BLOCK_SIZE/sizeof(u16); + } + return 0; } /** * ntfs_mst_pre_write_fixup - apply multi sector transfer protection - * @b: pointer to the data to protect - * @size: size in bytes of @b + * @b: pointer to the data to protect + * @size: size in bytes of @b * * Perform the necessary pre write multi sector transfer fixup on the data * pointer to by @b of @size. @@ -142,98 +138,94 @@ int ntfs_mst_post_read_fixup( NTFS_RECORD *b, const u32 size ) * otherwise a random word will be used (whatever was in the record at that * position at that time). */ -int ntfs_mst_pre_write_fixup( NTFS_RECORD *b, const u32 size ) +int ntfs_mst_pre_write_fixup(NTFS_RECORD *b, const u32 size) { - u16 usa_ofs, usa_count, usn; - u16 *usa_pos, *data_pos; + u16 usa_ofs, usa_count, usn; + u16 *usa_pos, *data_pos; - ntfs_log_trace( "Entering\n" ); + ntfs_log_trace("Entering\n"); - /* Sanity check + only fixup if it makes sense. */ - if ( !b || ntfs_is_baad_record( b->magic ) || - ntfs_is_hole_record( b->magic ) ) - { - errno = EINVAL; - ntfs_log_perror( "%s: bad argument", __FUNCTION__ ); - return -1; - } - /* Setup the variables. */ - usa_ofs = le16_to_cpu( b->usa_ofs ); - /* Decrement usa_count to get number of fixups. */ - usa_count = le16_to_cpu( b->usa_count ) - 1; - /* Size and alignment checks. */ - if ( size & ( NTFS_BLOCK_SIZE - 1 ) || usa_ofs & 1 || - ( u32 )( usa_ofs + ( usa_count * 2 ) ) > size || - ( size >> NTFS_BLOCK_SIZE_BITS ) != usa_count ) - { - errno = EINVAL; - ntfs_log_perror( "%s", __FUNCTION__ ); - return -1; - } - /* Position of usn in update sequence array. */ - usa_pos = ( u16* )( ( u8* )b + usa_ofs ); - /* - * Cyclically increment the update sequence number - * (skipping 0 and -1, i.e. 0xffff). - */ - usn = le16_to_cpup( usa_pos ) + 1; - if ( usn == 0xffff || !usn ) - usn = 1; - usn = cpu_to_le16( usn ); - *usa_pos = usn; - /* Position in data of first u16 that needs fixing up. */ - data_pos = ( u16* )b + NTFS_BLOCK_SIZE / sizeof( u16 ) - 1; - /* Fixup all sectors. */ - while ( usa_count-- ) - { - /* - * Increment the position in the usa and save the - * original data from the data buffer into the usa. - */ - *( ++usa_pos ) = *data_pos; - /* Apply fixup to data. */ - *data_pos = usn; - /* Increment position in data as well. */ - data_pos += NTFS_BLOCK_SIZE / sizeof( u16 ); - } - return 0; + /* Sanity check + only fixup if it makes sense. */ + if (!b || ntfs_is_baad_record(b->magic) || + ntfs_is_hole_record(b->magic)) { + errno = EINVAL; + ntfs_log_perror("%s: bad argument", __FUNCTION__); + return -1; + } + /* Setup the variables. */ + usa_ofs = le16_to_cpu(b->usa_ofs); + /* Decrement usa_count to get number of fixups. */ + usa_count = le16_to_cpu(b->usa_count) - 1; + /* Size and alignment checks. */ + if (size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 || + (u32)(usa_ofs + (usa_count * 2)) > size || + (size >> NTFS_BLOCK_SIZE_BITS) != usa_count) { + errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); + return -1; + } + /* Position of usn in update sequence array. */ + usa_pos = (u16*)((u8*)b + usa_ofs); + /* + * Cyclically increment the update sequence number + * (skipping 0 and -1, i.e. 0xffff). + */ + usn = le16_to_cpup(usa_pos) + 1; + if (usn == 0xffff || !usn) + usn = 1; + usn = cpu_to_le16(usn); + *usa_pos = usn; + /* Position in data of first u16 that needs fixing up. */ + data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; + /* Fixup all sectors. */ + while (usa_count--) { + /* + * Increment the position in the usa and save the + * original data from the data buffer into the usa. + */ + *(++usa_pos) = *data_pos; + /* Apply fixup to data. */ + *data_pos = usn; + /* Increment position in data as well. */ + data_pos += NTFS_BLOCK_SIZE/sizeof(u16); + } + return 0; } /** * ntfs_mst_post_write_fixup - deprotect multi sector transfer protected data - * @b: pointer to the data to deprotect + * @b: pointer to the data to deprotect * * Perform the necessary post write multi sector transfer fixup, not checking * for any errors, because we assume we have just used * ntfs_mst_pre_write_fixup(), thus the data will be fine or we would never * have gotten here. */ -void ntfs_mst_post_write_fixup( NTFS_RECORD *b ) +void ntfs_mst_post_write_fixup(NTFS_RECORD *b) { - u16 *usa_pos, *data_pos; + u16 *usa_pos, *data_pos; - u16 usa_ofs = le16_to_cpu( b->usa_ofs ); - u16 usa_count = le16_to_cpu( b->usa_count ) - 1; + u16 usa_ofs = le16_to_cpu(b->usa_ofs); + u16 usa_count = le16_to_cpu(b->usa_count) - 1; - ntfs_log_trace( "Entering\n" ); + ntfs_log_trace("Entering\n"); - /* Position of usn in update sequence array. */ - usa_pos = ( u16* )b + usa_ofs / sizeof( u16 ); + /* Position of usn in update sequence array. */ + usa_pos = (u16*)b + usa_ofs/sizeof(u16); - /* Position in protected data of first u16 that needs fixing up. */ - data_pos = ( u16* )b + NTFS_BLOCK_SIZE / sizeof( u16 ) - 1; + /* Position in protected data of first u16 that needs fixing up. */ + data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; - /* Fixup all sectors. */ - while ( usa_count-- ) - { - /* - * Increment position in usa and restore original data from - * the usa into the data buffer. - */ - *data_pos = *( ++usa_pos ); + /* Fixup all sectors. */ + while (usa_count--) { + /* + * Increment position in usa and restore original data from + * the usa into the data buffer. + */ + *data_pos = *(++usa_pos); - /* Increment position in data as well. */ - data_pos += NTFS_BLOCK_SIZE / sizeof( u16 ); - } + /* Increment position in data as well. */ + data_pos += NTFS_BLOCK_SIZE/sizeof(u16); + } } diff --git a/source/libntfs/mst.h b/source/libntfs/mst.h index a5d15ea5..ca813821 100644 --- a/source/libntfs/mst.h +++ b/source/libntfs/mst.h @@ -26,9 +26,9 @@ #include "types.h" #include "layout.h" -extern int ntfs_mst_post_read_fixup( NTFS_RECORD *b, const u32 size ); -extern int ntfs_mst_pre_write_fixup( NTFS_RECORD *b, const u32 size ); -extern void ntfs_mst_post_write_fixup( NTFS_RECORD *b ); +extern int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size); +extern int ntfs_mst_pre_write_fixup(NTFS_RECORD *b, const u32 size); +extern void ntfs_mst_post_write_fixup(NTFS_RECORD *b); #endif /* defined _NTFS_MST_H */ diff --git a/source/libntfs/ntfs.c b/source/libntfs/ntfs.c index 7d81c5f2..c2421c25 100644 --- a/source/libntfs/ntfs.c +++ b/source/libntfs/ntfs.c @@ -41,10 +41,9 @@ #include "cache.h" // NTFS device driver devoptab -static const devoptab_t devops_ntfs = -{ +static const devoptab_t devops_ntfs = { NULL, /* Device name */ - sizeof ( ntfs_file_state ), + sizeof (ntfs_file_state), ntfs_open_r, ntfs_close_r, ntfs_write_r, @@ -57,7 +56,7 @@ static const devoptab_t devops_ntfs = ntfs_chdir_r, ntfs_rename_r, ntfs_mkdir_r, - sizeof ( ntfs_dir_state ), + sizeof (ntfs_dir_state), ntfs_diropen_r, ntfs_dirreset_r, ntfs_dirnext_r, @@ -68,21 +67,20 @@ static const devoptab_t devops_ntfs = NULL /* Device data */ }; -void ntfsInit ( void ) +void ntfsInit (void) { static bool isInit = false; // Initialise ntfs-3g (if not already done so) - if ( !isInit ) - { + if (!isInit) { isInit = true; // Set the log handler -#ifdef NTFS_ENABLE_LOG - ntfs_log_set_handler( ntfs_log_handler_stderr ); -#else - ntfs_log_set_handler( ntfs_log_handler_null ); -#endif + #ifdef NTFS_ENABLE_LOG + ntfs_log_set_handler(ntfs_log_handler_stderr); + #else + ntfs_log_set_handler(ntfs_log_handler_null); + #endif // Set our current local ntfs_set_locale(); @@ -91,7 +89,7 @@ void ntfsInit ( void ) return; } -int ntfsFindPartitions ( const DISC_INTERFACE *interface, sec_t **partitions ) +int ntfsFindPartitions (const DISC_INTERFACE *interface, sec_t **partitions) { MASTER_BOOT_RECORD mbr; PARTITION_RECORD *partition = NULL; @@ -100,8 +98,7 @@ int ntfsFindPartitions ( const DISC_INTERFACE *interface, sec_t **partitions ) sec_t part_lba = 0; int i; - union - { + union { u8 buffer[512]; MASTER_BOOT_RECORD mbr; EXTENDED_BOOT_RECORD ebr; @@ -109,191 +106,156 @@ int ntfsFindPartitions ( const DISC_INTERFACE *interface, sec_t **partitions ) } sector; // Sanity check - if ( !interface ) - { + if (!interface) { errno = EINVAL; return -1; } - if ( !partitions ) + if (!partitions) return 0; // Initialise ntfs-3g ntfsInit(); // Start the device and check that it is inserted - if ( !interface->startup() ) - { + if (!interface->startup()) { errno = EIO; return -1; } - if ( !interface->isInserted() ) - { + if (!interface->isInserted()) { return 0; } // Read the first sector on the device - if ( !interface->readSectors( 0, 1, §or.buffer ) ) - { + if (!interface->readSectors(0, 1, §or.buffer)) { errno = EIO; return -1; } // If this is the devices master boot record - if ( sector.mbr.signature == MBR_SIGNATURE ) - { - memcpy( &mbr, §or, sizeof( MASTER_BOOT_RECORD ) ); - ntfs_log_debug( "Valid Master Boot Record found\n" ); + if (sector.mbr.signature == MBR_SIGNATURE) { + memcpy(&mbr, §or, sizeof(MASTER_BOOT_RECORD)); + ntfs_log_debug("Valid Master Boot Record found\n"); // Search the partition table for all NTFS partitions (max. 4 primary partitions) - for ( i = 0; i < 4; i++ ) - { + for (i = 0; i < 4; i++) { partition = &mbr.partitions[i]; - part_lba = le32_to_cpu( mbr.partitions[i].lba_start ); + part_lba = le32_to_cpu(mbr.partitions[i].lba_start); - ntfs_log_debug( "Partition %i: %s, sector %d, type 0x%x\n", i + 1, - partition->status == PARTITION_STATUS_BOOTABLE ? "bootable (active)" : "non-bootable", - part_lba, partition->type ); + ntfs_log_debug("Partition %i: %s, sector %d, type 0x%x\n", i + 1, + partition->status == PARTITION_STATUS_BOOTABLE ? "bootable (active)" : "non-bootable", + part_lba, partition->type); // Figure out what type of partition this is - switch ( partition->type ) - { + switch (partition->type) { - // Ignore empty partitions + // Ignore empty partitions case PARTITION_TYPE_EMPTY: continue; - // NTFS partition - case PARTITION_TYPE_NTFS: - { - ntfs_log_debug( "Partition %i: Claims to be NTFS\n", i + 1 ); + // NTFS partition + case PARTITION_TYPE_NTFS: { + ntfs_log_debug("Partition %i: Claims to be NTFS\n", i + 1); - // Read and validate the NTFS partition - if ( interface->readSectors( part_lba, 1, §or ) ) - { - if ( sector.boot.oem_id == NTFS_OEM_ID ) - { - ntfs_log_debug( "Partition %i: Valid NTFS boot sector found\n", i + 1 ); - if ( partition_count < NTFS_MAX_PARTITIONS ) - { - partition_starts[partition_count] = part_lba; - partition_count++; - } - } - else - { - ntfs_log_debug( "Partition %i: Invalid NTFS boot sector, not actually NTFS\n", i + 1 ); + // Read and validate the NTFS partition + if (interface->readSectors(part_lba, 1, §or)) { + if (sector.boot.oem_id == NTFS_OEM_ID) { + ntfs_log_debug("Partition %i: Valid NTFS boot sector found\n", i + 1); + if (partition_count < NTFS_MAX_PARTITIONS) { + partition_starts[partition_count] = part_lba; + partition_count++; } + } else { + ntfs_log_debug("Partition %i: Invalid NTFS boot sector, not actually NTFS\n", i + 1); } - - break; - } - // DOS 3.3+ or Windows 95 extended partition + break; + + } + + // DOS 3.3+ or Windows 95 extended partition case PARTITION_TYPE_DOS33_EXTENDED: - case PARTITION_TYPE_WIN95_EXTENDED: - { - ntfs_log_debug( "Partition %i: Claims to be Extended\n", i + 1 ); + case PARTITION_TYPE_WIN95_EXTENDED: { + ntfs_log_debug("Partition %i: Claims to be Extended\n", i + 1); - // Walk the extended partition chain, finding all NTFS partitions within it - sec_t ebr_lba = part_lba; - sec_t next_erb_lba = 0; - do - { + // Walk the extended partition chain, finding all NTFS partitions within it + sec_t ebr_lba = part_lba; + sec_t next_erb_lba = 0; + do { - // Read and validate the extended boot record - if ( interface->readSectors( ebr_lba + next_erb_lba, 1, §or ) ) - { - if ( sector.ebr.signature == EBR_SIGNATURE ) - { - ntfs_log_debug( "Logical Partition @ %d: type 0x%x\n", ebr_lba + next_erb_lba, - sector.ebr.partition.status == PARTITION_STATUS_BOOTABLE ? "bootable (active)" : "non-bootable", - sector.ebr.partition.type ); + // Read and validate the extended boot record + if (interface->readSectors(ebr_lba + next_erb_lba, 1, §or)) { + if (sector.ebr.signature == EBR_SIGNATURE) { + ntfs_log_debug("Logical Partition @ %d: type 0x%x\n", ebr_lba + next_erb_lba, + sector.ebr.partition.status == PARTITION_STATUS_BOOTABLE ? "bootable (active)" : "non-bootable", + sector.ebr.partition.type); - // Get the start sector of the current partition - // and the next extended boot record in the chain - part_lba = ebr_lba + next_erb_lba + le32_to_cpu( sector.ebr.partition.lba_start ); - next_erb_lba = le32_to_cpu( sector.ebr.next_ebr.lba_start ); + // Get the start sector of the current partition + // and the next extended boot record in the chain + part_lba = ebr_lba + next_erb_lba + le32_to_cpu(sector.ebr.partition.lba_start); + next_erb_lba = le32_to_cpu(sector.ebr.next_ebr.lba_start); - // Check if this partition has a valid NTFS boot record - if ( interface->readSectors( part_lba, 1, §or ) ) - { - if ( sector.boot.oem_id == NTFS_OEM_ID ) - { - ntfs_log_debug( "Logical Partition @ %d: Valid NTFS boot sector found\n", part_lba ); - if ( sector.ebr.partition.type != PARTITION_TYPE_NTFS ) - { - ntfs_log_warning( "Logical Partition @ %d: Is NTFS but type is 0x%x; 0x%x was expected\n", part_lba, sector.ebr.partition.type, PARTITION_TYPE_NTFS ); - } - if ( partition_count < NTFS_MAX_PARTITIONS ) - { - partition_starts[partition_count] = part_lba; - partition_count++; - } + // Check if this partition has a valid NTFS boot record + if (interface->readSectors(part_lba, 1, §or)) { + if (sector.boot.oem_id == NTFS_OEM_ID) { + ntfs_log_debug("Logical Partition @ %d: Valid NTFS boot sector found\n", part_lba); + if(sector.ebr.partition.type != PARTITION_TYPE_NTFS) { + ntfs_log_warning("Logical Partition @ %d: Is NTFS but type is 0x%x; 0x%x was expected\n", part_lba, sector.ebr.partition.type, PARTITION_TYPE_NTFS); + } + if (partition_count < NTFS_MAX_PARTITIONS) { + partition_starts[partition_count] = part_lba; + partition_count++; } } - } - else - { - next_erb_lba = 0; - } - } - } - while ( next_erb_lba ); - - break; - - } - - // Unknown or unsupported partition type - default: - { - - // Check if this partition has a valid NTFS boot record anyway, - // it might be misrepresented due to a lazy partition editor - if ( interface->readSectors( part_lba, 1, §or ) ) - { - if ( sector.boot.oem_id == NTFS_OEM_ID ) - { - ntfs_log_debug( "Partition %i: Valid NTFS boot sector found\n", i + 1 ); - if ( partition->type != PARTITION_TYPE_NTFS ) - { - ntfs_log_warning( "Partition %i: Is NTFS but type is 0x%x; 0x%x was expected\n", i + 1, partition->type, PARTITION_TYPE_NTFS ); - } - if ( partition_count < NTFS_MAX_PARTITIONS ) - { - partition_starts[partition_count] = part_lba; - partition_count++; - } + } else { + next_erb_lba = 0; } } - break; + } while (next_erb_lba); + break; + + } + + // Unknown or unsupported partition type + default: { + + // Check if this partition has a valid NTFS boot record anyway, + // it might be misrepresented due to a lazy partition editor + if (interface->readSectors(part_lba, 1, §or)) { + if (sector.boot.oem_id == NTFS_OEM_ID) { + ntfs_log_debug("Partition %i: Valid NTFS boot sector found\n", i + 1); + if(partition->type != PARTITION_TYPE_NTFS) { + ntfs_log_warning("Partition %i: Is NTFS but type is 0x%x; 0x%x was expected\n", i + 1, partition->type, PARTITION_TYPE_NTFS); + } + if (partition_count < NTFS_MAX_PARTITIONS) { + partition_starts[partition_count] = part_lba; + partition_count++; + } + } } + break; + + } + } } - // Else it is assumed this device has no master boot record - } - else - { - ntfs_log_debug( "No Master Boot Record was found!\n" ); + // Else it is assumed this device has no master boot record + } else { + ntfs_log_debug("No Master Boot Record was found!\n"); // As a last-ditched effort, search the first 64 sectors of the device for stray NTFS partitions - for ( i = 0; i < 64; i++ ) - { - if ( interface->readSectors( i, 1, §or ) ) - { - if ( sector.boot.oem_id == NTFS_OEM_ID ) - { - ntfs_log_debug( "Valid NTFS boot sector found at sector %d!\n", i ); - if ( partition_count < NTFS_MAX_PARTITIONS ) - { + for (i = 0; i < 64; i++) { + if (interface->readSectors(i, 1, §or)) { + if (sector.boot.oem_id == NTFS_OEM_ID) { + ntfs_log_debug("Valid NTFS boot sector found at sector %d!\n", i); + if (partition_count < NTFS_MAX_PARTITIONS) { partition_starts[partition_count] = i; partition_count++; } @@ -307,12 +269,10 @@ int ntfsFindPartitions ( const DISC_INTERFACE *interface, sec_t **partitions ) /*interface->shutdown();*/ // Return the found partitions (if any) - if ( partition_count > 0 ) - { - *partitions = ( sec_t* )ntfs_alloc( sizeof( sec_t ) * partition_count ); - if ( *partitions ) - { - memcpy( *partitions, &partition_starts, sizeof( sec_t ) * partition_count ); + if (partition_count > 0) { + *partitions = (sec_t*)ntfs_alloc(sizeof(sec_t) * partition_count); + if (*partitions) { + memcpy(*partitions, &partition_starts, sizeof(sec_t) * partition_count); return partition_count; } } @@ -320,7 +280,7 @@ int ntfsFindPartitions ( const DISC_INTERFACE *interface, sec_t **partitions ) return 0; } -int ntfsMountAll ( ntfs_md **mounts, u32 flags ) +int ntfsMountAll (ntfs_md **mounts, u32 flags) { const INTERFACE_ID *discs = ntfsGetDiscInterfaces(); const INTERFACE_ID *disc = NULL; @@ -335,34 +295,26 @@ int ntfsMountAll ( ntfs_md **mounts, u32 flags ) ntfsInit(); // Find and mount all NTFS partitions on all known devices - for ( i = 0; discs[i].name != NULL && discs[i].interface != NULL; i++ ) - { + for (i = 0; discs[i].name != NULL && discs[i].interface != NULL; i++) { disc = &discs[i]; - partition_count = ntfsFindPartitions( disc->interface, &partitions ); - if ( partition_count > 0 && partitions ) - { - for ( j = 0, k = 0; j < partition_count; j++ ) - { + partition_count = ntfsFindPartitions(disc->interface, &partitions); + if (partition_count > 0 && partitions) { + for (j = 0, k = 0; j < partition_count; j++) { // Find the next unused mount name - do - { - sprintf( name, "%s%i", NTFS_MOUNT_PREFIX, k++ ); - if ( k >= NTFS_MAX_MOUNTS ) - { - ntfs_free( partitions ); + do { + sprintf(name, "%s%i", NTFS_MOUNT_PREFIX, k++); + if (k >= NTFS_MAX_MOUNTS) { + ntfs_free(partitions); errno = EADDRNOTAVAIL; return -1; } - } - while ( ntfsGetDevice( name, false ) ); + } while (ntfsGetDevice(name, false)); // Mount the partition - if ( mount_count < NTFS_MAX_MOUNTS ) - { - if ( ntfsMount( name, disc->interface, partitions[j], CACHE_DEFAULT_PAGE_SIZE, CACHE_DEFAULT_PAGE_COUNT, flags ) ) - { - strcpy( mount_points[mount_count].name, name ); + if (mount_count < NTFS_MAX_MOUNTS) { + if (ntfsMount(name, disc->interface, partitions[j], CACHE_DEFAULT_PAGE_SIZE, CACHE_DEFAULT_PAGE_COUNT, flags)) { + strcpy(mount_points[mount_count].name, name); mount_points[mount_count].interface = disc->interface; mount_points[mount_count].startSector = partitions[j]; mount_count++; @@ -370,17 +322,15 @@ int ntfsMountAll ( ntfs_md **mounts, u32 flags ) } } - ntfs_free( partitions ); + ntfs_free(partitions); } } // Return the mounts (if any) - if ( mount_count > 0 && mounts ) - { - *mounts = ( ntfs_md* )ntfs_alloc( sizeof( ntfs_md ) * mount_count ); - if ( *mounts ) - { - memcpy( *mounts, &mount_points, sizeof( ntfs_md ) * mount_count ); + if (mount_count > 0 && mounts) { + *mounts = (ntfs_md*)ntfs_alloc(sizeof(ntfs_md) * mount_count); + if (*mounts) { + memcpy(*mounts, &mount_points, sizeof(ntfs_md) * mount_count); return mount_count; } } @@ -388,7 +338,7 @@ int ntfsMountAll ( ntfs_md **mounts, u32 flags ) return 0; } -int ntfsMountDevice ( const DISC_INTERFACE *interface, ntfs_md **mounts, u32 flags ) +int ntfsMountDevice (const DISC_INTERFACE *interface, ntfs_md **mounts, u32 flags) { const INTERFACE_ID *discs = ntfsGetDiscInterfaces(); const INTERFACE_ID *disc = NULL; @@ -400,8 +350,7 @@ int ntfsMountDevice ( const DISC_INTERFACE *interface, ntfs_md **mounts, u32 fla int i, j, k; // Sanity check - if ( !interface ) - { + if (!interface) { errno = EINVAL; return -1; } @@ -410,36 +359,27 @@ int ntfsMountDevice ( const DISC_INTERFACE *interface, ntfs_md **mounts, u32 fla ntfsInit(); // Find the specified device then find and mount all NTFS partitions on it - for ( i = 0; discs[i].name != NULL && discs[i].interface != NULL; i++ ) - { - if ( discs[i].interface == interface ) - { + for (i = 0; discs[i].name != NULL && discs[i].interface != NULL; i++) { + if (discs[i].interface == interface) { disc = &discs[i]; - partition_count = ntfsFindPartitions( disc->interface, &partitions ); - if ( partition_count > 0 && partitions ) - { - for ( j = 0, k = 0; j < partition_count; j++ ) - { + partition_count = ntfsFindPartitions(disc->interface, &partitions); + if (partition_count > 0 && partitions) { + for (j = 0, k = 0; j < partition_count; j++) { // Find the next unused mount name - do - { - sprintf( name, "%s%i", NTFS_MOUNT_PREFIX, k++ ); - if ( k >= NTFS_MAX_MOUNTS ) - { - ntfs_free( partitions ); + do { + sprintf(name, "%s%i", NTFS_MOUNT_PREFIX, k++); + if (k >= NTFS_MAX_MOUNTS) { + ntfs_free(partitions); errno = EADDRNOTAVAIL; return -1; } - } - while ( ntfsGetDevice( name, false ) ); + } while (ntfsGetDevice(name, false)); // Mount the partition - if ( mount_count < NTFS_MAX_MOUNTS ) - { - if ( ntfsMount( name, disc->interface, partitions[j], CACHE_DEFAULT_PAGE_SIZE, CACHE_DEFAULT_PAGE_COUNT, flags ) ) - { - strcpy( mount_points[mount_count].name, name ); + if (mount_count < NTFS_MAX_MOUNTS) { + if (ntfsMount(name, disc->interface, partitions[j], CACHE_DEFAULT_PAGE_SIZE, CACHE_DEFAULT_PAGE_COUNT, flags)) { + strcpy(mount_points[mount_count].name, name); mount_points[mount_count].interface = disc->interface; mount_points[mount_count].startSector = partitions[j]; mount_count++; @@ -447,26 +387,23 @@ int ntfsMountDevice ( const DISC_INTERFACE *interface, ntfs_md **mounts, u32 fla } } - ntfs_free( partitions ); + ntfs_free(partitions); } break; } } // If we couldn't find the device then return with error status - if ( !disc ) - { + if (!disc) { errno = ENODEV; return -1; } // Return the mounts (if any) - if ( mount_count > 0 && mounts ) - { - *mounts = ( ntfs_md* )ntfs_alloc( sizeof( ntfs_md ) * mount_count ); - if ( *mounts ) - { - memcpy( *mounts, &mount_points, sizeof( ntfs_md ) * mount_count ); + if (mount_count > 0 && mounts) { + *mounts = (ntfs_md*)ntfs_alloc(sizeof(ntfs_md) * mount_count); + if (*mounts) { + memcpy(*mounts, &mount_points, sizeof(ntfs_md) * mount_count); return mount_count; } } @@ -474,14 +411,13 @@ int ntfsMountDevice ( const DISC_INTERFACE *interface, ntfs_md **mounts, u32 fla return 0; } -bool ntfsMount ( const char *name, const DISC_INTERFACE *interface, sec_t startSector, u32 cachePageCount, u32 cachePageSize, u32 flags ) +bool ntfsMount (const char *name, const DISC_INTERFACE *interface, sec_t startSector, u32 cachePageCount, u32 cachePageSize, u32 flags) { ntfs_vd *vd = NULL; gekko_fd *fd = NULL; // Sanity check - if ( !name || !interface ) - { + if (!name || !interface) { errno = EINVAL; return false; } @@ -490,23 +426,20 @@ bool ntfsMount ( const char *name, const DISC_INTERFACE *interface, sec_t startS ntfsInit(); // Check that the requested mount name is free - if ( ntfsGetDevice( name, false ) ) - { + if (ntfsGetDevice(name, false)) { errno = EADDRINUSE; return false; } // Check that we can at least read from this device - if ( !( interface->features & FEATURE_MEDIUM_CANREAD ) ) - { + if (!(interface->features & FEATURE_MEDIUM_CANREAD)) { errno = EPERM; return false; } // Allocate the volume descriptor - vd = ( ntfs_vd* )ntfs_alloc( sizeof( ntfs_vd ) ); - if ( !vd ) - { + vd = (ntfs_vd*)ntfs_alloc(sizeof(ntfs_vd)); + if (!vd) { errno = ENOMEM; return false; } @@ -518,15 +451,14 @@ bool ntfsMount ( const char *name, const DISC_INTERFACE *interface, sec_t startS vd->gid = 0; vd->fmask = 0; vd->dmask = 0; - vd->atime = ( ( flags & NTFS_UPDATE_ACCESS_TIMES ) ? ATIME_ENABLED : ATIME_DISABLED ); - vd->showHiddenFiles = ( flags & NTFS_SHOW_HIDDEN_FILES ); - vd->showSystemFiles = ( flags & NTFS_SHOW_SYSTEM_FILES ); + vd->atime = ((flags & NTFS_UPDATE_ACCESS_TIMES) ? ATIME_ENABLED : ATIME_DISABLED); + vd->showHiddenFiles = (flags & NTFS_SHOW_HIDDEN_FILES); + vd->showSystemFiles = (flags & NTFS_SHOW_SYSTEM_FILES); // Allocate the device driver descriptor - fd = ( gekko_fd* )ntfs_alloc( sizeof( gekko_fd ) ); - if ( !fd ) - { - ntfs_free( vd ); + fd = (gekko_fd*)ntfs_alloc(sizeof(gekko_fd)); + if (!fd) { + ntfs_free(vd); errno = ENOMEM; return false; } @@ -540,94 +472,92 @@ bool ntfsMount ( const char *name, const DISC_INTERFACE *interface, sec_t startS fd->cachePageSize = cachePageSize; // Allocate the device driver - vd->dev = ntfs_device_alloc( name, 0, &ntfs_device_gekko_io_ops, fd ); - if ( !vd->dev ) - { - ntfs_free( fd ); - ntfs_free( vd ); + vd->dev = ntfs_device_alloc(name, 0, &ntfs_device_gekko_io_ops, fd); + if (!vd->dev) { + ntfs_free(fd); + ntfs_free(vd); return false; } // Build the mount flags - if ( flags & NTFS_READ_ONLY ) - vd->flags |= MS_RDONLY; + if (flags & NTFS_READ_ONLY) + vd->flags |= MS_RDONLY; else { - if ( !( interface->features & FEATURE_MEDIUM_CANWRITE ) ) - vd->flags |= MS_RDONLY; - if ( ( interface->features & FEATURE_MEDIUM_CANREAD ) && ( interface->features & FEATURE_MEDIUM_CANWRITE ) ) - vd->flags |= MS_EXCLUSIVE; + if (!(interface->features & FEATURE_MEDIUM_CANWRITE)) + vd->flags |= MS_RDONLY; + if ((interface->features & FEATURE_MEDIUM_CANREAD) && (interface->features & FEATURE_MEDIUM_CANWRITE)) + vd->flags |= MS_EXCLUSIVE; } - if ( flags & NTFS_RECOVER ) + if (flags & NTFS_RECOVER) vd->flags |= MS_RECOVER; - if ( flags & NTFS_IGNORE_HIBERFILE ) + if (flags & NTFS_IGNORE_HIBERFILE) vd->flags |= MS_IGNORE_HIBERFILE; - if ( vd->flags & MS_RDONLY ) - ntfs_log_debug( "Mounting \"%s\" as read-only\n", name ); + if (vd->flags & MS_RDONLY) + ntfs_log_debug("Mounting \"%s\" as read-only\n", name); // Mount the device - vd->vol = ntfs_device_mount( vd->dev, vd->flags ); - if ( !vd->vol ) - { - switch ( ntfs_volume_error( errno ) ) - { + vd->vol = ntfs_device_mount(vd->dev, vd->flags); + if (!vd->vol) { + switch(ntfs_volume_error(errno)) { case NTFS_VOLUME_NOT_NTFS: errno = EINVALPART; break; case NTFS_VOLUME_CORRUPT: errno = EINVALPART; break; case NTFS_VOLUME_HIBERNATED: errno = EHIBERNATED; break; case NTFS_VOLUME_UNCLEAN_UNMOUNT: errno = EDIRTY; break; default: errno = EINVAL; break; } - ntfs_device_free( vd->dev ); - ntfs_free( vd ); + ntfs_device_free(vd->dev); + ntfs_free(vd); return false; } + if (flags & NTFS_IGNORE_CASE) + ntfs_set_ignore_case(vd->vol); + // Initialise the volume descriptor - if ( ntfsInitVolume( vd ) ) - { - ntfs_umount( vd->vol, true ); - ntfs_free( vd ); + if (ntfsInitVolume(vd)) { + ntfs_umount(vd->vol, true); + ntfs_free(vd); return false; } // Add the device to the devoptab table - if ( ntfsAddDevice( name, vd ) ) - { - ntfsDeinitVolume( vd ); - ntfs_umount( vd->vol, true ); - ntfs_free( vd ); + if (ntfsAddDevice(name, vd)) { + ntfsDeinitVolume(vd); + ntfs_umount(vd->vol, true); + ntfs_free(vd); return false; } return true; } -void ntfsUnmount ( const char *name, bool force ) +void ntfsUnmount (const char *name, bool force) { ntfs_vd *vd = NULL; // Get the devices volume descriptor - vd = ntfsGetVolume( name ); - if ( !vd ) + vd = ntfsGetVolume(name); + if (!vd) return; // Remove the device from the devoptab table - ntfsRemoveDevice( name ); + ntfsRemoveDevice(name); // Deinitialise the volume descriptor - ntfsDeinitVolume( vd ); + ntfsDeinitVolume(vd); // Unmount the volume - ntfs_umount( vd->vol, force ); + ntfs_umount(vd->vol, force); // Free the volume descriptor - ntfs_free( vd ); + ntfs_free(vd); return; } -const char *ntfsGetVolumeName ( const char *name ) +const char *ntfsGetVolumeName (const char *name) { ntfs_vd *vd = NULL; //ntfs_attr *na = NULL; @@ -635,81 +565,79 @@ const char *ntfsGetVolumeName ( const char *name ) //char *volumeName = NULL; // Sanity check - if ( !name ) - { + if (!name) { errno = EINVAL; return NULL; } // Get the devices volume descriptor - vd = ntfsGetVolume( name ); - if ( !vd ) - { + vd = ntfsGetVolume(name); + if (!vd) { errno = ENODEV; return NULL; } return vd->vol->vol_name; - /* - - // If the volume name has already been cached then just use that - if (vd->name[0]) - return vd->name; - - // Lock - ntfsLock(vd); - - // Check if the volume name attribute exists - na = ntfs_attr_open(vd->vol->vol_ni, AT_VOLUME_NAME, NULL, 0); - if (!na) { - ntfsUnlock(vd); - errno = ENOENT; - return false; - } - - // Allocate a buffer to store the raw volume name - ulabel = ntfs_alloc(na->data_size * sizeof(ntfschar)); - if (!ulabel) { - ntfsUnlock(vd); - errno = ENOMEM; - return false; - } - - // Read the volume name - if (ntfs_attr_pread(na, 0, na->data_size, ulabel) != na->data_size) { - ntfs_free(ulabel); - ntfsUnlock(vd); - errno = EIO; - return false; - } - - // Convert the volume name to the current local - if (ntfsUnicodeToLocal(ulabel, na->data_size, &volumeName, 0) < 0) { - errno = EINVAL; - ntfs_free(ulabel); - ntfsUnlock(vd); - return false; - } - - // If the volume name was read then cache it (for future fetches) - if (volumeName) - strcpy(vd->name, volumeName); - - // Close the volume name attribute - if (na) - ntfs_attr_close(na); - - // Clean up - ntfs_free(volumeName); - ntfs_free(ulabel); - - // Unlock - ntfsUnlock(vd); +/* + // If the volume name has already been cached then just use that + if (vd->name[0]) return vd->name; - */ + + // Lock + ntfsLock(vd); + + // Check if the volume name attribute exists + na = ntfs_attr_open(vd->vol->vol_ni, AT_VOLUME_NAME, NULL, 0); + if (!na) { + ntfsUnlock(vd); + errno = ENOENT; + return false; + } + + // Allocate a buffer to store the raw volume name + ulabel = ntfs_alloc(na->data_size * sizeof(ntfschar)); + if (!ulabel) { + ntfsUnlock(vd); + errno = ENOMEM; + return false; + } + + // Read the volume name + if (ntfs_attr_pread(na, 0, na->data_size, ulabel) != na->data_size) { + ntfs_free(ulabel); + ntfsUnlock(vd); + errno = EIO; + return false; + } + + // Convert the volume name to the current local + if (ntfsUnicodeToLocal(ulabel, na->data_size, &volumeName, 0) < 0) { + errno = EINVAL; + ntfs_free(ulabel); + ntfsUnlock(vd); + return false; + } + + // If the volume name was read then cache it (for future fetches) + if (volumeName) + strcpy(vd->name, volumeName); + + // Close the volume name attribute + if (na) + ntfs_attr_close(na); + + // Clean up + ntfs_free(volumeName); + ntfs_free(ulabel); + + // Unlock + ntfsUnlock(vd); + + return vd->name; +*/ } -bool ntfsSetVolumeName ( const char *name, const char *volumeName ) +bool ntfsSetVolumeName (const char *name, const char *volumeName) { ntfs_vd *vd = NULL; ntfs_attr *na = NULL; @@ -717,62 +645,53 @@ bool ntfsSetVolumeName ( const char *name, const char *volumeName ) int ulabel_len; // Sanity check - if ( !name ) - { + if (!name) { errno = EINVAL; return false; } // Get the devices volume descriptor - vd = ntfsGetVolume( name ); - if ( !vd ) - { + vd = ntfsGetVolume(name); + if (!vd) { errno = ENODEV; return false; } // Lock - ntfsLock( vd ); + ntfsLock(vd); // Convert the new volume name to unicode - ulabel_len = ntfsLocalToUnicode( volumeName, &ulabel ) * sizeof( ntfschar ); - if ( ulabel_len < 0 ) - { - ntfsUnlock( vd ); + ulabel_len = ntfsLocalToUnicode(volumeName, &ulabel) * sizeof(ntfschar); + if (ulabel_len < 0) { + ntfsUnlock(vd); errno = EINVAL; return false; } // Check if the volume name attribute exists - na = ntfs_attr_open( vd->vol->vol_ni, AT_VOLUME_NAME, NULL, 0 ); - if ( na ) - { + na = ntfs_attr_open(vd->vol->vol_ni, AT_VOLUME_NAME, NULL, 0); + if (na) { // It does, resize it to match the length of the new volume name - if ( ntfs_attr_truncate( na, ulabel_len ) ) - { - ntfs_free( ulabel ); - ntfsUnlock( vd ); + if (ntfs_attr_truncate(na, ulabel_len)) { + ntfs_free(ulabel); + ntfsUnlock(vd); return false; } // Write the new volume name - if ( ntfs_attr_pwrite( na, 0, ulabel_len, ulabel ) != ulabel_len ) - { - ntfs_free( ulabel ); - ntfsUnlock( vd ); + if (ntfs_attr_pwrite(na, 0, ulabel_len, ulabel) != ulabel_len) { + ntfs_free(ulabel); + ntfsUnlock(vd); return false; } - } - else - { + } else { // It doesn't, create it now - if ( ntfs_attr_add( vd->vol->vol_ni, AT_VOLUME_NAME, NULL, 0, ( u8* )ulabel, ulabel_len ) ) - { - ntfs_free( ulabel ); - ntfsUnlock( vd ); + if (ntfs_attr_add(vd->vol->vol_ni, AT_VOLUME_NAME, NULL, 0, (u8*)ulabel, ulabel_len)) { + ntfs_free(ulabel); + ntfsUnlock(vd); return false; } @@ -782,27 +701,26 @@ bool ntfsSetVolumeName ( const char *name, const char *volumeName ) vd->name[0] = '\0'; // Close the volume name attribute - if ( na ) - ntfs_attr_close( na ); + if (na) + ntfs_attr_close(na); // Sync the volume node - if ( ntfs_inode_sync( vd->vol->vol_ni ) ) - { - ntfs_free( ulabel ); - ntfsUnlock( vd ); + if (ntfs_inode_sync(vd->vol->vol_ni)) { + ntfs_free(ulabel); + ntfsUnlock(vd); return false; } // Clean up - ntfs_free( ulabel ); + ntfs_free(ulabel); // Unlock - ntfsUnlock( vd ); + ntfsUnlock(vd); return true; } -const devoptab_t *ntfsGetDevOpTab ( void ) +const devoptab_t *ntfsGetDevOpTab (void) { return &devops_ntfs; } diff --git a/source/libntfs/ntfs.h b/source/libntfs/ntfs.h index 5cf81b75..22474232 100644 --- a/source/libntfs/ntfs.h +++ b/source/libntfs/ntfs.h @@ -23,25 +23,24 @@ #define _LIBNTFS_H #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif #include #include #include - /* NTFS errno values */ +/* NTFS errno values */ #define ENOPART 3000 /* No partition was found */ #define EINVALPART 3001 /* Specified partition is invalid or not supported */ #define EDIRTY 3002 /* Volume is dirty and NTFS_RECOVER was not specified during mount */ #define EHIBERNATED 3003 /* Volume is hibernated and NTFS_IGNORE_HIBERFILE was not specified during mount */ - /* NTFS cache options */ +/* NTFS cache options */ #define CACHE_DEFAULT_PAGE_COUNT 8 /* The default number of pages in the cache */ #define CACHE_DEFAULT_PAGE_SIZE 128 /* The default number of sectors per cache page */ - /* NTFS mount flags */ +/* NTFS mount flags */ #define NTFS_DEFAULT 0x00000000 /* Standard mount, expects a clean, non-hibernated volume */ #define NTFS_SHOW_HIDDEN_FILES 0x00000001 /* Display hidden files when enumerating directories */ #define NTFS_SHOW_SYSTEM_FILES 0x00000002 /* Display system files when enumerating directories */ @@ -49,100 +48,97 @@ extern "C" #define NTFS_RECOVER 0x00000008 /* Reset $LogFile if dirty (i.e. from unclean disconnect) */ #define NTFS_IGNORE_HIBERFILE 0x00000010 /* Mount even if volume is hibernated */ #define NTFS_READ_ONLY 0x00000020 /* Mount in read only mode */ -#define NTFS_SU NTFS_SHOW_HIDDEN_FILES & NTFS_SHOW_SYSTEM_FILES -#define NTFS_FORCE NTFS_RECOVER & NTFS_IGNORE_HIBERFILE +#define NTFS_IGNORE_CASE 0x00000040 /* Ignore case sensitivity. Everything must be and will be provided in lowercase. */ +#define NTFS_SU NTFS_SHOW_HIDDEN_FILES | NTFS_SHOW_SYSTEM_FILES +#define NTFS_FORCE NTFS_RECOVER | NTFS_IGNORE_HIBERFILE - /** - * ntfs_md - NTFS mount descriptor - */ - typedef struct _ntfs_md - { - char name[32]; /* Mount name (can be accessed as "name:/") */ - const DISC_INTERFACE *interface; /* Block device containing the mounted partition */ - sec_t startSector; /* Local block address to first sector of partition */ - } ntfs_md; +/** + * ntfs_md - NTFS mount descriptor + */ +typedef struct _ntfs_md { + char name[32]; /* Mount name (can be accessed as "name:/") */ + const DISC_INTERFACE *interface; /* Block device containing the mounted partition */ + sec_t startSector; /* Local block address to first sector of partition */ +} ntfs_md; - /** - * Find all NTFS partitions on a block device. - * - * @param INTERFACE The block device to search - * @param PARTITIONS (out) A pointer to receive the array of partition start sectors - * - * @return The number of entries in PARTITIONS or -1 if an error occurred (see errno) - * @note The caller is responsible for freeing PARTITIONS when finished with it - */ - extern int ntfsFindPartitions ( const DISC_INTERFACE *interface, sec_t **partitions ); +/** + * Find all NTFS partitions on a block device. + * + * @param INTERFACE The block device to search + * @param PARTITIONS (out) A pointer to receive the array of partition start sectors + * + * @return The number of entries in PARTITIONS or -1 if an error occurred (see errno) + * @note The caller is responsible for freeing PARTITIONS when finished with it + */ +extern int ntfsFindPartitions (const DISC_INTERFACE *interface, sec_t **partitions); - /** - * Mount all NTFS partitions on all inserted block devices. - * - * @param MOUNTS (out) A pointer to receive the array of mount descriptors - * @param FLAGS Additional mounting flags. (see above) - * - * @return The number of entries in MOUNTS or -1 if an error occurred (see errno) - * @note The caller is responsible for freeing MOUNTS when finished with it - * @note All device caches are setup using default values (see above) - */ - extern int ntfsMountAll ( ntfs_md **mounts, u32 flags ); +/** + * Mount all NTFS partitions on all inserted block devices. + * + * @param MOUNTS (out) A pointer to receive the array of mount descriptors + * @param FLAGS Additional mounting flags. (see above) + * + * @return The number of entries in MOUNTS or -1 if an error occurred (see errno) + * @note The caller is responsible for freeing MOUNTS when finished with it + * @note All device caches are setup using default values (see above) + */ +extern int ntfsMountAll (ntfs_md **mounts, u32 flags); - /** - * Mount all NTFS partitions on a block devices. - * - * @param INTERFACE The block device to mount. - * @param MOUNTS (out) A pointer to receive the array of mount descriptors - * @param FLAGS Additional mounting flags. (see above) - * - * @return The number of entries in MOUNTS or -1 if an error occurred (see errno) - * @note The caller is responsible for freeing MOUNTS when finished with it - * @note The device cache is setup using default values (see above) - */ - extern int ntfsMountDevice ( const DISC_INTERFACE* interface, ntfs_md **mounts, u32 flags ); +/** + * Mount all NTFS partitions on a block devices. + * + * @param INTERFACE The block device to mount. + * @param MOUNTS (out) A pointer to receive the array of mount descriptors + * @param FLAGS Additional mounting flags. (see above) + * + * @return The number of entries in MOUNTS or -1 if an error occurred (see errno) + * @note The caller is responsible for freeing MOUNTS when finished with it + * @note The device cache is setup using default values (see above) + */ +extern int ntfsMountDevice (const DISC_INTERFACE* interface, ntfs_md **mounts, u32 flags); - /** - * Mount a NTFS partition from a specific sector on a block device. - * - * @param NAME The name to mount the device under (can then be accessed as "NAME:/") - * @param INTERFACE The block device to mount - * @param STARTSECTOR The sector the partition begins at (see @ntfsFindPartitions) - * @param CACHEPAGECOUNT The total number of pages in the device cache - * @param CACHEPAGESIZE The number of sectors per cache page - * @param FLAGS Additional mounting flags (see above) - * - * @return True if mount was successful, false if no partition was found or an error occurred (see errno) - * @note ntfsFindPartitions should be used first to locate the partitions start sector - */ - extern bool ntfsMount ( const char *name, const DISC_INTERFACE *interface, sec_t startSector, u32 cachePageCount, u32 cachePageSize, u32 flags ); +/** + * Mount a NTFS partition from a specific sector on a block device. + * + * @param NAME The name to mount the device under (can then be accessed as "NAME:/") + * @param INTERFACE The block device to mount + * @param STARTSECTOR The sector the partition begins at (see @ntfsFindPartitions) + * @param CACHEPAGECOUNT The total number of pages in the device cache + * @param CACHEPAGESIZE The number of sectors per cache page + * @param FLAGS Additional mounting flags (see above) + * + * @return True if mount was successful, false if no partition was found or an error occurred (see errno) + * @note ntfsFindPartitions should be used first to locate the partitions start sector + */ +extern bool ntfsMount (const char *name, const DISC_INTERFACE *interface, sec_t startSector, u32 cachePageCount, u32 cachePageSize, u32 flags); - /** - * Unmount a NTFS partition. - * - * @param NAME The name of mount used in ntfsMountSimple() and ntfsMount() - * @param FORCE If true unmount even if the device is busy (may lead to data lose) - */ - extern void ntfsUnmount ( const char *name, bool force ); +/** + * Unmount a NTFS partition. + * + * @param NAME The name of mount used in ntfsMountSimple() and ntfsMount() + * @param FORCE If true unmount even if the device is busy (may lead to data lose) + */ +extern void ntfsUnmount (const char *name, bool force); - /** - * Get the volume name of a mounted NTFS partition. - * - * @param NAME The name of mount (see @ntfsMountAll, @ntfsMountDevice, and @ntfsMount) - * - * @return The volumes name if successful or NULL if an error occurred (see errno) - */ - extern const char *ntfsGetVolumeName ( const char *name ); +/** + * Get the volume name of a mounted NTFS partition. + * + * @param NAME The name of mount (see @ntfsMountAll, @ntfsMountDevice, and @ntfsMount) + * + * @return The volumes name if successful or NULL if an error occurred (see errno) + */ +extern const char *ntfsGetVolumeName (const char *name); - /** - * Set the volume name of a mounted NTFS partition. - * - * @param NAME The name of mount (see @ntfsMountAll, @ntfsMountDevice, and @ntfsMount) - * @param VOLUMENAME The new volume name - * - * @return True if mount was successful, false if an error occurred (see errno) - * @note The mount must be write-enabled else this will fail - */ - extern bool ntfsSetVolumeName ( const char *name, const char *volumeName ); - - typedef int ( *_ntfs_frag_append_t )( void *ff, u32 offset, u32 sector, u32 count ); - int _NTFS_get_fragments ( const char *path, _ntfs_frag_append_t append_fragment, void *callback_data ); +/** + * Set the volume name of a mounted NTFS partition. + * + * @param NAME The name of mount (see @ntfsMountAll, @ntfsMountDevice, and @ntfsMount) + * @param VOLUMENAME The new volume name + * + * @return True if mount was successful, false if an error occurred (see errno) + * @note The mount must be write-enabled else this will fail + */ +extern bool ntfsSetVolumeName (const char *name, const char *volumeName); #ifdef __cplusplus } diff --git a/source/libntfs/ntfsdir.c b/source/libntfs/ntfsdir.c index 90c54570..ad54b514 100644 --- a/source/libntfs/ntfsdir.c +++ b/source/libntfs/ntfsdir.c @@ -47,24 +47,23 @@ #define STATE(x) ((ntfs_dir_state*)(x)->dirStruct) -void ntfsCloseDir ( ntfs_dir_state *dir ) +void ntfsCloseDir (ntfs_dir_state *dir) { // Sanity check - if ( !dir || !dir->vd ) + if (!dir || !dir->vd) return; // Free the directory entries (if any) - while ( dir->first ) - { + while (dir->first) { ntfs_dir_entry *next = dir->first->next; - ntfs_free( dir->first->name ); - ntfs_free( dir->first ); + ntfs_free(dir->first->name); + ntfs_free(dir->first); dir->first = next; } // Close the directory (if open) - if ( dir->ni ) - ntfsCloseEntry( dir->vd, dir->ni ); + if (dir->ni) + ntfsCloseEntry(dir->vd, dir->ni); // Reset the directory state dir->ni = NULL; @@ -74,280 +73,264 @@ void ntfsCloseDir ( ntfs_dir_state *dir ) return; } -int ntfs_stat_r ( struct _reent *r, const char *path, struct stat *st ) +int ntfs_stat_r (struct _reent *r, const char *path, struct stat *st) { // Short circuit cases were we don't actually have to do anything - if ( !st || !path ) + if (!st || !path) return 0; - ntfs_log_trace( "path %s, st %p\n", path, st ); + ntfs_log_trace("path %s, st %p\n", path, st); ntfs_vd *vd = NULL; ntfs_inode *ni = NULL; // Get the volume descriptor for this path - vd = ntfsGetVolume( path ); - if ( !vd ) - { + vd = ntfsGetVolume(path); + if (!vd) { r->_errno = ENODEV; return -1; } - if ( strcmp( path, "." ) == 0 || strcmp( path, ".." ) == 0 ) + if(strcmp(path, ".") == 0 || strcmp(path, "..") == 0) { - memset( st, 0, sizeof( struct stat ) ); + memset(st, 0, sizeof(struct stat)); st->st_mode = S_IFDIR; return 0; } // Lock - ntfsLock( vd ); + ntfsLock(vd); // Find the entry - ni = ntfsOpenEntry( vd, path ); - if ( !ni ) - { + ni = ntfsOpenEntry(vd, path); + if (!ni) { r->_errno = errno; - ntfsUnlock( vd ); + ntfsUnlock(vd); return -1; } // Get the entry stats - int ret = ntfsStat( vd, ni, st ); - if ( ret ) + int ret = ntfsStat(vd, ni, st); + if (ret) r->_errno = errno; // Close the entry - ntfsCloseEntry( vd, ni ); + ntfsCloseEntry(vd, ni); - ntfsUnlock( vd ); + ntfsUnlock(vd); return 0; } -int ntfs_link_r ( struct _reent *r, const char *existing, const char *newLink ) +int ntfs_link_r (struct _reent *r, const char *existing, const char *newLink) { - ntfs_log_trace( "existing %s, newLink %s\n", existing, newLink ); + ntfs_log_trace("existing %s, newLink %s\n", existing, newLink); ntfs_vd *vd = NULL; ntfs_inode *ni = NULL; // Get the volume descriptor for this path - vd = ntfsGetVolume( existing ); - if ( !vd ) - { + vd = ntfsGetVolume(existing); + if (!vd) { r->_errno = ENODEV; return -1; } // Lock - ntfsLock( vd ); + ntfsLock(vd); // Create a symbolic link between the two paths - ni = ntfsCreate( vd, existing, S_IFLNK, newLink ); - if ( !ni ) - { - ntfsUnlock( vd ); + ni = ntfsCreate(vd, existing, S_IFLNK, newLink); + if (!ni) { + ntfsUnlock(vd); r->_errno = errno; return -1; } // Close the symbolic link - ntfsCloseEntry( vd, ni ); + ntfsCloseEntry(vd, ni); // Unlock - ntfsUnlock( vd ); + ntfsUnlock(vd); return 0; } -int ntfs_unlink_r ( struct _reent *r, const char *name ) +int ntfs_unlink_r (struct _reent *r, const char *name) { - ntfs_log_trace( "name %s\n", name ); + ntfs_log_trace("name %s\n", name); // Unlink the entry - int ret = ntfsUnlink( ntfsGetVolume( name ), name ); - if ( ret ) + int ret = ntfsUnlink(ntfsGetVolume(name), name); + if (ret) r->_errno = errno; return ret; } -int ntfs_chdir_r ( struct _reent *r, const char *name ) +int ntfs_chdir_r (struct _reent *r, const char *name) { - ntfs_log_trace( "name %s\n", name ); + ntfs_log_trace("name %s\n", name); ntfs_vd *vd = NULL; ntfs_inode *ni = NULL; // Get the volume descriptor for this path - vd = ntfsGetVolume( name ); - if ( !vd ) - { + vd = ntfsGetVolume(name); + if (!vd) { r->_errno = ENODEV; return -1; } // Lock - ntfsLock( vd ); + ntfsLock(vd); // Find the directory - ni = ntfsOpenEntry( vd, name ); - if ( !ni ) - { - ntfsUnlock( vd ); + ni = ntfsOpenEntry(vd, name); + if (!ni) { + ntfsUnlock(vd); r->_errno = ENOENT; return -1; } // Ensure that this directory is indeed a directory - if ( !( ni->mrec->flags && MFT_RECORD_IS_DIRECTORY ) ) - { - ntfsCloseEntry( vd, ni ); - ntfsUnlock( vd ); + if (!(ni->mrec->flags && MFT_RECORD_IS_DIRECTORY)) { + ntfsCloseEntry(vd, ni); + ntfsUnlock(vd); r->_errno = ENOTDIR; return -1; } // Close the old current directory (if any) - if ( vd->cwd_ni ) - ntfsCloseEntry( vd, vd->cwd_ni ); + if (vd->cwd_ni) + ntfsCloseEntry(vd, vd->cwd_ni); // Set the new current directory vd->cwd_ni = ni; // Unlock - ntfsUnlock( vd ); + ntfsUnlock(vd); return 0; } -int ntfs_rename_r ( struct _reent *r, const char *oldName, const char *newName ) +int ntfs_rename_r (struct _reent *r, const char *oldName, const char *newName) { - ntfs_log_trace( "oldName %s, newName %s\n", oldName, newName ); + ntfs_log_trace("oldName %s, newName %s\n", oldName, newName); ntfs_vd *vd = NULL; ntfs_inode *ni = NULL; // Get the volume descriptor for this path - vd = ntfsGetVolume( oldName ); - if ( !vd ) - { + vd = ntfsGetVolume(oldName); + if (!vd) { r->_errno = ENODEV; return -1; } // Lock - ntfsLock( vd ); + ntfsLock(vd); // You cannot rename between devices - if ( vd != ntfsGetVolume( newName ) ) - { - ntfsUnlock( vd ); + if(vd != ntfsGetVolume(newName)) { + ntfsUnlock(vd); r->_errno = EXDEV; return -1; } // Check that there is no existing entry with the new name - ni = ntfsOpenEntry( vd, newName ); - if ( ni ) - { - ntfsCloseEntry( vd, ni ); - ntfsUnlock( vd ); + ni = ntfsOpenEntry(vd, newName); + if (ni) { + ntfsCloseEntry(vd, ni); + ntfsUnlock(vd); r->_errno = EEXIST; return -1; } // Link the old entry with the new one - if ( ntfsLink( vd, oldName, newName ) ) - { - ntfsUnlock( vd ); + if (ntfsLink(vd, oldName, newName)) { + ntfsUnlock(vd); return -1; } // Unlink the old entry - if ( ntfsUnlink( vd, oldName ) ) - { - if ( ntfsUnlink( vd, newName ) ) - { - ntfsUnlock( vd ); + if (ntfsUnlink(vd, oldName)) { + if (ntfsUnlink(vd, newName)) { + ntfsUnlock(vd); return -1; } - ntfsUnlock( vd ); + ntfsUnlock(vd); return -1; } // Unlock - ntfsUnlock( vd ); + ntfsUnlock(vd); return 0; } -int ntfs_mkdir_r ( struct _reent *r, const char *path, int mode ) +int ntfs_mkdir_r (struct _reent *r, const char *path, int mode) { - ntfs_log_trace( "path %s, mode %i\n", path, mode ); + ntfs_log_trace("path %s, mode %i\n", path, mode); ntfs_vd *vd = NULL; ntfs_inode *ni = NULL; // Get the volume descriptor for this path - vd = ntfsGetVolume( path ); - if ( !vd ) - { + vd = ntfsGetVolume(path); + if (!vd) { r->_errno = ENODEV; return -1; } // Lock - ntfsLock( vd ); + ntfsLock(vd); // Create the directory - ni = ntfsCreate( vd, path, S_IFDIR, NULL ); - if ( !ni ) - { - ntfsUnlock( vd ); + ni = ntfsCreate(vd, path, S_IFDIR, NULL); + if (!ni) { + ntfsUnlock(vd); r->_errno = errno; return -1; } // Close the directory - ntfsCloseEntry( vd, ni ); + ntfsCloseEntry(vd, ni); // Unlock - ntfsUnlock( vd ); + ntfsUnlock(vd); return 0; } -int ntfs_statvfs_r ( struct _reent *r, const char *path, struct statvfs *buf ) +int ntfs_statvfs_r (struct _reent *r, const char *path, struct statvfs *buf) { - ntfs_log_trace( "path %s, buf %p\n", path, buf ); + ntfs_log_trace("path %s, buf %p\n", path, buf); ntfs_vd *vd = NULL; s64 size; int delta_bits; // Get the volume descriptor for this path - vd = ntfsGetVolume( path ); - if ( !vd ) - { + vd = ntfsGetVolume(path); + if (!vd) { r->_errno = ENODEV; return -1; } // Short circuit cases were we don't actually have to do anything - if ( !buf ) + if (!buf) return 0; // Lock - ntfsLock( vd ); + ntfsLock(vd); // Zero out the stat buffer - memset( buf, 0, sizeof( struct statvfs ) ); + memset(buf, 0, sizeof(struct statvfs)); - if ( ntfs_volume_get_free_space( vd->vol ) < 0 ) + if(ntfs_volume_get_free_space(vd->vol) < 0) { - ntfsUnlock( vd ); + ntfsUnlock(vd); return -1; } @@ -361,34 +344,34 @@ int ntfs_statvfs_r ( struct _reent *r, const char *path, struct statvfs *buf ) buf->f_blocks = vd->vol->nr_clusters; // Free blocks available for all and for non-privileged processes - size = MAX( vd->vol->free_clusters, 0 ); + size = MAX(vd->vol->free_clusters, 0); buf->f_bfree = buf->f_bavail = size; // Free inodes on the free space delta_bits = vd->vol->cluster_size_bits - vd->vol->mft_record_size_bits; - if ( delta_bits >= 0 ) + if (delta_bits >= 0) size <<= delta_bits; else size >>= -delta_bits; // Number of inodes at this point in time - buf->f_files = ( vd->vol->mftbmp_na->allocated_size << 3 ) + size; + buf->f_files = (vd->vol->mftbmp_na->allocated_size << 3) + size; // Free inodes available for all and for non-privileged processes size += vd->vol->free_mft_records; - buf->f_ffree = buf->f_favail = MAX( size, 0 ); + buf->f_ffree = buf->f_favail = MAX(size, 0); // File system id buf->f_fsid = vd->id; // Bit mask of f_flag values. - buf->f_flag = ( NVolReadOnly( vd->vol ) ? ST_RDONLY : 0 ); + buf->f_flag = (NVolReadOnly(vd->vol) ? ST_RDONLY : 0); // Maximum length of filenames buf->f_namemax = NTFS_MAX_NAME_LEN; // Unlock - ntfsUnlock( vd ); + ntfsUnlock(vd); return 0; } @@ -396,83 +379,74 @@ int ntfs_statvfs_r ( struct _reent *r, const char *path, struct statvfs *buf ) /** * PRIVATE: Callback for directory walking */ -int ntfs_readdir_filler ( DIR_ITER *dirState, const ntfschar *name, const int name_len, const int name_type, - const s64 pos, const MFT_REF mref, const unsigned dt_type ) +int ntfs_readdir_filler (DIR_ITER *dirState, const ntfschar *name, const int name_len, const int name_type, + const s64 pos, const MFT_REF mref, const unsigned dt_type) { - ntfs_dir_state *dir = STATE( dirState ); + ntfs_dir_state *dir = STATE(dirState); ntfs_dir_entry *entry = NULL; char *entry_name = NULL; // Sanity check - if ( !dir || !dir->vd ) - { + if (!dir || !dir->vd) { errno = EINVAL; return -1; } // Ignore DOS file names - if ( name_type == FILE_NAME_DOS ) - { + if (name_type == FILE_NAME_DOS) { return 0; } // Preliminary check that this entry can be enumerated (as described by the volume descriptor) - if ( MREF( mref ) == FILE_root || MREF( mref ) >= FILE_first_user || dir->vd->showSystemFiles ) - { + if (MREF(mref) == FILE_root || MREF(mref) >= FILE_first_user || dir->vd->showSystemFiles) { // Convert the entry name to our current local - if ( ntfsUnicodeToLocal( name, name_len, &entry_name, 0 ) < 0 ) - { + if (ntfsUnicodeToLocal(name, name_len, &entry_name, 0) < 0) { return -1; } - if ( dir->first && dir->first->mref == FILE_root && - MREF( mref ) == FILE_root && strcmp( entry_name, ".." ) == 0 ) + if(dir->first && dir->first->mref == FILE_root && + MREF(mref) == FILE_root && strcmp(entry_name, "..") == 0) { return 0; } // If this is not the parent or self directory reference - if ( ( strcmp( entry_name, "." ) != 0 ) && ( strcmp( entry_name, ".." ) != 0 ) ) - { + if ((strcmp(entry_name, ".") != 0) && (strcmp(entry_name, "..") != 0)) { // Open the entry - ntfs_inode *ni = ntfs_pathname_to_inode( dir->vd->vol, dir->ni, entry_name ); - if ( !ni ) + ntfs_inode *ni = ntfs_pathname_to_inode(dir->vd->vol, dir->ni, entry_name); + if (!ni) return -1; // Double check that this entry can be emuerated (as described by the volume descriptor) - if ( ( ( ni->flags & FILE_ATTR_HIDDEN ) && !dir->vd->showHiddenFiles ) || - ( ( ni->flags & FILE_ATTR_SYSTEM ) && !dir->vd->showSystemFiles ) ) - { - ntfs_inode_close( ni ); + if (((ni->flags & FILE_ATTR_HIDDEN) && !dir->vd->showHiddenFiles) || + ((ni->flags & FILE_ATTR_SYSTEM) && !dir->vd->showSystemFiles)) { + ntfs_inode_close(ni); return 0; } // Close the entry - ntfs_inode_close( ni ); + ntfs_inode_close(ni); } // Allocate a new directory entry - entry = ( ntfs_dir_entry * ) ntfs_alloc( sizeof( ntfs_dir_entry ) ); - if ( !entry ) + entry = (ntfs_dir_entry *) ntfs_alloc(sizeof(ntfs_dir_entry)); + if (!entry) return -1; // Setup the entry entry->name = entry_name; entry->next = NULL; - entry->mref = MREF( mref ); + entry->mref = MREF(mref); // Link the entry to the directory - if ( !dir->first ) - { + if (!dir->first) { dir->first = entry; - } - else - { + } else { ntfs_dir_entry *last = dir->first; - while ( last->next ) last = last->next; + while (last->next) last = last->next; last->next = entry; } @@ -481,48 +455,44 @@ int ntfs_readdir_filler ( DIR_ITER *dirState, const ntfschar *name, const int na return 0; } -DIR_ITER *ntfs_diropen_r ( struct _reent *r, DIR_ITER *dirState, const char *path ) +DIR_ITER *ntfs_diropen_r (struct _reent *r, DIR_ITER *dirState, const char *path) { - ntfs_log_trace( "dirState %p, path %s\n", dirState, path ); + ntfs_log_trace("dirState %p, path %s\n", dirState, path); - ntfs_dir_state* dir = STATE( dirState ); + ntfs_dir_state* dir = STATE(dirState); s64 position = 0; // Get the volume descriptor for this path - dir->vd = ntfsGetVolume( path ); - if ( !dir->vd ) - { + dir->vd = ntfsGetVolume(path); + if (!dir->vd) { r->_errno = ENODEV; return NULL; } // Lock - ntfsLock( dir->vd ); + ntfsLock(dir->vd); // Find the directory - dir->ni = ntfsOpenEntry( dir->vd, path ); - if ( !dir->ni ) - { - ntfsUnlock( dir->vd ); + dir->ni = ntfsOpenEntry(dir->vd, path); + if (!dir->ni) { + ntfsUnlock(dir->vd); r->_errno = ENOENT; return NULL; } // Ensure that this directory is indeed a directory - if ( !( dir->ni->mrec->flags && MFT_RECORD_IS_DIRECTORY ) ) - { - ntfsCloseEntry( dir->vd, dir->ni ); - ntfsUnlock( dir->vd ); + if (!(dir->ni->mrec->flags && MFT_RECORD_IS_DIRECTORY)) { + ntfsCloseEntry(dir->vd, dir->ni); + ntfsUnlock(dir->vd); r->_errno = ENOTDIR; return NULL; } // Read the directory dir->first = dir->current = NULL; - if ( ntfs_readdir( dir->ni, &position, dirState, ( ntfs_filldir_t )ntfs_readdir_filler ) ) - { - ntfsCloseDir( dir ); - ntfsUnlock( dir->vd ); + if (ntfs_readdir(dir->ni, &position, dirState, (ntfs_filldir_t)ntfs_readdir_filler)) { + ntfsCloseDir(dir); + ntfsUnlock(dir->vd); r->_errno = errno; return NULL; } @@ -531,16 +501,13 @@ DIR_ITER *ntfs_diropen_r ( struct _reent *r, DIR_ITER *dirState, const char *pat dir->current = dir->first; // Update directory times - ntfsUpdateTimes( dir->vd, dir->ni, NTFS_UPDATE_ATIME ); + ntfsUpdateTimes(dir->vd, dir->ni, NTFS_UPDATE_ATIME); // Insert the directory into the double-linked FILO list of open directories - if ( dir->vd->firstOpenDir ) - { + if (dir->vd->firstOpenDir) { dir->nextOpenDir = dir->vd->firstOpenDir; dir->vd->firstOpenDir->prevOpenDir = dir; - } - else - { + } else { dir->nextOpenDir = NULL; } dir->prevOpenDir = NULL; @@ -549,80 +516,76 @@ DIR_ITER *ntfs_diropen_r ( struct _reent *r, DIR_ITER *dirState, const char *pat dir->vd->openDirCount++; // Unlock - ntfsUnlock( dir->vd ); + ntfsUnlock(dir->vd); return dirState; } -int ntfs_dirreset_r ( struct _reent *r, DIR_ITER *dirState ) +int ntfs_dirreset_r (struct _reent *r, DIR_ITER *dirState) { - ntfs_log_trace( "dirState %p\n", dirState ); + ntfs_log_trace("dirState %p\n", dirState); - ntfs_dir_state* dir = STATE( dirState ); + ntfs_dir_state* dir = STATE(dirState); // Sanity check - if ( !dir || !dir->vd || !dir->ni ) - { + if (!dir || !dir->vd || !dir->ni) { r->_errno = EBADF; return -1; } // Lock - ntfsLock( dir->vd ); + ntfsLock(dir->vd); // Move to the first entry in the directory dir->current = dir->first; // Update directory times - ntfsUpdateTimes( dir->vd, dir->ni, NTFS_UPDATE_ATIME ); + ntfsUpdateTimes(dir->vd, dir->ni, NTFS_UPDATE_ATIME); // Unlock - ntfsUnlock( dir->vd ); + ntfsUnlock(dir->vd); return 0; } -int ntfs_dirnext_r ( struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat ) +int ntfs_dirnext_r (struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat) { - ntfs_log_trace( "dirState %p, filename %p, filestat %p\n", dirState, filename, filestat ); + ntfs_log_trace("dirState %p, filename %p, filestat %p\n", dirState, filename, filestat); - ntfs_dir_state* dir = STATE( dirState ); + ntfs_dir_state* dir = STATE(dirState); ntfs_inode *ni = NULL; // Sanity check - if ( !dir || !dir->vd || !dir->ni ) - { + if (!dir || !dir->vd || !dir->ni) { r->_errno = EBADF; return -1; } // Lock - ntfsLock( dir->vd ); + ntfsLock(dir->vd); // Check that there is a entry waiting to be fetched - if ( !dir->current ) - { - ntfsUnlock( dir->vd ); + if (!dir->current) { + ntfsUnlock(dir->vd); r->_errno = ENOENT; return -1; } // Fetch the current entry - strcpy( filename, dir->current->name ); - if ( filestat != NULL ) + strcpy(filename, dir->current->name); + if(filestat != NULL) { - if ( strcmp( dir->current->name, "." ) == 0 || strcmp( dir->current->name, ".." ) == 0 ) + if(strcmp(dir->current->name, ".") == 0 || strcmp(dir->current->name, "..") == 0) { - memset( filestat, 0, sizeof( struct stat ) ); + memset(filestat, 0, sizeof(struct stat)); filestat->st_mode = S_IFDIR; } else { - ni = ntfsOpenEntry( dir->vd, dir->current->name ); - if ( ni ) - { - ntfsStat( dir->vd, ni, filestat ); - ntfsCloseEntry( dir->vd, ni ); + ni = ntfsOpenEntry(dir->vd, dir->current->name); + if (ni) { + ntfsStat(dir->vd, ni, filestat); + ntfsCloseEntry(dir->vd, ni); } } } @@ -631,44 +594,43 @@ int ntfs_dirnext_r ( struct _reent *r, DIR_ITER *dirState, char *filename, struc dir->current = dir->current->next; // Update directory times - ntfsUpdateTimes( dir->vd, dir->ni, NTFS_UPDATE_ATIME ); + ntfsUpdateTimes(dir->vd, dir->ni, NTFS_UPDATE_ATIME); // Unlock - ntfsUnlock( dir->vd ); + ntfsUnlock(dir->vd); return 0; } -int ntfs_dirclose_r ( struct _reent *r, DIR_ITER *dirState ) +int ntfs_dirclose_r (struct _reent *r, DIR_ITER *dirState) { - ntfs_log_trace( "dirState %p\n", dirState ); + ntfs_log_trace("dirState %p\n", dirState); - ntfs_dir_state* dir = STATE( dirState ); + ntfs_dir_state* dir = STATE(dirState); // Sanity check - if ( !dir || !dir->vd ) - { + if (!dir || !dir->vd) { r->_errno = EBADF; return -1; } // Lock - ntfsLock( dir->vd ); + ntfsLock(dir->vd); // Close the directory - ntfsCloseDir( dir ); + ntfsCloseDir(dir); // Remove the directory from the double-linked FILO list of open directories dir->vd->openDirCount--; - if ( dir->nextOpenDir ) + if (dir->nextOpenDir) dir->nextOpenDir->prevOpenDir = dir->prevOpenDir; - if ( dir->prevOpenDir ) + if (dir->prevOpenDir) dir->prevOpenDir->nextOpenDir = dir->nextOpenDir; else dir->vd->firstOpenDir = dir->nextOpenDir; // Unlock - ntfsUnlock( dir->vd ); + ntfsUnlock(dir->vd); return 0; } diff --git a/source/libntfs/ntfsdir.h b/source/libntfs/ntfsdir.h index c7adbe9b..0550592f 100644 --- a/source/libntfs/ntfsdir.h +++ b/source/libntfs/ntfsdir.h @@ -28,8 +28,7 @@ /** * ntfs_dir_entry - Directory entry */ -typedef struct _ntfs_dir_entry -{ +typedef struct _ntfs_dir_entry { char *name; u64 mref; struct _ntfs_dir_entry *next; @@ -38,8 +37,7 @@ typedef struct _ntfs_dir_entry /** * ntfs_dir_state - Directory state */ -typedef struct _ntfs_dir_state -{ +typedef struct _ntfs_dir_state { ntfs_vd *vd; /* Volume this directory belongs to */ ntfs_inode *ni; /* Directory descriptor */ ntfs_dir_entry *first; /* The first entry in the directory */ @@ -49,22 +47,22 @@ typedef struct _ntfs_dir_state } ntfs_dir_state; /* Directory state routines */ -void ntfsCloseDir ( ntfs_dir_state *file ); +void ntfsCloseDir (ntfs_dir_state *file); /* Gekko devoptab directory routines for NTFS-based devices */ -extern int ntfs_stat_r ( struct _reent *r, const char *path, struct stat *st ); -extern int ntfs_link_r ( struct _reent *r, const char *existing, const char *newLink ); -extern int ntfs_unlink_r ( struct _reent *r, const char *name ); -extern int ntfs_chdir_r ( struct _reent *r, const char *name ); -extern int ntfs_rename_r ( struct _reent *r, const char *oldName, const char *newName ); -extern int ntfs_mkdir_r ( struct _reent *r, const char *path, int mode ); -extern int ntfs_statvfs_r ( struct _reent *r, const char *path, struct statvfs *buf ); +extern int ntfs_stat_r (struct _reent *r, const char *path, struct stat *st); +extern int ntfs_link_r (struct _reent *r, const char *existing, const char *newLink); +extern int ntfs_unlink_r (struct _reent *r, const char *name); +extern int ntfs_chdir_r (struct _reent *r, const char *name); +extern int ntfs_rename_r (struct _reent *r, const char *oldName, const char *newName); +extern int ntfs_mkdir_r (struct _reent *r, const char *path, int mode); +extern int ntfs_statvfs_r (struct _reent *r, const char *path, struct statvfs *buf); /* Gekko devoptab directory walking routines for NTFS-based devices */ -extern DIR_ITER *ntfs_diropen_r ( struct _reent *r, DIR_ITER *dirState, const char *path ); -extern int ntfs_dirreset_r ( struct _reent *r, DIR_ITER *dirState ); -extern int ntfs_dirnext_r ( struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat ); -extern int ntfs_dirclose_r ( struct _reent *r, DIR_ITER *dirState ); +extern DIR_ITER *ntfs_diropen_r (struct _reent *r, DIR_ITER *dirState, const char *path); +extern int ntfs_dirreset_r (struct _reent *r, DIR_ITER *dirState); +extern int ntfs_dirnext_r (struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat); +extern int ntfs_dirclose_r (struct _reent *r, DIR_ITER *dirState); #endif /* _NTFSDIR_H */ diff --git a/source/libntfs/ntfsfile.c b/source/libntfs/ntfsfile.c index 7e3a20d2..84aa5372 100644 --- a/source/libntfs/ntfsfile.c +++ b/source/libntfs/ntfsfile.c @@ -45,36 +45,36 @@ #define STATE(x) ((ntfs_file_state*)x) -void ntfsCloseFile ( ntfs_file_state *file ) +void ntfsCloseFile (ntfs_file_state *file) { // Sanity check - if ( !file || !file->vd ) + if (!file || !file->vd) return; // Special case fix ups for compressed and/or encrypted files - if ( file->compressed ) - ntfs_attr_pclose( file->data_na ); + if (file->compressed) + ntfs_attr_pclose(file->data_na); #ifdef HAVE_SETXATTR - if ( file->encrypted ) - ntfs_efs_fixup_attribute( NULL, file->data_na ); + if (file->encrypted) + ntfs_efs_fixup_attribute(NULL, file->data_na); #endif // Close the file data attribute (if open) - if ( file->data_na ) - ntfs_attr_close( file->data_na ); + if (file->data_na) + ntfs_attr_close(file->data_na); // Sync the file (and its attributes) to disc - if ( file->write ) + if(file->write) { - ntfsUpdateTimes( file->vd, file->ni, NTFS_UPDATE_ATIME | NTFS_UPDATE_CTIME ); - ntfsSync( file->vd, file->ni ); + ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_ATIME | NTFS_UPDATE_CTIME); + ntfsSync(file->vd, file->ni); } - if ( file->read ) - ntfsUpdateTimes( file->vd, file->ni, NTFS_UPDATE_ATIME ); + if (file->read) + ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_ATIME); // Close the file (if open) - if ( file->ni ) - ntfsCloseEntry( file->vd, file->ni ); + if (file->ni) + ntfsCloseEntry(file->vd, file->ni); // Reset the file state file->ni = NULL; @@ -89,132 +89,106 @@ void ntfsCloseFile ( ntfs_file_state *file ) return; } -int ntfs_open_r ( struct _reent *r, void *fileStruct, const char *path, int flags, int mode ) +int ntfs_open_r (struct _reent *r, void *fileStruct, const char *path, int flags, int mode) { - ntfs_log_trace( "fileStruct %p, path %s, flags %i, mode %i\n", fileStruct, path, flags, mode ); + ntfs_log_trace("fileStruct %p, path %s, flags %i, mode %i\n", fileStruct, path, flags, mode); - ntfs_file_state* file = STATE( fileStruct ); + ntfs_file_state* file = STATE(fileStruct); // Get the volume descriptor for this path - file->vd = ntfsGetVolume( path ); - if ( !file->vd ) - { + file->vd = ntfsGetVolume(path); + if (!file->vd) { r->_errno = ENODEV; return -1; } // Lock - ntfsLock( file->vd ); + ntfsLock(file->vd); // Determine which mode the file is opened for file->flags = flags; - if ( ( flags & 0x03 ) == O_RDONLY ) - { + if ((flags & 0x03) == O_RDONLY) { file->read = true; file->write = false; file->append = false; - } - else if ( ( flags & 0x03 ) == O_WRONLY ) - { + } else if ((flags & 0x03) == O_WRONLY) { file->read = false; file->write = true; - file->append = ( flags & O_APPEND ); - } - else if ( ( flags & 0x03 ) == O_RDWR ) - { + file->append = (flags & O_APPEND); + } else if ((flags & 0x03) == O_RDWR) { file->read = true; file->write = true; - file->append = ( flags & O_APPEND ); - } - else - { + file->append = (flags & O_APPEND); + } else { r->_errno = EACCES; - ntfsUnlock( file->vd ); + ntfsUnlock(file->vd); return -1; } // Try and find the file and (if found) ensure that it is not a directory - file->ni = ntfsOpenEntry( file->vd, path ); - if ( file->ni && ( file->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ) ) - { - ntfsCloseEntry( file->vd, file->ni ); - ntfsUnlock( file->vd ); + file->ni = ntfsOpenEntry(file->vd, path); + if (file->ni && (file->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) { + ntfsCloseEntry(file->vd, file->ni); + ntfsUnlock(file->vd); r->_errno = EISDIR; return -1; } // Are we creating this file? - if ( flags & O_CREAT ) - { - - // The file SHOULD NOT already exist - if ( file->ni ) - { - ntfsCloseEntry( file->vd, file->ni ); - ntfsUnlock( file->vd ); - r->_errno = EEXIST; - return -1; - } + if ((flags & O_CREAT) && !file->ni) { // Create the file - file->ni = ntfsCreate( file->vd, path, S_IFREG, NULL ); - if ( !file->ni ) - { - ntfsUnlock( file->vd ); + file->ni = ntfsCreate(file->vd, path, S_IFREG, NULL); + if (!file->ni) { + ntfsUnlock(file->vd); return -1; } } // Sanity check, the file should be open by now - if ( !file->ni ) - { - ntfsUnlock( file->vd ); + if (!file->ni) { + ntfsUnlock(file->vd); r->_errno = ENOENT; return -1; } // Open the files data attribute - file->data_na = ntfs_attr_open( file->ni, AT_DATA, AT_UNNAMED, 0 ); - if ( !file->data_na ) - { - ntfsCloseEntry( file->vd, file->ni ); - ntfsUnlock( file->vd ); + file->data_na = ntfs_attr_open(file->ni, AT_DATA, AT_UNNAMED, 0); + if(!file->data_na) { + ntfsCloseEntry(file->vd, file->ni); + ntfsUnlock(file->vd); return -1; } // Determine if this files data is compressed and/or encrypted - file->compressed = NAttrCompressed( file->data_na ) || ( file->ni->flags & FILE_ATTR_COMPRESSED ); - file->encrypted = NAttrEncrypted( file->data_na ) || ( file->ni->flags & FILE_ATTR_ENCRYPTED ); + file->compressed = NAttrCompressed(file->data_na) || (file->ni->flags & FILE_ATTR_COMPRESSED); + file->encrypted = NAttrEncrypted(file->data_na) || (file->ni->flags & FILE_ATTR_ENCRYPTED); // We cannot read/write encrypted files - if ( file->encrypted ) - { - ntfs_attr_close( file->data_na ); - ntfsCloseEntry( file->vd, file->ni ); - ntfsUnlock( file->vd ); + if (file->encrypted) { + ntfs_attr_close(file->data_na); + ntfsCloseEntry(file->vd, file->ni); + ntfsUnlock(file->vd); r->_errno = EACCES; return -1; } // Make sure we aren't trying to write to a read-only file - if ( ( file->ni->flags & FILE_ATTR_READONLY ) && file->write ) - { - ntfs_attr_close( file->data_na ); - ntfsCloseEntry( file->vd, file->ni ); - ntfsUnlock( file->vd ); + if ((file->ni->flags & FILE_ATTR_READONLY) && file->write) { + ntfs_attr_close(file->data_na); + ntfsCloseEntry(file->vd, file->ni); + ntfsUnlock(file->vd); r->_errno = EROFS; return -1; } // Truncate the file if requested - if ( ( flags & O_TRUNC ) && file->write ) - { - if ( ntfs_attr_truncate( file->data_na, 0 ) ) - { - ntfs_attr_close( file->data_na ); - ntfsCloseEntry( file->vd, file->ni ); - ntfsUnlock( file->vd ); + if ((flags & O_TRUNC) && file->write) { + if (ntfs_attr_truncate(file->data_na, 0)) { + ntfs_attr_close(file->data_na); + ntfsCloseEntry(file->vd, file->ni); + ntfsUnlock(file->vd); r->_errno = errno; return -1; } @@ -224,19 +198,16 @@ int ntfs_open_r ( struct _reent *r, void *fileStruct, const char *path, int flag file->pos = 0; file->len = file->data_na->data_size; - ntfs_log_trace( "file->len %d\n", file->len ); + ntfs_log_trace("file->len %d\n", file->len); // Update file times - ntfsUpdateTimes( file->vd, file->ni, NTFS_UPDATE_ATIME ); + ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_ATIME); // Insert the file into the double-linked FILO list of open files - if ( file->vd->firstOpenFile ) - { + if (file->vd->firstOpenFile) { file->nextOpenFile = file->vd->firstOpenFile; file->vd->firstOpenFile->prevOpenFile = file; - } - else - { + } else { file->nextOpenFile = NULL; } file->prevOpenFile = NULL; @@ -244,91 +215,84 @@ int ntfs_open_r ( struct _reent *r, void *fileStruct, const char *path, int flag file->vd->openFileCount++; // Unlock - ntfsUnlock( file->vd ); + ntfsUnlock(file->vd); - return ( int )fileStruct; + return (int)fileStruct; } -int ntfs_close_r ( struct _reent *r, int fd ) +int ntfs_close_r (struct _reent *r, int fd) { - ntfs_log_trace( "fd %p\n", fd ); + ntfs_log_trace("fd %p\n", fd); - ntfs_file_state* file = STATE( fd ); + ntfs_file_state* file = STATE(fd); // Sanity check - if ( !file || !file->vd ) - { + if (!file || !file->vd) { r->_errno = EBADF; return -1; } // Lock - ntfsLock( file->vd ); + ntfsLock(file->vd); // Close the file - ntfsCloseFile( file ); + ntfsCloseFile(file); // Remove the file from the double-linked FILO list of open files file->vd->openFileCount--; - if ( file->nextOpenFile ) + if (file->nextOpenFile) file->nextOpenFile->prevOpenFile = file->prevOpenFile; - if ( file->prevOpenFile ) + if (file->prevOpenFile) file->prevOpenFile->nextOpenFile = file->nextOpenFile; else file->vd->firstOpenFile = file->nextOpenFile; // Unlock - ntfsUnlock( file->vd ); + ntfsUnlock(file->vd); return 0; } -ssize_t ntfs_write_r ( struct _reent *r, int fd, const char *ptr, size_t len ) +ssize_t ntfs_write_r (struct _reent *r, int fd, const char *ptr, size_t len) { - ntfs_log_trace( "fd %p, ptr %p, len %Li\n", fd, ptr, len ); + ntfs_log_trace("fd %p, ptr %p, len %Li\n", fd, ptr, len); - ntfs_file_state* file = STATE( fd ); + ntfs_file_state* file = STATE(fd); ssize_t written = 0; off_t old_pos = 0; // Sanity check - if ( !file || !file->vd || !file->ni || !file->data_na ) - { + if (!file || !file->vd || !file->ni || !file->data_na) { r->_errno = EINVAL; return -1; } // Short circuit cases where we don't actually have to do anything - if ( !ptr || len <= 0 ) - { + if (!ptr || len <= 0) { return 0; } // Lock - ntfsLock( file->vd ); + ntfsLock(file->vd); // Check that we are allowed to write to this file - if ( !file->write ) - { - ntfsUnlock( file->vd ); + if (!file->write) { + ntfsUnlock(file->vd); r->_errno = EACCES; return -1; } // If we are in append mode, backup the current position and move to the end of the file - if ( file->append ) - { + if (file->append) { old_pos = file->pos; file->pos = file->len; } // Write to the files data atrribute - while ( len ) - { - ssize_t ret = ntfs_attr_pwrite( file->data_na, file->pos, len, ptr ); - if ( ret <= 0 ) - { - ntfsUnlock( file->vd ); + while (len) { + ssize_t ret = ntfs_attr_pwrite(file->data_na, file->pos, len, ptr); + if (ret <= 0) { + ntfsUnlock(file->vd); r->_errno = errno; return -1; } @@ -338,72 +302,65 @@ ssize_t ntfs_write_r ( struct _reent *r, int fd, const char *ptr, size_t len ) } // If we are in append mode, restore the current position to were it was prior to this write - if ( file->append ) - { + if (file->append) { file->pos = old_pos; } // Mark the file for archiving (if we actually wrote something) - if ( written ) + if (written) file->ni->flags |= FILE_ATTR_ARCHIVE; // Update the files data length file->len = file->data_na->data_size; // Unlock - ntfsUnlock( file->vd ); + ntfsUnlock(file->vd); return written; } -ssize_t ntfs_read_r ( struct _reent *r, int fd, char *ptr, size_t len ) +ssize_t ntfs_read_r (struct _reent *r, int fd, char *ptr, size_t len) { - ntfs_log_trace( "fd %p, ptr %p, len %Li\n", fd, ptr, len ); + ntfs_log_trace("fd %p, ptr %p, len %Li\n", fd, ptr, len); - ntfs_file_state* file = STATE( fd ); + ntfs_file_state* file = STATE(fd); ssize_t read = 0; // Sanity check - if ( !file || !file->vd || !file->ni || !file->data_na ) - { + if (!file || !file->vd || !file->ni || !file->data_na) { r->_errno = EINVAL; return -1; } // Short circuit cases where we don't actually have to do anything - if ( !ptr || len <= 0 ) - { + if (!ptr || len <= 0) { return 0; } // Lock - ntfsLock( file->vd ); + ntfsLock(file->vd); // Check that we are allowed to read from this file - if ( !file->read ) - { - ntfsUnlock( file->vd ); + if (!file->read) { + ntfsUnlock(file->vd); r->_errno = EACCES; return -1; } // Don't read past the end of file - if ( file->pos + len > file->len ) - { + if (file->pos + len > file->len) { r->_errno = EOVERFLOW; len = file->len - file->pos; - ntfs_log_trace( "EOVERFLOW" ); + ntfs_log_trace("EOVERFLOW"); } - ntfs_log_trace( "file->pos:%d, len:%d, file->len:%d \n", ( u32 )file->pos, ( u32 )len, ( u32 )file->len ); + ntfs_log_trace("file->pos:%d, len:%d, file->len:%d \n", (u32)file->pos, (u32)len, (u32)file->len); // Read from the files data attribute - while ( len ) - { - ssize_t ret = ntfs_attr_pread( file->data_na, file->pos, len, ptr ); - if ( ret <= 0 || ret > len ) - { - ntfsUnlock( file->vd ); + while (len) { + ssize_t ret = ntfs_attr_pread(file->data_na, file->pos, len, ptr); + if (ret <= 0 || ret > len) { + ntfsUnlock(file->vd); r->_errno = errno; return -1; } @@ -416,166 +373,154 @@ ssize_t ntfs_read_r ( struct _reent *r, int fd, char *ptr, size_t len ) // Update file times (if we actually read something) // Unlock - ntfsUnlock( file->vd ); + ntfsUnlock(file->vd); return read; } -off_t ntfs_seek_r ( struct _reent *r, int fd, off_t pos, int dir ) +off_t ntfs_seek_r (struct _reent *r, int fd, off_t pos, int dir) { - ntfs_log_trace( "fd %p, pos %Li, dir %i\n", fd, pos, dir ); + ntfs_log_trace("fd %p, pos %Li, dir %i\n", fd, pos, dir); - ntfs_file_state* file = STATE( fd ); + ntfs_file_state* file = STATE(fd); off_t position = 0; // Sanity check - if ( !file || !file->vd || !file->ni || !file->data_na ) - { + if (!file || !file->vd || !file->ni || !file->data_na) { r->_errno = EINVAL; return -1; } // Lock - ntfsLock( file->vd ); + ntfsLock(file->vd); // Set the files current position - switch ( dir ) - { - case SEEK_SET: position = file->pos = MIN( MAX( pos, 0 ), file->len ); break; - case SEEK_CUR: position = file->pos = MIN( MAX( file->pos + pos, 0 ), file->len ); break; - case SEEK_END: position = file->pos = MIN( MAX( file->len + pos, 0 ), file->len ); break; + switch(dir) { + case SEEK_SET: position = file->pos = MIN(MAX(pos, 0), file->len); break; + case SEEK_CUR: position = file->pos = MIN(MAX(file->pos + pos, 0), file->len); break; + case SEEK_END: position = file->pos = MIN(MAX(file->len + pos, 0), file->len); break; } // Unlock - ntfsUnlock( file->vd ); + ntfsUnlock(file->vd); return position; } -int ntfs_fstat_r ( struct _reent *r, int fd, struct stat *st ) +int ntfs_fstat_r (struct _reent *r, int fd, struct stat *st) { - ntfs_log_trace( "fd %p\n", fd ); + ntfs_log_trace("fd %p\n", fd); - ntfs_file_state* file = STATE( fd ); + ntfs_file_state* file = STATE(fd); int ret = 0; // Sanity check - if ( !file || !file->vd || !file->ni || !file->data_na ) - { + if (!file || !file->vd || !file->ni || !file->data_na) { r->_errno = EINVAL; return -1; } // Short circuit cases were we don't actually have to do anything - if ( !st ) + if (!st) return 0; // Get the file stats - ret = ntfsStat( file->vd, file->ni, st ); - if ( ret ) + ret = ntfsStat(file->vd, file->ni, st); + if (ret) r->_errno = errno; return ret; } -int ntfs_ftruncate_r ( struct _reent *r, int fd, off_t len ) +int ntfs_ftruncate_r (struct _reent *r, int fd, off_t len) { - ntfs_log_trace( "fd %p, len %Li\n", fd, len ); + ntfs_log_trace("fd %p, len %Li\n", fd, len); - ntfs_file_state* file = STATE( fd ); + ntfs_file_state* file = STATE(fd); // Sanity check - if ( !file || !file->vd || !file->ni || !file->data_na ) - { + if (!file || !file->vd || !file->ni || !file->data_na) { r->_errno = EINVAL; return -1; } // Lock - ntfsLock( file->vd ); + ntfsLock(file->vd); // Check that we are allowed to write to this file - if ( !file->write ) - { - ntfsUnlock( file->vd ); + if (!file->write) { + ntfsUnlock(file->vd); r->_errno = EACCES; return -1; } // For compressed files, only deleting and expanding contents are implemented - if ( file->compressed && - len > 0 && - len < file->data_na->initialized_size ) - { - ntfsUnlock( file->vd ); + if (file->compressed && + len > 0 && + len < file->data_na->initialized_size) { + ntfsUnlock(file->vd); r->_errno = EOPNOTSUPP; return -1; } // Resize the files data attribute, either by expanding or truncating - if ( file->compressed && len > file->data_na->initialized_size ) - { + if (file->compressed && len > file->data_na->initialized_size) { char zero = 0; - if ( ntfs_attr_pwrite( file->data_na, len - 1, 1, &zero ) <= 0 ) - { - ntfsUnlock( file->vd ); + if (ntfs_attr_pwrite(file->data_na, len - 1, 1, &zero) <= 0) { + ntfsUnlock(file->vd); r->_errno = errno; return -1; } - } - else - { - if ( ntfs_attr_truncate( file->data_na, len ) ) - { - ntfsUnlock( file->vd ); + } else { + if (ntfs_attr_truncate(file->data_na, len)) { + ntfsUnlock(file->vd); r->_errno = errno; return -1; } } // Mark the file for archiving (if we actually changed something) - if ( file->len != file->data_na->data_size ) + if (file->len != file->data_na->data_size) file->ni->flags |= FILE_ATTR_ARCHIVE; // Update file times (if we actually changed something) - if ( file->len != file->data_na->data_size ) - ntfsUpdateTimes( file->vd, file->ni, NTFS_UPDATE_AMCTIME ); + if (file->len != file->data_na->data_size) + ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_AMCTIME); // Update the files data length file->len = file->data_na->data_size; // Sync the file (and its attributes) to disc - ntfsSync( file->vd, file->ni ); + ntfsSync(file->vd, file->ni); // Unlock - ntfsUnlock( file->vd ); + ntfsUnlock(file->vd); return 0; } -int ntfs_fsync_r ( struct _reent *r, int fd ) +int ntfs_fsync_r (struct _reent *r, int fd) { - ntfs_log_trace( "fd %p\n", fd ); + ntfs_log_trace("fd %p\n", fd); - ntfs_file_state* file = STATE( fd ); + ntfs_file_state* file = STATE(fd); int ret = 0; // Sanity check - if ( !file || !file->vd || !file->ni || !file->data_na ) - { + if (!file || !file->vd || !file->ni || !file->data_na) { r->_errno = EINVAL; return -1; } // Lock - ntfsLock( file->vd ); + ntfsLock(file->vd); // Sync the file (and its attributes) to disc - ret = ntfsSync( file->vd, file->ni ); - if ( ret ) + ret = ntfsSync(file->vd, file->ni); + if (ret) r->_errno = errno; // Unlock - ntfsUnlock( file->vd ); + ntfsUnlock(file->vd); return ret; } diff --git a/source/libntfs/ntfsfile.h b/source/libntfs/ntfsfile.h index 8e23a688..8e5ffcc6 100644 --- a/source/libntfs/ntfsfile.h +++ b/source/libntfs/ntfsfile.h @@ -32,8 +32,7 @@ /** * ntfs_file_state - File state */ -typedef struct _ntfs_file_state -{ +typedef struct _ntfs_file_state { ntfs_vd *vd; /* Volume this file belongs to */ ntfs_inode *ni; /* File descriptor */ ntfs_attr *data_na; /* File data descriptor */ @@ -50,17 +49,17 @@ typedef struct _ntfs_file_state } ntfs_file_state; /* File state routines */ -void ntfsCloseFile ( ntfs_file_state *file ); +void ntfsCloseFile (ntfs_file_state *file); /* Gekko devoptab file routines for NTFS-based devices */ -extern int ntfs_open_r ( struct _reent *r, void *fileStruct, const char *path, int flags, int mode ); -extern int ntfs_close_r ( struct _reent *r, int fd ); -extern ssize_t ntfs_write_r ( struct _reent *r, int fd, const char *ptr, size_t len ); -extern ssize_t ntfs_read_r ( struct _reent *r, int fd, char *ptr, size_t len ); -extern off_t ntfs_seek_r ( struct _reent *r, int fd, off_t pos, int dir ); -extern int ntfs_fstat_r ( struct _reent *r, int fd, struct stat *st ); -extern int ntfs_ftruncate_r ( struct _reent *r, int fd, off_t len ); -extern int ntfs_fsync_r ( struct _reent *r, int fd ); +extern int ntfs_open_r (struct _reent *r, void *fileStruct, const char *path, int flags, int mode); +extern int ntfs_close_r (struct _reent *r, int fd); +extern ssize_t ntfs_write_r (struct _reent *r, int fd, const char *ptr, size_t len); +extern ssize_t ntfs_read_r (struct _reent *r, int fd, char *ptr, size_t len); +extern off_t ntfs_seek_r (struct _reent *r, int fd, off_t pos, int dir); +extern int ntfs_fstat_r (struct _reent *r, int fd, struct stat *st); +extern int ntfs_ftruncate_r (struct _reent *r, int fd, off_t len); +extern int ntfs_fsync_r (struct _reent *r, int fd); #endif /* _NTFSFILE_H */ diff --git a/source/libntfs/ntfsinternal.c b/source/libntfs/ntfsinternal.c index 36291d82..fed15bce 100644 --- a/source/libntfs/ntfsinternal.c +++ b/source/libntfs/ntfsinternal.c @@ -43,8 +43,7 @@ #include #include -const INTERFACE_ID ntfs_disc_interfaces[] = -{ +const INTERFACE_ID ntfs_disc_interfaces[] = { { "sd", &__io_wiisd }, { "usb", &__io_usbstorage }, { "carda", &__io_gcsda }, @@ -55,8 +54,7 @@ const INTERFACE_ID ntfs_disc_interfaces[] = #elif defined(__gamecube__) #include -const INTERFACE_ID ntfs_disc_interfaces[] = -{ +const INTERFACE_ID ntfs_disc_interfaces[] = { { "carda", &__io_gcsda }, { "cardb", &__io_gcsdb }, { NULL, NULL } @@ -64,7 +62,7 @@ const INTERFACE_ID ntfs_disc_interfaces[] = #endif -int ntfsAddDevice ( const char *name, void *deviceData ) +int ntfsAddDevice (const char *name, void *deviceData) { const devoptab_t *devoptab_ntfs = ntfsGetDevOpTab(); devoptab_t *dev = NULL; @@ -72,34 +70,30 @@ int ntfsAddDevice ( const char *name, void *deviceData ) int i; // Sanity check - if ( !name || !deviceData || !devoptab_ntfs ) - { + if (!name || !deviceData || !devoptab_ntfs) { errno = EINVAL; return -1; } // Allocate a devoptab for this device - dev = ( devoptab_t * ) ntfs_alloc( sizeof( devoptab_t ) + strlen( name ) + 1 ); - if ( !dev ) - { + dev = (devoptab_t *) ntfs_alloc(sizeof(devoptab_t) + strlen(name) + 1); + if (!dev) { errno = ENOMEM; return false; } // Use the space allocated at the end of the devoptab for storing the device name - devname = ( char* )( dev + 1 ); - strcpy( devname, name ); + devname = (char*)(dev + 1); + strcpy(devname, name); // Setup the devoptab - memcpy( dev, devoptab_ntfs, sizeof( devoptab_t ) ); + memcpy(dev, devoptab_ntfs, sizeof(devoptab_t)); dev->name = devname; dev->deviceData = deviceData; // Add the device to the devoptab table (if there is a free slot) - for ( i = 0; i < STD_MAX; i++ ) - { - if ( devoptab_list[i] == devoptab_list[0] && i != 0 ) - { + for (i = 0; i < STD_MAX; i++) { + if (devoptab_list[i] == devoptab_list[0] && i != 0) { devoptab_list[i] = dev; return 0; } @@ -110,29 +104,26 @@ int ntfsAddDevice ( const char *name, void *deviceData ) return -1; } -void ntfsRemoveDevice ( const char *path ) +void ntfsRemoveDevice (const char *path) { const devoptab_t *devoptab = NULL; char name[128] = {0}; int i; // Get the device name from the path - strncpy( name, path, 127 ); - strtok( name, ":/" ); + strncpy(name, path, 127); + strtok(name, ":/"); // Find and remove the specified device from the devoptab table // NOTE: We do this manually due to a 'bug' in RemoveDevice // which ignores names with suffixes and causes names // like "ntfs" and "ntfs1" to be seen as equals - for ( i = 0; i < STD_MAX; i++ ) - { + for (i = 0; i < STD_MAX; i++) { devoptab = devoptab_list[i]; - if ( devoptab && devoptab->name ) - { - if ( strcmp( name, devoptab->name ) == 0 ) - { + if (devoptab && devoptab->name) { + if (strcmp(name, devoptab->name) == 0) { devoptab_list[i] = devoptab_list[0]; - ntfs_free( ( devoptab_t* )devoptab ); + ntfs_free((devoptab_t*)devoptab); break; } } @@ -141,27 +132,24 @@ void ntfsRemoveDevice ( const char *path ) return; } -const devoptab_t *ntfsGetDevice ( const char *path, bool useDefaultDevice ) +const devoptab_t *ntfsGetDevice (const char *path, bool useDefaultDevice) { const devoptab_t *devoptab = NULL; char name[128] = {0}; int i; // Get the device name from the path - strncpy( name, path, 127 ); - strtok( name, ":/" ); + strncpy(name, path, 127); + strtok(name, ":/"); // Search the devoptab table for the specified device name // NOTE: We do this manually due to a 'bug' in GetDeviceOpTab // which ignores names with suffixes and causes names // like "ntfs" and "ntfs1" to be seen as equals - for ( i = 0; i < STD_MAX; i++ ) - { + for (i = 0; i < STD_MAX; i++) { devoptab = devoptab_list[i]; - if ( devoptab && devoptab->name ) - { - if ( strcmp( name, devoptab->name ) == 0 ) - { + if (devoptab && devoptab->name) { + if (strcmp(name, devoptab->name) == 0) { return devoptab; } } @@ -170,40 +158,39 @@ const devoptab_t *ntfsGetDevice ( const char *path, bool useDefaultDevice ) // If we reach here then we couldn't find the device name, // chances are that this path has no device name in it. // Call GetDeviceOpTab to get our default device (chdir). - if ( useDefaultDevice ) - return GetDeviceOpTab( "" ); + if (useDefaultDevice) + return GetDeviceOpTab(""); return NULL; } -const INTERFACE_ID *ntfsGetDiscInterfaces ( void ) +const INTERFACE_ID *ntfsGetDiscInterfaces (void) { // Get all know disc interfaces on the host system return ntfs_disc_interfaces; } -ntfs_vd *ntfsGetVolume ( const char *path ) +ntfs_vd *ntfsGetVolume (const char *path) { // Get the volume descriptor from the paths associated devoptab (if found) const devoptab_t *devoptab_ntfs = ntfsGetDevOpTab(); - const devoptab_t *devoptab = ntfsGetDevice( path, true ); - if ( devoptab && devoptab_ntfs && ( devoptab->open_r == devoptab_ntfs->open_r ) ) - return ( ntfs_vd* )devoptab->deviceData; + const devoptab_t *devoptab = ntfsGetDevice(path, true); + if (devoptab && devoptab_ntfs && (devoptab->open_r == devoptab_ntfs->open_r)) + return (ntfs_vd*)devoptab->deviceData; return NULL; } -int ntfsInitVolume ( ntfs_vd *vd ) +int ntfsInitVolume (ntfs_vd *vd) { // Sanity check - if ( !vd ) - { + if (!vd) { errno = ENODEV; return -1; } // Initialise the volume lock - LWP_MutexInit( &vd->lock, false ); + LWP_MutexInit(&vd->lock, false); // Reset the volumes name cache vd->name[0] = '\0'; @@ -220,33 +207,30 @@ int ntfsInitVolume ( ntfs_vd *vd ) return 0; } -void ntfsDeinitVolume ( ntfs_vd *vd ) +void ntfsDeinitVolume (ntfs_vd *vd) { // Sanity check - if ( !vd ) - { + if (!vd) { errno = ENODEV; return; } // Lock - ntfsLock( vd ); + ntfsLock(vd); // Close any directories which are still open (lazy programmers!) ntfs_dir_state *nextDir = vd->firstOpenDir; - while ( nextDir ) - { - ntfs_log_warning( "Cleaning up orphaned directory @ %p\n", nextDir ); - ntfsCloseDir( nextDir ); + while (nextDir) { + ntfs_log_warning("Cleaning up orphaned directory @ %p\n", nextDir); + ntfsCloseDir(nextDir); nextDir = nextDir->nextOpenDir; } // Close any files which are still open (lazy programmers!) ntfs_file_state *nextFile = vd->firstOpenFile; - while ( nextFile ) - { - ntfs_log_warning( "Cleaning up orphaned file @ %p\n", nextFile ); - ntfsCloseFile( nextFile ); + while (nextFile) { + ntfs_log_warning("Cleaning up orphaned file @ %p\n", nextFile); + ntfsCloseFile(nextFile); nextFile = nextFile->nextOpenFile; } @@ -258,89 +242,81 @@ void ntfsDeinitVolume ( ntfs_vd *vd ) // Close the volumes current directory (if any) //if (vd->cwd_ni) { - //ntfsCloseEntry(vd, vd->cwd_ni); - //vd->cwd_ni = NULL; + //ntfsCloseEntry(vd, vd->cwd_ni); + //vd->cwd_ni = NULL; //} // Force the underlying device to sync - vd->dev->d_ops->sync( vd->dev ); + vd->dev->d_ops->sync(vd->dev); // Unlock - ntfsUnlock( vd ); + ntfsUnlock(vd); // Deinitialise the volume lock - LWP_MutexDestroy( vd->lock ); + LWP_MutexDestroy(vd->lock); return; } -ntfs_inode *ntfsOpenEntry ( ntfs_vd *vd, const char *path ) +ntfs_inode *ntfsOpenEntry (ntfs_vd *vd, const char *path) { - return ntfsParseEntry( vd, path, 1 ); + return ntfsParseEntry(vd, path, 1); } -ntfs_inode *ntfsParseEntry ( ntfs_vd *vd, const char *path, int reparseLevel ) +ntfs_inode *ntfsParseEntry (ntfs_vd *vd, const char *path, int reparseLevel) { ntfs_inode *ni = NULL; char *target = NULL; int attr_size; // Sanity check - if ( !vd ) - { + if (!vd) { errno = ENODEV; return NULL; } // Get the actual path of the entry - path = ntfsRealPath( path ); - if ( !path ) - { + path = ntfsRealPath(path); + if (!path) { errno = EINVAL; return NULL; - } - else if ( path[0] == '\0' ) - { + } else if (path[0] == '\0') { path = "."; } // Find the entry, taking into account our current directory (if any) - if ( path[0] != PATH_SEP ) - ni = ntfs_pathname_to_inode( vd->vol, vd->cwd_ni, path++ ); + if (path[0] != PATH_SEP) + ni = ntfs_pathname_to_inode(vd->vol, vd->cwd_ni, path++); else - ni = ntfs_pathname_to_inode( vd->vol, NULL, path ); + ni = ntfs_pathname_to_inode(vd->vol, NULL, path); // If the entry was found and it has reparse data then parse its true path; // this resolves the true location of symbolic links and directory junctions - if ( ni && ( ni->flags & FILE_ATTR_REPARSE_POINT ) ) - { - if ( ntfs_possible_symlink( ni ) ) - { + if (ni && (ni->flags & FILE_ATTR_REPARSE_POINT)) { + if (ntfs_possible_symlink(ni)) { // Sanity check, give up if we are parsing to deep - if ( reparseLevel > NTFS_MAX_SYMLINK_DEPTH ) - { - ntfsCloseEntry( vd, ni ); + if (reparseLevel > NTFS_MAX_SYMLINK_DEPTH) { + ntfsCloseEntry(vd, ni); errno = ELOOP; return NULL; } // Get the target path of this entry - target = ntfs_make_symlink( ni, path, &attr_size ); - if ( !target ) - { - ntfsCloseEntry( vd, ni ); + target = ntfs_make_symlink(ni, path, &attr_size); + if (!target) { + ntfsCloseEntry(vd, ni); return NULL; } // Close the entry (we are no longer interested in it) - ntfsCloseEntry( vd, ni ); + ntfsCloseEntry(vd, ni); // Parse the entries target - ni = ntfsParseEntry( vd, target, reparseLevel++ ); + ni = ntfsParseEntry(vd, target, reparseLevel++); // Clean up - ntfs_free( target ); + ntfs_free(target); } } @@ -348,33 +324,32 @@ ntfs_inode *ntfsParseEntry ( ntfs_vd *vd, const char *path, int reparseLevel ) return ni; } -void ntfsCloseEntry ( ntfs_vd *vd, ntfs_inode *ni ) +void ntfsCloseEntry (ntfs_vd *vd, ntfs_inode *ni) { // Sanity check - if ( !vd ) - { + if (!vd) { errno = ENODEV; return; } // Lock - ntfsLock( vd ); + ntfsLock(vd); // Sync the entry (if it is dirty) - if ( NInoDirty( ni ) ) - ntfsSync( vd, ni ); + if (NInoDirty(ni)) + ntfsSync(vd, ni); // Close the entry - ntfs_inode_close( ni ); + ntfs_inode_close(ni); // Unlock - ntfsUnlock( vd ); + ntfsUnlock(vd); return; } -ntfs_inode *ntfsCreate ( ntfs_vd *vd, const char *path, mode_t type, const char *target ) +ntfs_inode *ntfsCreate (ntfs_vd *vd, const char *path, mode_t type, const char *target) { ntfs_inode *dir_ni = NULL, *ni = NULL; char *dir = NULL; @@ -383,134 +358,123 @@ ntfs_inode *ntfsCreate ( ntfs_vd *vd, const char *path, mode_t type, const char int uname_len, utarget_len; // Sanity check - if ( !vd ) - { + if (!vd) { errno = ENODEV; return NULL; } // You cannot link between devices - if ( target ) - { - if ( vd != ntfsGetVolume( target ) ) - { + if(target) { + if(vd != ntfsGetVolume(target)) { errno = EXDEV; return NULL; } } // Get the actual paths of the entry - path = ntfsRealPath( path ); - target = ntfsRealPath( target ); - if ( !path ) - { + path = ntfsRealPath(path); + target = ntfsRealPath(target); + if (!path) { errno = EINVAL; return NULL; } // Lock - ntfsLock( vd ); + ntfsLock(vd); // Get the unicode name for the entry and find its parent directory // TODO: This looks horrible, clean it up - dir = strdup( path ); - if ( !dir ) - { + dir = strdup(path); + if (!dir) { errno = EINVAL; goto cleanup; } - name = strrchr( dir, '/' ); - if ( name ) + name = strrchr(dir, '/'); + if (name) name++; else name = dir; - uname_len = ntfsLocalToUnicode( name, &uname ); - if ( uname_len < 0 ) - { + uname_len = ntfsLocalToUnicode(name, &uname); + if (uname_len < 0) { errno = EINVAL; goto cleanup; } - name = strrchr( dir, '/' ); - if ( name ) + name = strrchr(dir, '/'); + if(name) { name++; name[0] = 0; } // Open the entries parent directory - dir_ni = ntfsOpenEntry( vd, dir ); - if ( !dir_ni ) - { + dir_ni = ntfsOpenEntry(vd, dir); + if (!dir_ni) { goto cleanup; } // Create the entry - switch ( type ) - { + switch (type) { - // Symbolic link + // Symbolic link case S_IFLNK: - if ( !target ) - { + if (!target) { errno = EINVAL; goto cleanup; } - utarget_len = ntfsLocalToUnicode( target, &utarget ); - if ( utarget_len < 0 ) - { + utarget_len = ntfsLocalToUnicode(target, &utarget); + if (utarget_len < 0) { errno = EINVAL; goto cleanup; } - ni = ntfs_create_symlink( dir_ni, 0, uname, uname_len, utarget, utarget_len ); + ni = ntfs_create_symlink(dir_ni, 0, uname, uname_len, utarget, utarget_len); break; - // Directory or file + // Directory or file case S_IFDIR: case S_IFREG: - ni = ntfs_create( dir_ni, 0, uname, uname_len, type ); + ni = ntfs_create(dir_ni, 0, uname, uname_len, type); break; } // If the entry was created - if ( ni ) - { + if (ni) { // Mark the entry for archiving ni->flags |= FILE_ATTR_ARCHIVE; // Mark the entry as dirty - NInoSetDirty( ni ); + NInoSetDirty(ni); // Sync the entry to disc - ntfsSync( vd, ni ); + ntfsSync(vd, ni); // Update parent directories times - ntfsUpdateTimes( vd, dir_ni, NTFS_UPDATE_MCTIME ); + ntfsUpdateTimes(vd, dir_ni, NTFS_UPDATE_MCTIME); } cleanup: - if ( dir_ni ) - ntfsCloseEntry( vd, dir_ni ); + if(dir_ni) + ntfsCloseEntry(vd, dir_ni); - if ( utarget ) - ntfs_free( utarget ); + if(utarget) + ntfs_free(utarget); - if ( uname ) - ntfs_free( uname ); + if(uname) + ntfs_free(uname); - if ( dir ) - ntfs_free( dir ); + if(dir) + ntfs_free(dir); // Unlock - ntfsUnlock( vd ); + ntfsUnlock(vd); return ni; } -int ntfsLink ( ntfs_vd *vd, const char *old_path, const char *new_path ) +int ntfsLink (ntfs_vd *vd, const char *old_path, const char *new_path) { ntfs_inode *dir_ni = NULL, *ni = NULL; char *dir = NULL; @@ -520,104 +484,96 @@ int ntfsLink ( ntfs_vd *vd, const char *old_path, const char *new_path ) int res = 0; // Sanity check - if ( !vd ) - { + if (!vd) { errno = ENODEV; return -1; } // You cannot link between devices - if ( vd != ntfsGetVolume( new_path ) ) - { + if(vd != ntfsGetVolume(new_path)) { errno = EXDEV; return -1; } // Get the actual paths of the entry - old_path = ntfsRealPath( old_path ); - new_path = ntfsRealPath( new_path ); - if ( !old_path || !new_path ) - { + old_path = ntfsRealPath(old_path); + new_path = ntfsRealPath(new_path); + if (!old_path || !new_path) { errno = EINVAL; return -1; } // Lock - ntfsLock( vd ); + ntfsLock(vd); // Get the unicode name for the entry and find its parent directory // TODO: This looks horrible, clean it up - dir = strdup( new_path ); - if ( !dir ) - { + dir = strdup(new_path); + if (!dir) { errno = EINVAL; goto cleanup; } - name = strrchr( dir, '/' ); - if ( name ) + name = strrchr(dir, '/'); + if (name) name++; else name = dir; - uname_len = ntfsLocalToUnicode( name, &uname ); - if ( uname_len < 0 ) - { + uname_len = ntfsLocalToUnicode(name, &uname); + if (uname_len < 0) { errno = EINVAL; goto cleanup; } *name = 0; // Find the entry - ni = ntfsOpenEntry( vd, old_path ); - if ( !ni ) - { + ni = ntfsOpenEntry(vd, old_path); + if (!ni) { errno = ENOENT; res = -1; goto cleanup; } // Open the entries new parent directory - dir_ni = ntfsOpenEntry( vd, dir ); - if ( !dir_ni ) - { + dir_ni = ntfsOpenEntry(vd, dir); + if (!dir_ni) { errno = ENOENT; res = -1; goto cleanup; } // Link the entry to its new parent - if ( ntfs_link( ni, dir_ni, uname, uname_len ) ) - { + if (ntfs_link(ni, dir_ni, uname, uname_len)) { res = -1; goto cleanup; } // Update entry times - ntfsUpdateTimes( vd, dir_ni, NTFS_UPDATE_MCTIME ); + ntfsUpdateTimes(vd, dir_ni, NTFS_UPDATE_MCTIME); // Sync the entry to disc - ntfsSync( vd, ni ); + ntfsSync(vd, ni); cleanup: - if ( dir_ni ) - ntfsCloseEntry( vd, dir_ni ); + if(dir_ni) + ntfsCloseEntry(vd, dir_ni); - if ( ni ) - ntfsCloseEntry( vd, ni ); + if(ni) + ntfsCloseEntry(vd, ni); - if ( uname ) - ntfs_free( uname ); + if(uname) + ntfs_free(uname); - if ( dir ) - ntfs_free( dir ); + if(dir) + ntfs_free(dir); // Unlock - ntfsUnlock( vd ); + ntfsUnlock(vd); return res; } -int ntfsUnlink ( ntfs_vd *vd, const char *path ) +int ntfsUnlink (ntfs_vd *vd, const char *path) { ntfs_inode *dir_ni = NULL, *ni = NULL; char *dir = NULL; @@ -627,185 +583,170 @@ int ntfsUnlink ( ntfs_vd *vd, const char *path ) int res = 0; // Sanity check - if ( !vd ) - { + if (!vd) { errno = ENODEV; return -1; } // Get the actual path of the entry - path = ntfsRealPath( path ); - if ( !path ) - { + path = ntfsRealPath(path); + if (!path) { errno = EINVAL; return -1; } // Lock - ntfsLock( vd ); + ntfsLock(vd); // Get the unicode name for the entry and find its parent directory // TODO: This looks horrible - dir = strdup( path ); - if ( !dir ) - { + dir = strdup(path); + if (!dir) { errno = EINVAL; goto cleanup; } - name = strrchr( dir, '/' ); - if ( name ) + name = strrchr(dir, '/'); + if (name) name++; else name = dir; - uname_len = ntfsLocalToUnicode( name, &uname ); - if ( uname_len < 0 ) - { + uname_len = ntfsLocalToUnicode(name, &uname); + if (uname_len < 0) { errno = EINVAL; goto cleanup; } - name = strrchr( dir, '/' ); - if ( name ) + name = strrchr(dir, '/'); + if(name) { name++; name[0] = 0; } // Find the entry - ni = ntfsOpenEntry( vd, path ); - if ( !ni ) - { + ni = ntfsOpenEntry(vd, path); + if (!ni) { errno = ENOENT; res = -1; goto cleanup; } // Open the entries parent directory - dir_ni = ntfsOpenEntry( vd, dir ); - if ( !dir_ni ) - { + dir_ni = ntfsOpenEntry(vd, dir); + if (!dir_ni) { errno = ENOENT; res = -1; goto cleanup; } // Unlink the entry from its parent - if ( ntfs_delete( vd->vol, path, ni, dir_ni, uname, uname_len ) ) - { + if (ntfs_delete(vd->vol, path, ni, dir_ni, uname, uname_len)) { res = -1; } // Force the underlying device to sync - vd->dev->d_ops->sync( vd->dev ); + vd->dev->d_ops->sync(vd->dev); // ntfs_delete() ALWAYS closes ni and dir_ni; so no need for us to anymore dir_ni = ni = NULL; cleanup: - if ( dir_ni ) - ntfsCloseEntry( vd, dir_ni ); + if(dir_ni) + ntfsCloseEntry(vd, dir_ni); - if ( ni ) - ntfsCloseEntry( vd, ni ); + if(ni) + ntfsCloseEntry(vd, ni); - if ( uname ) - ntfs_free( uname ); + if(uname) + ntfs_free(uname); - if ( dir ) - ntfs_free( dir ); + if(dir) + ntfs_free(dir); // Unlock - ntfsUnlock( vd ); + ntfsUnlock(vd); return 0; } -int ntfsSync ( ntfs_vd *vd, ntfs_inode *ni ) +int ntfsSync (ntfs_vd *vd, ntfs_inode *ni) { int res = 0; // Sanity check - if ( !vd ) - { + if (!vd) { errno = ENODEV; return -1; } // Sanity check - if ( !ni ) - { + if (!ni) { errno = ENOENT; return -1; } // Lock - ntfsLock( vd ); + ntfsLock(vd); // Sync the entry - res = ntfs_inode_sync( ni ); + res = ntfs_inode_sync(ni); // Force the underlying device to sync - vd->dev->d_ops->sync( vd->dev ); + vd->dev->d_ops->sync(vd->dev); // Unlock - ntfsUnlock( vd ); + ntfsUnlock(vd); return res; } -int ntfsStat ( ntfs_vd *vd, ntfs_inode *ni, struct stat *st ) +int ntfsStat (ntfs_vd *vd, ntfs_inode *ni, struct stat *st) { ntfs_attr *na = NULL; int res = 0; // Sanity check - if ( !vd ) - { + if (!vd) { errno = ENODEV; return -1; } // Sanity check - if ( !ni ) - { + if (!ni) { errno = ENOENT; return -1; } // Short circuit cases were we don't actually have to do anything - if ( !st ) + if (!st) return 0; // Lock - ntfsLock( vd ); + ntfsLock(vd); // Zero out the stat buffer - memset( st, 0, sizeof( struct stat ) ); + memset(st, 0, sizeof(struct stat)); // Is this entry a directory - if ( ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ) - { - st->st_mode = S_IFDIR | ( 0777 & ~vd->dmask ); + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + st->st_mode = S_IFDIR | (0777 & ~vd->dmask); st->st_nlink = 1; // Open the directories index allocation table attribute - na = ntfs_attr_open( ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4 ); - if ( na ) - { + na = ntfs_attr_open(ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4); + if (na) { st->st_size = na->data_size; st->st_blocks = na->allocated_size >> 9; - ntfs_attr_close( na ); + ntfs_attr_close(na); } - // Else it must be a file - } - else - { - st->st_mode = S_IFREG | ( 0777 & ~vd->fmask ); + // Else it must be a file + } else { + st->st_mode = S_IFREG | (0777 & ~vd->fmask); st->st_size = ni->data_size; - st->st_blocks = ( ni->allocated_size + 511 ) >> 9; - st->st_nlink = le16_to_cpu( ni->mrec->link_count ); + st->st_blocks = (ni->allocated_size + 511) >> 9; + st->st_nlink = le16_to_cpu(ni->mrec->link_count); } // Fill in the generic entry stats @@ -818,81 +759,74 @@ int ntfsStat ( ntfs_vd *vd, ntfs_inode *ni, struct stat *st ) st->st_mtime = ni->last_data_change_time; // Update entry times - ntfsUpdateTimes( vd, ni, NTFS_UPDATE_ATIME ); + ntfsUpdateTimes(vd, ni, NTFS_UPDATE_ATIME); // Unlock - ntfsUnlock( vd ); + ntfsUnlock(vd); return res; } -void ntfsUpdateTimes ( ntfs_vd *vd, ntfs_inode *ni, ntfs_time_update_flags mask ) +void ntfsUpdateTimes (ntfs_vd *vd, ntfs_inode *ni, ntfs_time_update_flags mask) { // Run the access time update strategy against the device driver settings first - if ( vd && vd->atime == ATIME_DISABLED ) + if (vd && vd->atime == ATIME_DISABLED) mask &= ~NTFS_UPDATE_ATIME; // Update entry times - if ( ni && mask ) - ntfs_inode_update_times( ni, mask ); + if (ni && mask) + ntfs_inode_update_times(ni, mask); return; } -const char *ntfsRealPath ( const char *path ) +const char *ntfsRealPath (const char *path) { // Sanity check - if ( !path ) + if (!path) return NULL; // Move the path pointer to the start of the actual path - if ( strchr( path, ':' ) != NULL ) - { - path = strchr( path, ':' ) + 1; + if (strchr(path, ':') != NULL) { + path = strchr(path, ':') + 1; } - if ( strchr( path, ':' ) != NULL ) - { + if (strchr(path, ':') != NULL) { return NULL; } return path; } -int ntfsUnicodeToLocal ( const ntfschar *ins, const int ins_len, char **outs, int outs_len ) +int ntfsUnicodeToLocal (const ntfschar *ins, const int ins_len, char **outs, int outs_len) { int len = 0; int i; // Sanity check - if ( !ins || !ins_len || !outs ) + if (!ins || !ins_len || !outs) return 0; // Convert the unicode string to our current local - len = ntfs_ucstombs( ins, ins_len, outs, outs_len ); - if ( len == -1 && errno == EILSEQ ) - { + len = ntfs_ucstombs(ins, ins_len, outs, outs_len); + if (len == -1 && errno == EILSEQ) { // The string could not be converted to the current local, // do it manually by replacing non-ASCII characters with underscores - if ( !*outs || outs_len >= ins_len ) - { - if ( !*outs ) - { - *outs = ( char * ) ntfs_alloc( ins_len + 1 ); - if ( !*outs ) - { + if (!*outs || outs_len >= ins_len) { + if (!*outs) { + *outs = (char *) ntfs_alloc(ins_len + 1); + if (!*outs) { errno = ENOMEM; return -1; } } - for ( i = 0; i < ins_len; i++ ) - { - ntfschar uc = le16_to_cpu( ins[i] ); - if ( uc > 0xff ) - uc = ( ntfschar )'_'; - *outs[i] = ( char )uc; + for (i = 0; i < ins_len; i++) { + ntfschar uc = le16_to_cpu(ins[i]); + if (uc > 0xff) + uc = (ntfschar)'_'; + *outs[i] = (char)uc; } - *outs[ins_len] = ( ntfschar )'\0'; + *outs[ins_len] = (ntfschar)'\0'; len = ins_len; } @@ -901,12 +835,12 @@ int ntfsUnicodeToLocal ( const ntfschar *ins, const int ins_len, char **outs, in return len; } -int ntfsLocalToUnicode ( const char *ins, ntfschar **outs ) +int ntfsLocalToUnicode (const char *ins, ntfschar **outs) { // Sanity check - if ( !ins || !outs ) + if (!ins || !outs) return 0; // Convert the local string to unicode - return ntfs_mbstoucs( ins, outs ); + return ntfs_mbstoucs(ins, outs); } diff --git a/source/libntfs/ntfsinternal.h b/source/libntfs/ntfsinternal.h index 46b9be20..11dfb8fd 100644 --- a/source/libntfs/ntfsinternal.h +++ b/source/libntfs/ntfsinternal.h @@ -69,43 +69,39 @@ struct _ntfs_dir_state; /** * PRIMARY_PARTITION - Block device partition record */ -typedef struct _PARTITION_RECORD -{ +typedef struct _PARTITION_RECORD { u8 status; /* Partition status; see above */ u8 chs_start[3]; /* Cylinder-head-sector address to first block of partition */ u8 type; /* Partition type; see above */ u8 chs_end[3]; /* Cylinder-head-sector address to last block of partition */ u32 lba_start; /* Local block address to first sector of partition */ u32 block_count; /* Number of blocks in partition */ -} __attribute__( ( __packed__ ) ) PARTITION_RECORD; +} __attribute__((__packed__)) PARTITION_RECORD; /** * MASTER_BOOT_RECORD - Block device master boot record */ -typedef struct _MASTER_BOOT_RECORD -{ +typedef struct _MASTER_BOOT_RECORD { u8 code_area[446]; /* Code area; normally empty */ PARTITION_RECORD partitions[4]; /* 4 primary partitions */ u16 signature; /* MBR signature; 0xAA55 */ -} __attribute__( ( __packed__ ) ) MASTER_BOOT_RECORD; +} __attribute__((__packed__)) MASTER_BOOT_RECORD; /** * EXTENDED_PARTITION - Block device extended boot record */ -typedef struct _EXTENDED_BOOT_RECORD -{ +typedef struct _EXTENDED_BOOT_RECORD { u8 code_area[446]; /* Code area; normally empty */ PARTITION_RECORD partition; /* Primary partition */ PARTITION_RECORD next_ebr; /* Next extended boot record in the chain */ u8 reserved[32]; /* Normally empty */ u16 signature; /* EBR signature; 0xAA55 */ -} __attribute__( ( __packed__ ) ) EXTENDED_BOOT_RECORD; +} __attribute__((__packed__)) EXTENDED_BOOT_RECORD; /** * INTERFACE_ID - Disc interface identifier */ -typedef struct _INTERFACE_ID -{ +typedef struct _INTERFACE_ID { const char *name; /* Interface name */ const DISC_INTERFACE *interface; /* Disc interface */ } INTERFACE_ID; @@ -113,8 +109,7 @@ typedef struct _INTERFACE_ID /** * ntfs_atime_t - File access time update strategies */ -typedef enum -{ +typedef enum { ATIME_ENABLED, /* Update access times */ ATIME_DISABLED /* Don't update access times */ } ntfs_atime_t; @@ -122,8 +117,7 @@ typedef enum /** * ntfs_vd - NTFS volume descriptor */ -typedef struct _ntfs_vd -{ +typedef struct _ntfs_vd { struct ntfs_device *dev; /* NTFS device handle */ ntfs_volume *vol; /* NTFS volume handle */ mutex_t lock; /* Volume lock mutex */ @@ -145,40 +139,40 @@ typedef struct _ntfs_vd } ntfs_vd; /* Lock volume */ -static inline void ntfsLock ( ntfs_vd *vd ) +static inline void ntfsLock (ntfs_vd *vd) { - LWP_MutexLock( vd->lock ); + LWP_MutexLock(vd->lock); } /* Unlock volume */ -static inline void ntfsUnlock ( ntfs_vd *vd ) +static inline void ntfsUnlock (ntfs_vd *vd) { - LWP_MutexUnlock( vd->lock ); + LWP_MutexUnlock(vd->lock); } /* Gekko device related routines */ -int ntfsAddDevice ( const char *name, void *deviceData ); -void ntfsRemoveDevice ( const char *path ); -const devoptab_t *ntfsGetDevice ( const char *path, bool useDefaultDevice ); -const devoptab_t *ntfsGetDevOpTab ( void ); -const INTERFACE_ID* ntfsGetDiscInterfaces ( void ); +int ntfsAddDevice (const char *name, void *deviceData); +void ntfsRemoveDevice (const char *path); +const devoptab_t *ntfsGetDevice (const char *path, bool useDefaultDevice); +const devoptab_t *ntfsGetDevOpTab (void); +const INTERFACE_ID* ntfsGetDiscInterfaces (void); /* Miscellaneous helper/support routines */ -int ntfsInitVolume ( ntfs_vd *vd ); -void ntfsDeinitVolume ( ntfs_vd *vd ); -ntfs_vd *ntfsGetVolume ( const char *path ); -ntfs_inode *ntfsOpenEntry ( ntfs_vd *vd, const char *path ); -ntfs_inode *ntfsParseEntry ( ntfs_vd *vd, const char *path, int reparseLevel ); -void ntfsCloseEntry ( ntfs_vd *vd, ntfs_inode *ni ); -ntfs_inode *ntfsCreate ( ntfs_vd *vd, const char *path, mode_t type, const char *target ); -int ntfsLink ( ntfs_vd *vd, const char *old_path, const char *new_path ); -int ntfsUnlink ( ntfs_vd *vd, const char *path ); -int ntfsSync ( ntfs_vd *vd, ntfs_inode *ni ); -int ntfsStat ( ntfs_vd *vd, ntfs_inode *ni, struct stat *st ); -void ntfsUpdateTimes ( ntfs_vd *vd, ntfs_inode *ni, ntfs_time_update_flags mask ); +int ntfsInitVolume (ntfs_vd *vd); +void ntfsDeinitVolume (ntfs_vd *vd); +ntfs_vd *ntfsGetVolume (const char *path); +ntfs_inode *ntfsOpenEntry (ntfs_vd *vd, const char *path); +ntfs_inode *ntfsParseEntry (ntfs_vd *vd, const char *path, int reparseLevel); +void ntfsCloseEntry (ntfs_vd *vd, ntfs_inode *ni); +ntfs_inode *ntfsCreate (ntfs_vd *vd, const char *path, mode_t type, const char *target); +int ntfsLink (ntfs_vd *vd, const char *old_path, const char *new_path); +int ntfsUnlink (ntfs_vd *vd, const char *path); +int ntfsSync (ntfs_vd *vd, ntfs_inode *ni); +int ntfsStat (ntfs_vd *vd, ntfs_inode *ni, struct stat *st); +void ntfsUpdateTimes (ntfs_vd *vd, ntfs_inode *ni, ntfs_time_update_flags mask); -const char *ntfsRealPath ( const char *path ); -int ntfsUnicodeToLocal ( const ntfschar *ins, const int ins_len, char **outs, int outs_len ); -int ntfsLocalToUnicode ( const char *ins, ntfschar **outs ); +const char *ntfsRealPath (const char *path); +int ntfsUnicodeToLocal (const ntfschar *ins, const int ins_len, char **outs, int outs_len); +int ntfsLocalToUnicode (const char *ins, ntfschar **outs); #endif /* _NTFSINTERNAL_H */ diff --git a/source/libntfs/ntfstime.h b/source/libntfs/ntfstime.h index ffc4a24c..426269d7 100644 --- a/source/libntfs/ntfstime.h +++ b/source/libntfs/ntfstime.h @@ -55,19 +55,19 @@ typedef sle64 ntfs_time; * * Return: A Unix time (number of seconds since 1970, and nanoseconds) */ -static __inline__ struct timespec ntfs2timespec( ntfs_time ntfstime ) +static __inline__ struct timespec ntfs2timespec(ntfs_time ntfstime) { - struct timespec spec; - s64 cputime; + struct timespec spec; + s64 cputime; - cputime = sle64_to_cpu( ntfstime ); - spec.tv_sec = ( cputime - ( NTFS_TIME_OFFSET ) ) / 10000000; - spec.tv_nsec = ( cputime - ( NTFS_TIME_OFFSET ) - - ( s64 )spec.tv_sec*10000000 )*100; - /* force zero nsec for overflowing dates */ - if ( ( spec.tv_nsec < 0 ) || ( spec.tv_nsec > 999999999 ) ) - spec.tv_nsec = 0; - return ( spec ); + cputime = sle64_to_cpu(ntfstime); + spec.tv_sec = (cputime - (NTFS_TIME_OFFSET)) / 10000000; + spec.tv_nsec = (cputime - (NTFS_TIME_OFFSET) + - (s64)spec.tv_sec*10000000)*100; + /* force zero nsec for overflowing dates */ + if ((spec.tv_nsec < 0) || (spec.tv_nsec > 999999999)) + spec.tv_nsec = 0; + return (spec); } /** @@ -86,36 +86,36 @@ static __inline__ struct timespec ntfs2timespec( ntfs_time ntfstime ) * * Return: An NTFS time (100ns units since Jan 1601) */ -static __inline__ ntfs_time timespec2ntfs( struct timespec spec ) +static __inline__ ntfs_time timespec2ntfs(struct timespec spec) { - s64 units; + s64 units; - units = ( s64 )spec.tv_sec * 10000000 - + NTFS_TIME_OFFSET + spec.tv_nsec / 100; - return ( cpu_to_le64( units ) ); + units = (s64)spec.tv_sec * 10000000 + + NTFS_TIME_OFFSET + spec.tv_nsec/100; + return (cpu_to_le64(units)); } /* - * Return the current time in ntfs format + * Return the current time in ntfs format */ -static __inline__ ntfs_time ntfs_current_time( void ) +static __inline__ ntfs_time ntfs_current_time(void) { - struct timespec now; + struct timespec now; #if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_SYS_CLOCK_GETTIME) - clock_gettime( CLOCK_REALTIME, &now ); + clock_gettime(CLOCK_REALTIME, &now); #elif defined(HAVE_GETTIMEOFDAY) - struct timeval microseconds; + struct timeval microseconds; - gettimeofday( µseconds, ( struct timezone* )NULL ); - now.tv_sec = microseconds.tv_sec; - now.tv_nsec = microseconds.tv_usec * 1000; + gettimeofday(µseconds, (struct timezone*)NULL); + now.tv_sec = microseconds.tv_sec; + now.tv_nsec = microseconds.tv_usec*1000; #else - now.tv_sec = time( ( time_t* )NULL ); - now.tv_nsec = 0; + now.tv_sec = time((time_t*)NULL); + now.tv_nsec = 0; #endif - return ( timespec2ntfs( now ) ); + return (timespec2ntfs(now)); } #endif /* _NTFS_NTFSTIME_H */ diff --git a/source/libntfs/object_id.c b/source/libntfs/object_id.c index 28a9ddba..555dd137 100644 --- a/source/libntfs/object_id.c +++ b/source/libntfs/object_id.c @@ -1,7 +1,7 @@ /** * object_id.c - Processing of object ids * - * This module is part of ntfs-3g library + * This module is part of ntfs-3g library * * Copyright (c) 2009 Jean-Pierre Andre * @@ -60,15 +60,15 @@ #include "misc.h" /* - * Endianness considerations + * Endianness considerations * - * According to RFC 4122, GUIDs should be printed with the most - * significant byte first, and the six fields be compared individually - * for ordering. RFC 4122 does not define the internal representation. + * According to RFC 4122, GUIDs should be printed with the most + * significant byte first, and the six fields be compared individually + * for ordering. RFC 4122 does not define the internal representation. * - * Here we always copy disk images with no endianness change, - * and, for indexing, GUIDs are compared as if they were a sequence - * of four unsigned 32 bit integers. + * Here we always copy disk images with no endianness change, + * and, for indexing, GUIDs are compared as if they were a sequence + * of four unsigned 32 bit integers. * * --------------------- begin from RFC 4122 ---------------------- * Consider each field of the UUID to be an unsigned integer as shown @@ -106,592 +106,532 @@ * ---------------------- end from RFC 4122 ----------------------- */ -typedef struct -{ - GUID object_id; +typedef struct { + GUID object_id; } OBJECT_ID_INDEX_KEY; -typedef struct -{ - le64 file_id; - GUID birth_volume_id; - GUID birth_object_id; - GUID domain_id; +typedef struct { + le64 file_id; + GUID birth_volume_id; + GUID birth_object_id; + GUID domain_id; } OBJECT_ID_INDEX_DATA; // known as OBJ_ID_INDEX_DATA -struct OBJECT_ID_INDEX /* index entry in $Extend/$ObjId */ -{ - INDEX_ENTRY_HEADER header; - OBJECT_ID_INDEX_KEY key; - OBJECT_ID_INDEX_DATA data; +struct OBJECT_ID_INDEX { /* index entry in $Extend/$ObjId */ + INDEX_ENTRY_HEADER header; + OBJECT_ID_INDEX_KEY key; + OBJECT_ID_INDEX_DATA data; } ; -static ntfschar objid_index_name[] = { const_cpu_to_le16( '$' ), - const_cpu_to_le16( 'O' ) - }; -#ifdef HAVE_SETXATTR /* extended attributes interface required */ +static ntfschar objid_index_name[] = { const_cpu_to_le16('$'), + const_cpu_to_le16('O') }; +#ifdef HAVE_SETXATTR /* extended attributes interface required */ /* - * Set the index for a new object id + * Set the index for a new object id * - * Returns 0 if success - * -1 if failure, explained by errno + * Returns 0 if success + * -1 if failure, explained by errno */ -static int set_object_id_index( ntfs_inode *ni, ntfs_index_context *xo, - const OBJECT_ID_ATTR *object_id ) +static int set_object_id_index(ntfs_inode *ni, ntfs_index_context *xo, + const OBJECT_ID_ATTR *object_id) { - struct OBJECT_ID_INDEX indx; - u64 file_id_cpu; - le64 file_id; - le16 seqn; + struct OBJECT_ID_INDEX indx; + u64 file_id_cpu; + le64 file_id; + le16 seqn; - seqn = ni->mrec->sequence_number; - file_id_cpu = MK_MREF( ni->mft_no, le16_to_cpu( seqn ) ); - file_id = cpu_to_le64( file_id_cpu ); - indx.header.data_offset = const_cpu_to_le16( - sizeof( INDEX_ENTRY_HEADER ) - + sizeof( OBJECT_ID_INDEX_KEY ) ); - indx.header.data_length = const_cpu_to_le16( - sizeof( OBJECT_ID_INDEX_DATA ) ); - indx.header.reservedV = const_cpu_to_le32( 0 ); - indx.header.length = const_cpu_to_le16( - sizeof( struct OBJECT_ID_INDEX ) ); - indx.header.key_length = const_cpu_to_le16( - sizeof( OBJECT_ID_INDEX_KEY ) ); - indx.header.flags = const_cpu_to_le16( 0 ); - indx.header.reserved = const_cpu_to_le16( 0 ); + seqn = ni->mrec->sequence_number; + file_id_cpu = MK_MREF(ni->mft_no,le16_to_cpu(seqn)); + file_id = cpu_to_le64(file_id_cpu); + indx.header.data_offset = const_cpu_to_le16( + sizeof(INDEX_ENTRY_HEADER) + + sizeof(OBJECT_ID_INDEX_KEY)); + indx.header.data_length = const_cpu_to_le16( + sizeof(OBJECT_ID_INDEX_DATA)); + indx.header.reservedV = const_cpu_to_le32(0); + indx.header.length = const_cpu_to_le16( + sizeof(struct OBJECT_ID_INDEX)); + indx.header.key_length = const_cpu_to_le16( + sizeof(OBJECT_ID_INDEX_KEY)); + indx.header.flags = const_cpu_to_le16(0); + indx.header.reserved = const_cpu_to_le16(0); - memcpy( &indx.key.object_id, object_id, sizeof( GUID ) ); + memcpy(&indx.key.object_id,object_id,sizeof(GUID)); - indx.data.file_id = file_id; - memcpy( &indx.data.birth_volume_id, - &object_id->birth_volume_id, sizeof( GUID ) ); - memcpy( &indx.data.birth_object_id, - &object_id->birth_object_id, sizeof( GUID ) ); - memcpy( &indx.data.domain_id, - &object_id->domain_id, sizeof( GUID ) ); - ntfs_index_ctx_reinit( xo ); - return ( ntfs_ie_add( xo, ( INDEX_ENTRY* )&indx ) ); + indx.data.file_id = file_id; + memcpy(&indx.data.birth_volume_id, + &object_id->birth_volume_id,sizeof(GUID)); + memcpy(&indx.data.birth_object_id, + &object_id->birth_object_id,sizeof(GUID)); + memcpy(&indx.data.domain_id, + &object_id->domain_id,sizeof(GUID)); + ntfs_index_ctx_reinit(xo); + return (ntfs_ie_add(xo,(INDEX_ENTRY*)&indx)); } #endif /* HAVE_SETXATTR */ /* - * Open the $Extend/$ObjId file and its index + * Open the $Extend/$ObjId file and its index * - * Return the index context if opened - * or NULL if an error occurred (errno tells why) + * Return the index context if opened + * or NULL if an error occurred (errno tells why) * - * The index has to be freed and inode closed when not needed any more. + * The index has to be freed and inode closed when not needed any more. */ -static ntfs_index_context *open_object_id_index( ntfs_volume *vol ) +static ntfs_index_context *open_object_id_index(ntfs_volume *vol) { - u64 inum; - ntfs_inode *ni; - ntfs_inode *dir_ni; - ntfs_index_context *xo; + u64 inum; + ntfs_inode *ni; + ntfs_inode *dir_ni; + ntfs_index_context *xo; - /* do not use path_name_to inode - could reopen root */ - dir_ni = ntfs_inode_open( vol, FILE_Extend ); - ni = ( ntfs_inode* )NULL; - if ( dir_ni ) - { - inum = ntfs_inode_lookup_by_mbsname( dir_ni, "$ObjId" ); - if ( inum != ( u64 ) - 1 ) - ni = ntfs_inode_open( vol, inum ); - ntfs_inode_close( dir_ni ); - } - if ( ni ) - { - xo = ntfs_index_ctx_get( ni, objid_index_name, 2 ); - if ( !xo ) - { - ntfs_inode_close( ni ); - } - } - else - xo = ( ntfs_index_context* )NULL; - return ( xo ); + /* do not use path_name_to inode - could reopen root */ + dir_ni = ntfs_inode_open(vol, FILE_Extend); + ni = (ntfs_inode*)NULL; + if (dir_ni) { + inum = ntfs_inode_lookup_by_mbsname(dir_ni,"$ObjId"); + if (inum != (u64)-1) + ni = ntfs_inode_open(vol, inum); + ntfs_inode_close(dir_ni); + } + if (ni) { + xo = ntfs_index_ctx_get(ni, objid_index_name, 2); + if (!xo) { + ntfs_inode_close(ni); + } + } else + xo = (ntfs_index_context*)NULL; + return (xo); } -#ifdef HAVE_SETXATTR /* extended attributes interface required */ +#ifdef HAVE_SETXATTR /* extended attributes interface required */ /* - * Merge object_id data stored in the index into - * a full object_id struct. + * Merge object_id data stored in the index into + * a full object_id struct. * - * returns 0 if merging successful - * -1 if no data could be merged. This is generally not an error + * returns 0 if merging successful + * -1 if no data could be merged. This is generally not an error */ -static int merge_index_data( ntfs_inode *ni, - const OBJECT_ID_ATTR *objectid_attr, - OBJECT_ID_ATTR *full_objectid ) +static int merge_index_data(ntfs_inode *ni, + const OBJECT_ID_ATTR *objectid_attr, + OBJECT_ID_ATTR *full_objectid) { - OBJECT_ID_INDEX_KEY key; - struct OBJECT_ID_INDEX *entry; - ntfs_index_context *xo; - ntfs_inode *xoni; - int res; + OBJECT_ID_INDEX_KEY key; + struct OBJECT_ID_INDEX *entry; + ntfs_index_context *xo; + ntfs_inode *xoni; + int res; - res = -1; - xo = open_object_id_index( ni->vol ); - if ( xo ) - { - memcpy( &key.object_id, objectid_attr, sizeof( GUID ) ); - if ( !ntfs_index_lookup( &key, - sizeof( OBJECT_ID_INDEX_KEY ), xo ) ) - { - entry = ( struct OBJECT_ID_INDEX* )xo->entry; - /* make sure inode numbers match */ - if ( entry - && ( MREF( le64_to_cpu( entry->data.file_id ) ) - == ni->mft_no ) ) - { - memcpy( &full_objectid->birth_volume_id, - &entry->data.birth_volume_id, - sizeof( GUID ) ); - memcpy( &full_objectid->birth_object_id, - &entry->data.birth_object_id, - sizeof( GUID ) ); - memcpy( &full_objectid->domain_id, - &entry->data.domain_id, - sizeof( GUID ) ); - res = 0; - } - } - xoni = xo->ni; - ntfs_index_ctx_put( xo ); - ntfs_inode_close( xoni ); - } - return ( res ); + res = -1; + xo = open_object_id_index(ni->vol); + if (xo) { + memcpy(&key.object_id,objectid_attr,sizeof(GUID)); + if (!ntfs_index_lookup(&key, + sizeof(OBJECT_ID_INDEX_KEY), xo)) { + entry = (struct OBJECT_ID_INDEX*)xo->entry; + /* make sure inode numbers match */ + if (entry + && (MREF(le64_to_cpu(entry->data.file_id)) + == ni->mft_no)) { + memcpy(&full_objectid->birth_volume_id, + &entry->data.birth_volume_id, + sizeof(GUID)); + memcpy(&full_objectid->birth_object_id, + &entry->data.birth_object_id, + sizeof(GUID)); + memcpy(&full_objectid->domain_id, + &entry->data.domain_id, + sizeof(GUID)); + res = 0; + } + } + xoni = xo->ni; + ntfs_index_ctx_put(xo); + ntfs_inode_close(xoni); + } + return (res); } #endif /* HAVE_SETXATTR */ /* - * Remove an object id index entry if attribute present + * Remove an object id index entry if attribute present * - * Returns the size of existing object id - * (the existing object_d is returned) - * -1 if failure, explained by errno + * Returns the size of existing object id + * (the existing object_d is returned) + * -1 if failure, explained by errno */ -static int remove_object_id_index( ntfs_attr *na, ntfs_index_context *xo, - OBJECT_ID_ATTR *old_attr ) +static int remove_object_id_index(ntfs_attr *na, ntfs_index_context *xo, + OBJECT_ID_ATTR *old_attr) { - OBJECT_ID_INDEX_KEY key; - struct OBJECT_ID_INDEX *entry; - s64 size; - int ret; + OBJECT_ID_INDEX_KEY key; + struct OBJECT_ID_INDEX *entry; + s64 size; + int ret; - ret = na->data_size; - if ( ret ) - { - /* read the existing object id attribute */ - size = ntfs_attr_pread( na, 0, sizeof( GUID ), old_attr ); - if ( size >= ( s64 )sizeof( GUID ) ) - { - memcpy( &key.object_id, - &old_attr->object_id, sizeof( GUID ) ); - size = sizeof( GUID ); - if ( !ntfs_index_lookup( &key, - sizeof( OBJECT_ID_INDEX_KEY ), xo ) ) - { - entry = ( struct OBJECT_ID_INDEX* )xo->entry; - memcpy( &old_attr->birth_volume_id, - &entry->data.birth_volume_id, - sizeof( GUID ) ); - memcpy( &old_attr->birth_object_id, - &entry->data.birth_object_id, - sizeof( GUID ) ); - memcpy( &old_attr->domain_id, - &entry->data.domain_id, - sizeof( GUID ) ); - size = sizeof( OBJECT_ID_ATTR ); - if ( ntfs_index_rm( xo ) ) - ret = -1; - } - } - else - { - ret = -1; - errno = ENODATA; - } - } - return ( ret ); + ret = na->data_size; + if (ret) { + /* read the existing object id attribute */ + size = ntfs_attr_pread(na, 0, sizeof(GUID), old_attr); + if (size >= (s64)sizeof(GUID)) { + memcpy(&key.object_id, + &old_attr->object_id,sizeof(GUID)); + size = sizeof(GUID); + if (!ntfs_index_lookup(&key, + sizeof(OBJECT_ID_INDEX_KEY), xo)) { + entry = (struct OBJECT_ID_INDEX*)xo->entry; + memcpy(&old_attr->birth_volume_id, + &entry->data.birth_volume_id, + sizeof(GUID)); + memcpy(&old_attr->birth_object_id, + &entry->data.birth_object_id, + sizeof(GUID)); + memcpy(&old_attr->domain_id, + &entry->data.domain_id, + sizeof(GUID)); + size = sizeof(OBJECT_ID_ATTR); + if (ntfs_index_rm(xo)) + ret = -1; + } + } else { + ret = -1; + errno = ENODATA; + } + } + return (ret); } -#ifdef HAVE_SETXATTR /* extended attributes interface required */ +#ifdef HAVE_SETXATTR /* extended attributes interface required */ /* - * Update the object id and index + * Update the object id and index * - * The object_id attribute should have been created and the - * non-duplication of the GUID should have been checked before. + * The object_id attribute should have been created and the + * non-duplication of the GUID should have been checked before. * - * Returns 0 if success - * -1 if failure, explained by errno - * If could not remove the existing index, nothing is done, - * If could not write the new data, no index entry is inserted - * If failed to insert the index, data is removed + * Returns 0 if success + * -1 if failure, explained by errno + * If could not remove the existing index, nothing is done, + * If could not write the new data, no index entry is inserted + * If failed to insert the index, data is removed */ -static int update_object_id( ntfs_inode *ni, ntfs_index_context *xo, - const OBJECT_ID_ATTR *value, size_t size ) +static int update_object_id(ntfs_inode *ni, ntfs_index_context *xo, + const OBJECT_ID_ATTR *value, size_t size) { - OBJECT_ID_ATTR old_attr; - ntfs_attr *na; - int oldsize; - int written; - int res; + OBJECT_ID_ATTR old_attr; + ntfs_attr *na; + int oldsize; + int written; + int res; - res = 0; + res = 0; - na = ntfs_attr_open( ni, AT_OBJECT_ID, AT_UNNAMED, 0 ); - if ( na ) - { + na = ntfs_attr_open(ni, AT_OBJECT_ID, AT_UNNAMED, 0); + if (na) { - /* remove the existing index entry */ - oldsize = remove_object_id_index( na, xo, &old_attr ); - if ( oldsize < 0 ) - res = -1; - else - { - /* resize attribute */ - res = ntfs_attr_truncate( na, ( s64 )sizeof( GUID ) ); - /* write the object_id in attribute */ - if ( !res && value ) - { - written = ( int )ntfs_attr_pwrite( na, - ( s64 )0, ( s64 )sizeof( GUID ), - &value->object_id ); - if ( written != ( s64 )sizeof( GUID ) ) - { - ntfs_log_error( "Failed to update " - "object id\n" ); - errno = EIO; - res = -1; - } - } - /* write index part if provided */ - if ( !res - && ( ( size < sizeof( OBJECT_ID_ATTR ) ) - || set_object_id_index( ni, xo, value ) ) ) - { - /* - * If cannot index, try to remove the object - * id and log the error. There will be an - * inconsistency if removal fails. - */ - ntfs_attr_rm( na ); - ntfs_log_error( "Failed to index object id." - " Possible corruption.\n" ); - } - } - ntfs_attr_close( na ); - NInoSetDirty( ni ); - } - else - res = -1; - return ( res ); + /* remove the existing index entry */ + oldsize = remove_object_id_index(na,xo,&old_attr); + if (oldsize < 0) + res = -1; + else { + /* resize attribute */ + res = ntfs_attr_truncate(na, (s64)sizeof(GUID)); + /* write the object_id in attribute */ + if (!res && value) { + written = (int)ntfs_attr_pwrite(na, + (s64)0, (s64)sizeof(GUID), + &value->object_id); + if (written != (s64)sizeof(GUID)) { + ntfs_log_error("Failed to update " + "object id\n"); + errno = EIO; + res = -1; + } + } + /* write index part if provided */ + if (!res + && ((size < sizeof(OBJECT_ID_ATTR)) + || set_object_id_index(ni,xo,value))) { + /* + * If cannot index, try to remove the object + * id and log the error. There will be an + * inconsistency if removal fails. + */ + ntfs_attr_rm(na); + ntfs_log_error("Failed to index object id." + " Possible corruption.\n"); + } + } + ntfs_attr_close(na); + NInoSetDirty(ni); + } else + res = -1; + return (res); } /* - * Add a (dummy) object id to an inode if it does not exist + * Add a (dummy) object id to an inode if it does not exist * - * returns 0 if attribute was inserted (or already present) - * -1 if adding failed (explained by errno) + * returns 0 if attribute was inserted (or already present) + * -1 if adding failed (explained by errno) */ -static int add_object_id( ntfs_inode *ni, int flags ) +static int add_object_id(ntfs_inode *ni, int flags) { - int res; - u8 dummy; + int res; + u8 dummy; - res = -1; /* default return */ - if ( !ntfs_attr_exist( ni, AT_OBJECT_ID, AT_UNNAMED, 0 ) ) - { - if ( !( flags & XATTR_REPLACE ) ) - { - /* - * no object id attribute : add one, - * apparently, this does not feed the new value in - * Note : NTFS version must be >= 3 - */ - if ( ni->vol->major_ver >= 3 ) - { - res = ntfs_attr_add( ni, AT_OBJECT_ID, - AT_UNNAMED, 0, &dummy, ( s64 )0 ); - NInoSetDirty( ni ); - } - else - errno = EOPNOTSUPP; - } - else - errno = ENODATA; - } - else - { - if ( flags & XATTR_CREATE ) - errno = EEXIST; - else - res = 0; - } - return ( res ); + res = -1; /* default return */ + if (!ntfs_attr_exist(ni,AT_OBJECT_ID, AT_UNNAMED,0)) { + if (!(flags & XATTR_REPLACE)) { + /* + * no object id attribute : add one, + * apparently, this does not feed the new value in + * Note : NTFS version must be >= 3 + */ + if (ni->vol->major_ver >= 3) { + res = ntfs_attr_add(ni, AT_OBJECT_ID, + AT_UNNAMED, 0, &dummy, (s64)0); + NInoSetDirty(ni); + } else + errno = EOPNOTSUPP; + } else + errno = ENODATA; + } else { + if (flags & XATTR_CREATE) + errno = EEXIST; + else + res = 0; + } + return (res); } #endif /* HAVE_SETXATTR */ /* - * Delete an object_id index entry + * Delete an object_id index entry * - * Returns 0 if success - * -1 if failure, explained by errno + * Returns 0 if success + * -1 if failure, explained by errno */ -int ntfs_delete_object_id_index( ntfs_inode *ni ) +int ntfs_delete_object_id_index(ntfs_inode *ni) { - ntfs_index_context *xo; - ntfs_inode *xoni; - ntfs_attr *na; - OBJECT_ID_ATTR old_attr; - int res; + ntfs_index_context *xo; + ntfs_inode *xoni; + ntfs_attr *na; + OBJECT_ID_ATTR old_attr; + int res; - res = 0; - na = ntfs_attr_open( ni, AT_OBJECT_ID, AT_UNNAMED, 0 ); - if ( na ) - { - /* - * read the existing object id - * and un-index it - */ - xo = open_object_id_index( ni->vol ); - if ( xo ) - { - if ( remove_object_id_index( na, xo, &old_attr ) < 0 ) - res = -1; - xoni = xo->ni; - ntfs_index_entry_mark_dirty( xo ); - NInoSetDirty( xoni ); - ntfs_index_ctx_put( xo ); - ntfs_inode_close( xoni ); - } - ntfs_attr_close( na ); - } - return ( res ); + res = 0; + na = ntfs_attr_open(ni, AT_OBJECT_ID, AT_UNNAMED, 0); + if (na) { + /* + * read the existing object id + * and un-index it + */ + xo = open_object_id_index(ni->vol); + if (xo) { + if (remove_object_id_index(na,xo,&old_attr) < 0) + res = -1; + xoni = xo->ni; + ntfs_index_entry_mark_dirty(xo); + NInoSetDirty(xoni); + ntfs_index_ctx_put(xo); + ntfs_inode_close(xoni); + } + ntfs_attr_close(na); + } + return (res); } -#ifdef HAVE_SETXATTR /* extended attributes interface required */ +#ifdef HAVE_SETXATTR /* extended attributes interface required */ /* - * Get the ntfs object id into an extended attribute + * Get the ntfs object id into an extended attribute * - * If present, the object_id from the attribute and the GUIDs - * from the index are returned (formatted as OBJECT_ID_ATTR) + * If present, the object_id from the attribute and the GUIDs + * from the index are returned (formatted as OBJECT_ID_ATTR) * - * Returns the global size (can be 0, 16 or 64) - * and the buffer is updated if it is long enough + * Returns the global size (can be 0, 16 or 64) + * and the buffer is updated if it is long enough */ -int ntfs_get_ntfs_object_id( ntfs_inode *ni, char *value, size_t size ) +int ntfs_get_ntfs_object_id(ntfs_inode *ni, char *value, size_t size) { - OBJECT_ID_ATTR full_objectid; - OBJECT_ID_ATTR *objectid_attr; - s64 attr_size; - int full_size; + OBJECT_ID_ATTR full_objectid; + OBJECT_ID_ATTR *objectid_attr; + s64 attr_size; + int full_size; - full_size = 0; /* default to no data and some error to be defined */ - if ( ni ) - { - objectid_attr = ( OBJECT_ID_ATTR* )ntfs_attr_readall( ni, - AT_OBJECT_ID, ( ntfschar* )NULL, 0, &attr_size ); - if ( objectid_attr ) - { - /* restrict to only GUID present in attr */ - if ( attr_size == sizeof( GUID ) ) - { - memcpy( &full_objectid.object_id, - objectid_attr, sizeof( GUID ) ); - full_size = sizeof( GUID ); - /* get data from index, if any */ - if ( !merge_index_data( ni, objectid_attr, - &full_objectid ) ) - { - full_size = sizeof( OBJECT_ID_ATTR ); - } - if ( full_size <= ( s64 )size ) - { - if ( value ) - memcpy( value, &full_objectid, - full_size ); - else - errno = EINVAL; - } - } - else - { - /* unexpected size, better return unsupported */ - errno = EOPNOTSUPP; - full_size = 0; - } - free( objectid_attr ); - } - else - errno = ENODATA; - } - return ( full_size ? ( int )full_size : -errno ); + full_size = 0; /* default to no data and some error to be defined */ + if (ni) { + objectid_attr = (OBJECT_ID_ATTR*)ntfs_attr_readall(ni, + AT_OBJECT_ID,(ntfschar*)NULL, 0, &attr_size); + if (objectid_attr) { + /* restrict to only GUID present in attr */ + if (attr_size == sizeof(GUID)) { + memcpy(&full_objectid.object_id, + objectid_attr,sizeof(GUID)); + full_size = sizeof(GUID); + /* get data from index, if any */ + if (!merge_index_data(ni, objectid_attr, + &full_objectid)) { + full_size = sizeof(OBJECT_ID_ATTR); + } + if (full_size <= (s64)size) { + if (value) + memcpy(value,&full_objectid, + full_size); + else + errno = EINVAL; + } + } else { + /* unexpected size, better return unsupported */ + errno = EOPNOTSUPP; + full_size = 0; + } + free(objectid_attr); + } else + errno = ENODATA; + } + return (full_size ? (int)full_size : -errno); } /* - * Set the object id from an extended attribute + * Set the object id from an extended attribute * - * If the size is 64, the attribute and index are set. - * else if the size is not less than 16 only the attribute is set. - * The object id index is set accordingly. + * If the size is 64, the attribute and index are set. + * else if the size is not less than 16 only the attribute is set. + * The object id index is set accordingly. * - * Returns 0, or -1 if there is a problem + * Returns 0, or -1 if there is a problem */ -int ntfs_set_ntfs_object_id( ntfs_inode *ni, - const char *value, size_t size, int flags ) +int ntfs_set_ntfs_object_id(ntfs_inode *ni, + const char *value, size_t size, int flags) { - OBJECT_ID_INDEX_KEY key; - ntfs_inode *xoni; - ntfs_index_context *xo; - int res; + OBJECT_ID_INDEX_KEY key; + ntfs_inode *xoni; + ntfs_index_context *xo; + int res; - res = 0; - if ( ni && value && ( size >= sizeof( GUID ) ) ) - { - xo = open_object_id_index( ni->vol ); - if ( xo ) - { - /* make sure the GUID was not used somewhere */ - memcpy( &key.object_id, value, sizeof( GUID ) ); - if ( ntfs_index_lookup( &key, - sizeof( OBJECT_ID_INDEX_KEY ), xo ) ) - { - ntfs_index_ctx_reinit( xo ); - res = add_object_id( ni, flags ); - if ( !res ) - { - /* update value and index */ - res = update_object_id( ni, xo, - ( const OBJECT_ID_ATTR* )value, - size ); - } - } - else - { - /* GUID is present elsewhere */ - res = -1; - errno = EEXIST; - } - xoni = xo->ni; - ntfs_index_entry_mark_dirty( xo ); - NInoSetDirty( xoni ); - ntfs_index_ctx_put( xo ); - ntfs_inode_close( xoni ); - } - else - { - res = -1; - } - } - else - { - errno = EINVAL; - res = -1; - } - return ( res ? -1 : 0 ); + res = 0; + if (ni && value && (size >= sizeof(GUID))) { + xo = open_object_id_index(ni->vol); + if (xo) { + /* make sure the GUID was not used somewhere */ + memcpy(&key.object_id, value, sizeof(GUID)); + if (ntfs_index_lookup(&key, + sizeof(OBJECT_ID_INDEX_KEY), xo)) { + ntfs_index_ctx_reinit(xo); + res = add_object_id(ni, flags); + if (!res) { + /* update value and index */ + res = update_object_id(ni,xo, + (const OBJECT_ID_ATTR*)value, + size); + } + } else { + /* GUID is present elsewhere */ + res = -1; + errno = EEXIST; + } + xoni = xo->ni; + ntfs_index_entry_mark_dirty(xo); + NInoSetDirty(xoni); + ntfs_index_ctx_put(xo); + ntfs_inode_close(xoni); + } else { + res = -1; + } + } else { + errno = EINVAL; + res = -1; + } + return (res ? -1 : 0); } /* - * Remove the object id + * Remove the object id * - * Returns 0, or -1 if there is a problem + * Returns 0, or -1 if there is a problem */ -int ntfs_remove_ntfs_object_id( ntfs_inode *ni ) +int ntfs_remove_ntfs_object_id(ntfs_inode *ni) { - int res; - int olderrno; - ntfs_attr *na; - ntfs_inode *xoni; - ntfs_index_context *xo; - int oldsize; - OBJECT_ID_ATTR old_attr; + int res; + int olderrno; + ntfs_attr *na; + ntfs_inode *xoni; + ntfs_index_context *xo; + int oldsize; + OBJECT_ID_ATTR old_attr; - res = 0; - if ( ni ) - { - /* - * open and delete the object id - */ - na = ntfs_attr_open( ni, AT_OBJECT_ID, - AT_UNNAMED, 0 ); - if ( na ) - { - /* first remove index (old object id needed) */ - xo = open_object_id_index( ni->vol ); - if ( xo ) - { - oldsize = remove_object_id_index( na, xo, - &old_attr ); - if ( oldsize < 0 ) - { - res = -1; - } - else - { - /* now remove attribute */ - res = ntfs_attr_rm( na ); - if ( res - && ( oldsize > ( int )sizeof( GUID ) ) ) - { - /* - * If we could not remove the - * attribute, try to restore the - * index and log the error. There - * will be an inconsistency if - * the reindexing fails. - */ - set_object_id_index( ni, xo, - &old_attr ); - ntfs_log_error( - "Failed to remove object id." - " Possible corruption.\n" ); - } - } + res = 0; + if (ni) { + /* + * open and delete the object id + */ + na = ntfs_attr_open(ni, AT_OBJECT_ID, + AT_UNNAMED,0); + if (na) { + /* first remove index (old object id needed) */ + xo = open_object_id_index(ni->vol); + if (xo) { + oldsize = remove_object_id_index(na,xo, + &old_attr); + if (oldsize < 0) { + res = -1; + } else { + /* now remove attribute */ + res = ntfs_attr_rm(na); + if (res + && (oldsize > (int)sizeof(GUID))) { + /* + * If we could not remove the + * attribute, try to restore the + * index and log the error. There + * will be an inconsistency if + * the reindexing fails. + */ + set_object_id_index(ni, xo, + &old_attr); + ntfs_log_error( + "Failed to remove object id." + " Possible corruption.\n"); + } + } - xoni = xo->ni; - ntfs_index_entry_mark_dirty( xo ); - NInoSetDirty( xoni ); - ntfs_index_ctx_put( xo ); - ntfs_inode_close( xoni ); - } - olderrno = errno; - ntfs_attr_close( na ); - /* avoid errno pollution */ - if ( errno == ENOENT ) - errno = olderrno; - } - else - { - errno = ENODATA; - res = -1; - } - NInoSetDirty( ni ); - } - else - { - errno = EINVAL; - res = -1; - } - return ( res ? -1 : 0 ); + xoni = xo->ni; + ntfs_index_entry_mark_dirty(xo); + NInoSetDirty(xoni); + ntfs_index_ctx_put(xo); + ntfs_inode_close(xoni); + } + olderrno = errno; + ntfs_attr_close(na); + /* avoid errno pollution */ + if (errno == ENOENT) + errno = olderrno; + } else { + errno = ENODATA; + res = -1; + } + NInoSetDirty(ni); + } else { + errno = EINVAL; + res = -1; + } + return (res ? -1 : 0); } #endif /* HAVE_SETXATTR */ diff --git a/source/libntfs/object_id.h b/source/libntfs/object_id.h index ab35e51d..31af9fd8 100644 --- a/source/libntfs/object_id.h +++ b/source/libntfs/object_id.h @@ -24,12 +24,12 @@ #ifndef OBJECT_ID_H #define OBJECT_ID_H -int ntfs_get_ntfs_object_id( ntfs_inode *ni, char *value, size_t size ); +int ntfs_get_ntfs_object_id(ntfs_inode *ni, char *value, size_t size); -int ntfs_set_ntfs_object_id( ntfs_inode *ni, const char *value, - size_t size, int flags ); -int ntfs_remove_ntfs_object_id( ntfs_inode *ni ); +int ntfs_set_ntfs_object_id(ntfs_inode *ni, const char *value, + size_t size, int flags); +int ntfs_remove_ntfs_object_id(ntfs_inode *ni); -int ntfs_delete_object_id_index( ntfs_inode *ni ); +int ntfs_delete_object_id_index(ntfs_inode *ni); #endif /* OBJECT_ID_H */ diff --git a/source/libntfs/param.h b/source/libntfs/param.h index 59a24ee3..f21bd653 100644 --- a/source/libntfs/param.h +++ b/source/libntfs/param.h @@ -22,59 +22,58 @@ #ifndef _NTFS_PARAM_H #define _NTFS_PARAM_H -#define CACHE_INODE_SIZE 32 /* inode cache, zero or >= 3 and not too big */ -#define CACHE_NIDATA_SIZE 64 /* idata cache, zero or >= 3 and not too big */ -#define CACHE_LOOKUP_SIZE 64 /* lookup cache, zero or >= 3 and not too big */ +#define CACHE_INODE_SIZE 32 /* inode cache, zero or >= 3 and not too big */ +#define CACHE_NIDATA_SIZE 64 /* idata cache, zero or >= 3 and not too big */ +#define CACHE_LOOKUP_SIZE 64 /* lookup cache, zero or >= 3 and not too big */ #define CACHE_SECURID_SIZE 16 /* securid cache, zero or >= 3 and not too big */ #define CACHE_LEGACY_SIZE 8 /* legacy cache size, zero or >= 3 and not too big */ -#define FORCE_FORMAT_v1x 0 /* Insert security data as in NTFS v1.x */ -#define OWNERFROMACL 1 /* Get the owner from ACL (not Windows owner) */ +#define FORCE_FORMAT_v1x 0 /* Insert security data as in NTFS v1.x */ +#define OWNERFROMACL 1 /* Get the owner from ACL (not Windows owner) */ -/* default security sub-authorities */ -enum -{ - DEFSECAUTH1 = -1153374643, /* 3141592653 */ - DEFSECAUTH2 = 589793238, - DEFSECAUTH3 = 462843383, - DEFSECBASE = 10000 + /* default security sub-authorities */ +enum { + DEFSECAUTH1 = -1153374643, /* 3141592653 */ + DEFSECAUTH2 = 589793238, + DEFSECAUTH3 = 462843383, + DEFSECBASE = 10000 }; /* - * Parameters for compression + * Parameters for compression */ -/* default option for compression */ + /* default option for compression */ #define DEFAULT_COMPRESSION FALSE -/* (log2 of) number of clusters in a compression block for new files */ + /* (log2 of) number of clusters in a compression block for new files */ #define STANDARD_COMPRESSION_UNIT 4 -/* maximum cluster size for allowing compression for new files */ + /* maximum cluster size for allowing compression for new files */ #define MAX_COMPRESSION_CLUSTER_SIZE 4096 /* - * Permission checking modes for high level and low level + * Permission checking modes for high level and low level * - * The choices for high and low lowel are independent, they have - * no effect on the library + * The choices for high and low lowel are independent, they have + * no effect on the library * - * Stick to the recommended values unless you understand the consequences - * on protection and performances. Use of cacheing is good for - * performances, but bad on security. + * Stick to the recommended values unless you understand the consequences + * on protection and performances. Use of cacheing is good for + * performances, but bad on security. * - * Possible values for high level : - * 1 : no cache, kernel control (recommended) - * 4 : no cache, file system control - * 7 : no cache, kernel control for ACLs + * Possible values for high level : + * 1 : no cache, kernel control (recommended) + * 4 : no cache, file system control + * 7 : no cache, kernel control for ACLs * - * Possible values for low level : - * 2 : no cache, kernel control - * 3 : use kernel/fuse cache, kernel control - * 5 : no cache, file system control (recommended) - * 8 : no cache, kernel control for ACLs + * Possible values for low level : + * 2 : no cache, kernel control + * 3 : use kernel/fuse cache, kernel control + * 5 : no cache, file system control (recommended) + * 8 : no cache, kernel control for ACLs * - * Use of options 7 and 8 requires a patch to fuse - * When Posix ACLs are selected in the configure options, a value - * of 6 is added in the mount report. + * Use of options 7 and 8 requires a patch to fuse + * When Posix ACLs are selected in the configure options, a value + * of 6 is added in the mount report. */ #define HPERMSCONFIG 1 diff --git a/source/libntfs/reparse.c b/source/libntfs/reparse.c index e40dd022..0f6360e1 100644 --- a/source/libntfs/reparse.c +++ b/source/libntfs/reparse.c @@ -1,7 +1,7 @@ /** * reparse.c - Processing of reparse points * - * This module is part of ntfs-3g library + * This module is part of ntfs-3g library * * Copyright (c) 2008-2009 Jean-Pierre Andre * @@ -71,1258 +71,1152 @@ #define IO_REPARSE_TAG_SIS const_cpu_to_le32(0x80000007) #define IO_REPARSE_TAG_SYMLINK const_cpu_to_le32(0xA000000C) -struct MOUNT_POINT_REPARSE_DATA /* reparse data for junctions */ -{ - le16 subst_name_offset; - le16 subst_name_length; - le16 print_name_offset; - le16 print_name_length; - char path_buffer[0]; /* above data assume this is char array */ +struct MOUNT_POINT_REPARSE_DATA { /* reparse data for junctions */ + le16 subst_name_offset; + le16 subst_name_length; + le16 print_name_offset; + le16 print_name_length; + char path_buffer[0]; /* above data assume this is char array */ } ; -struct SYMLINK_REPARSE_DATA /* reparse data for symlinks */ -{ - le16 subst_name_offset; - le16 subst_name_length; - le16 print_name_offset; - le16 print_name_length; - le32 flags; /* 1 for full target, otherwise 0 */ - char path_buffer[0]; /* above data assume this is char array */ +struct SYMLINK_REPARSE_DATA { /* reparse data for symlinks */ + le16 subst_name_offset; + le16 subst_name_length; + le16 print_name_offset; + le16 print_name_length; + le32 flags; /* 1 for full target, otherwise 0 */ + char path_buffer[0]; /* above data assume this is char array */ } ; -struct REPARSE_INDEX /* index entry in $Extend/$Reparse */ -{ - INDEX_ENTRY_HEADER header; - REPARSE_INDEX_KEY key; - le32 filling; +struct REPARSE_INDEX { /* index entry in $Extend/$Reparse */ + INDEX_ENTRY_HEADER header; + REPARSE_INDEX_KEY key; + le32 filling; } ; -static const ntfschar dir_junction_head[] = -{ - const_cpu_to_le16( '\\' ), - const_cpu_to_le16( '?' ), - const_cpu_to_le16( '?' ), - const_cpu_to_le16( '\\' ) +static const ntfschar dir_junction_head[] = { + const_cpu_to_le16('\\'), + const_cpu_to_le16('?'), + const_cpu_to_le16('?'), + const_cpu_to_le16('\\') } ; -static const ntfschar vol_junction_head[] = -{ - const_cpu_to_le16( '\\' ), - const_cpu_to_le16( '?' ), - const_cpu_to_le16( '?' ), - const_cpu_to_le16( '\\' ), - const_cpu_to_le16( 'V' ), - const_cpu_to_le16( 'o' ), - const_cpu_to_le16( 'l' ), - const_cpu_to_le16( 'u' ), - const_cpu_to_le16( 'm' ), - const_cpu_to_le16( 'e' ), - const_cpu_to_le16( '{' ), +static const ntfschar vol_junction_head[] = { + const_cpu_to_le16('\\'), + const_cpu_to_le16('?'), + const_cpu_to_le16('?'), + const_cpu_to_le16('\\'), + const_cpu_to_le16('V'), + const_cpu_to_le16('o'), + const_cpu_to_le16('l'), + const_cpu_to_le16('u'), + const_cpu_to_le16('m'), + const_cpu_to_le16('e'), + const_cpu_to_le16('{'), } ; -static ntfschar reparse_index_name[] = { const_cpu_to_le16( '$' ), - const_cpu_to_le16( 'R' ) - }; +static ntfschar reparse_index_name[] = { const_cpu_to_le16('$'), + const_cpu_to_le16('R') }; static const char mappingdir[] = ".NTFS-3G/"; /* - * Fix a file name with doubtful case in some directory index - * and return the name with the casing used in directory. + * Fix a file name with doubtful case in some directory index + * and return the name with the casing used in directory. * - * Should only be used to translate paths stored with case insensitivity - * (such as directory junctions) when no case conflict is expected. - * If there some ambiguity, the name which collates first is returned. + * Should only be used to translate paths stored with case insensitivity + * (such as directory junctions) when no case conflict is expected. + * If there some ambiguity, the name which collates first is returned. * - * The name is converted to upper case and searched the usual way. - * The collation rules for file names are such that we should get the - * first candidate if any. + * The name is converted to upper case and searched the usual way. + * The collation rules for file names are such that we should get the + * first candidate if any. */ -static u64 ntfs_fix_file_name( ntfs_inode *dir_ni, ntfschar *uname, - int uname_len ) +static u64 ntfs_fix_file_name(ntfs_inode *dir_ni, ntfschar *uname, + int uname_len) { - ntfs_volume *vol = dir_ni->vol; - ntfs_index_context *icx; - u64 mref; - le64 lemref; - int lkup; - int olderrno; - int i; - u32 cpuchar; - INDEX_ENTRY *entry; - FILE_NAME_ATTR *found; - struct - { - FILE_NAME_ATTR attr; - ntfschar file_name[NTFS_MAX_NAME_LEN + 1]; - } find; + ntfs_volume *vol = dir_ni->vol; + ntfs_index_context *icx; + u64 mref; + le64 lemref; + int lkup; + int olderrno; + int i; + u32 cpuchar; + INDEX_ENTRY *entry; + FILE_NAME_ATTR *found; + struct { + FILE_NAME_ATTR attr; + ntfschar file_name[NTFS_MAX_NAME_LEN + 1]; + } find; - mref = ( u64 ) - 1; /* default return (not found) */ - icx = ntfs_index_ctx_get( dir_ni, NTFS_INDEX_I30, 4 ); - if ( icx ) - { - if ( uname_len > NTFS_MAX_NAME_LEN ) - uname_len = NTFS_MAX_NAME_LEN; - find.attr.file_name_length = uname_len; - for ( i = 0; i < uname_len; i++ ) - { - cpuchar = le16_to_cpu( uname[i] ); - /* - * We need upper or lower value, whichever is smaller, - * but we can only convert to upper case, so we - * will fail when searching for an upper case char - * whose lower case is smaller (such as umlauted Y) - */ - if ( ( cpuchar < vol->upcase_len ) - && ( le16_to_cpu( vol->upcase[cpuchar] ) < cpuchar ) ) - find.attr.file_name[i] = vol->upcase[cpuchar]; - else - find.attr.file_name[i] = uname[i]; - } - olderrno = errno; - lkup = ntfs_index_lookup( ( char* ) & find, uname_len, icx ); - if ( errno == ENOENT ) - errno = olderrno; - /* - * We generally only get the first matching candidate, - * so we still have to check whether this is a real match - */ - if ( icx->entry && ( icx->entry->ie_flags & INDEX_ENTRY_END ) ) - /* get next entry if reaching end of block */ - entry = ntfs_index_next( icx->entry, icx ); - else - entry = icx->entry; - if ( entry ) - { - found = &entry->key.file_name; - if ( lkup - && ntfs_names_are_equal( find.attr.file_name, - find.attr.file_name_length, - found->file_name, found->file_name_length, - IGNORE_CASE, - vol->upcase, vol->upcase_len ) ) - lkup = 0; - if ( !lkup ) - { - /* - * name found : - * fix original name and return inode - */ - lemref = entry->indexed_file; - mref = le64_to_cpu( lemref ); - for ( i = 0; i < found->file_name_length; i++ ) - uname[i] = found->file_name[i]; - } - } - ntfs_index_ctx_put( icx ); - } - return ( mref ); + mref = (u64)-1; /* default return (not found) */ + icx = ntfs_index_ctx_get(dir_ni, NTFS_INDEX_I30, 4); + if (icx) { + if (uname_len > NTFS_MAX_NAME_LEN) + uname_len = NTFS_MAX_NAME_LEN; + find.attr.file_name_length = uname_len; + for (i=0; iupcase_len) + && (le16_to_cpu(vol->upcase[cpuchar]) < cpuchar)) + find.attr.file_name[i] = vol->upcase[cpuchar]; + else + find.attr.file_name[i] = uname[i]; + } + olderrno = errno; + lkup = ntfs_index_lookup((char*)&find, uname_len, icx); + if (errno == ENOENT) + errno = olderrno; + /* + * We generally only get the first matching candidate, + * so we still have to check whether this is a real match + */ + if (icx->entry && (icx->entry->ie_flags & INDEX_ENTRY_END)) + /* get next entry if reaching end of block */ + entry = ntfs_index_next(icx->entry, icx); + else + entry = icx->entry; + if (entry) { + found = &entry->key.file_name; + if (lkup + && ntfs_names_are_equal(find.attr.file_name, + find.attr.file_name_length, + found->file_name, found->file_name_length, + IGNORE_CASE, + vol->upcase, vol->upcase_len)) + lkup = 0; + if (!lkup) { + /* + * name found : + * fix original name and return inode + */ + lemref = entry->indexed_file; + mref = le64_to_cpu(lemref); + for (i=0; ifile_name_length; i++) + uname[i] = found->file_name[i]; + } + } + ntfs_index_ctx_put(icx); + } + return (mref); } /* - * Search for a directory junction or a symbolic link - * along the target path, with target defined as a full absolute path + * Search for a directory junction or a symbolic link + * along the target path, with target defined as a full absolute path * - * Returns the path translated to a Linux path - * or NULL if the path is not valid + * Returns the path translated to a Linux path + * or NULL if the path is not valid */ -static char *search_absolute( ntfs_volume *vol, ntfschar *path, - int count, BOOL isdir ) +static char *search_absolute(ntfs_volume *vol, ntfschar *path, + int count, BOOL isdir) { - ntfs_inode *ni; - u64 inum; - char *target; - int start; - int len; + ntfs_inode *ni; + u64 inum; + char *target; + int start; + int len; - target = ( char* )NULL; /* default return */ - ni = ntfs_inode_open( vol, ( MFT_REF )FILE_root ); - if ( ni ) - { - start = 0; - do - { - len = 0; - while ( ( ( start + len ) < count ) - && ( path[start + len] != const_cpu_to_le16( '\\' ) ) ) - len++; - inum = ntfs_fix_file_name( ni, &path[start], len ); - ntfs_inode_close( ni ); - ni = ( ntfs_inode* )NULL; - if ( inum != ( u64 ) - 1 ) - { - inum = MREF( inum ); - ni = ntfs_inode_open( vol, inum ); - start += len; - if ( start < count ) - path[start++] = const_cpu_to_le16( '/' ); - } - } - while ( ni - && ( ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ) - && ( start < count ) ); - if ( ni - && ( ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ? isdir : !isdir ) ) - if ( ntfs_ucstombs( path, count, &target, 0 ) < 0 ) - { - if ( target ) - { - free( target ); - target = ( char* )NULL; - } - } - if ( ni ) - ntfs_inode_close( ni ); - } - return ( target ); + target = (char*)NULL; /* default return */ + ni = ntfs_inode_open(vol, (MFT_REF)FILE_root); + if (ni) { + start = 0; + do { + len = 0; + while (((start + len) < count) + && (path[start + len] != const_cpu_to_le16('\\'))) + len++; + inum = ntfs_fix_file_name(ni, &path[start], len); + ntfs_inode_close(ni); + ni = (ntfs_inode*)NULL; + if (inum != (u64)-1) { + inum = MREF(inum); + ni = ntfs_inode_open(vol, inum); + start += len; + if (start < count) + path[start++] = const_cpu_to_le16('/'); + } + } while (ni + && (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + && (start < count)); + if (ni + && (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ? isdir : !isdir)) + if (ntfs_ucstombs(path, count, &target, 0) < 0) { + if (target) { + free(target); + target = (char*)NULL; + } + } + if (ni) + ntfs_inode_close(ni); + } + return (target); } /* - * Search for a symbolic link along the target path, - * with the target defined as a relative path + * Search for a symbolic link along the target path, + * with the target defined as a relative path * - * Note : the path used to access the current inode, may be - * different from the one implied in the target definition, - * when an inode has names in several directories. + * Note : the path used to access the current inode, may be + * different from the one implied in the target definition, + * when an inode has names in several directories. * - * Returns the path translated to a Linux path - * or NULL if the path is not valid + * Returns the path translated to a Linux path + * or NULL if the path is not valid */ -static char *search_relative( ntfs_inode *ni, ntfschar *path, int count ) +static char *search_relative(ntfs_inode *ni, ntfschar *path, int count) { - char *target = ( char* )NULL; - ntfs_inode *curni; - ntfs_inode *newni; - u64 inum; - int pos; - int lth; - BOOL ok; - int max = 32; /* safety */ + char *target = (char*)NULL; + ntfs_inode *curni; + ntfs_inode *newni; + u64 inum; + int pos; + int lth; + BOOL ok; + int max = 32; /* safety */ - pos = 0; - ok = TRUE; - curni = ntfs_dir_parent_inode( ni ); - while ( curni && ok && ( pos < ( count - 1 ) ) && --max ) - { - if ( ( count >= ( pos + 2 ) ) - && ( path[pos] == const_cpu_to_le16( '.' ) ) - && ( path[pos+1] == const_cpu_to_le16( '\\' ) ) ) - { - path[1] = const_cpu_to_le16( '/' ); - pos += 2; - } - else - { - if ( ( count >= ( pos + 3 ) ) - && ( path[pos] == const_cpu_to_le16( '.' ) ) - && ( path[pos+1] == const_cpu_to_le16( '.' ) ) - && ( path[pos+2] == const_cpu_to_le16( '\\' ) ) ) - { - path[2] = const_cpu_to_le16( '/' ); - pos += 3; - newni = ntfs_dir_parent_inode( curni ); - if ( curni != ni ) - ntfs_inode_close( curni ); - curni = newni; - if ( !curni ) - ok = FALSE; - } - else - { - lth = 0; - while ( ( ( pos + lth ) < count ) - && ( path[pos + lth] != const_cpu_to_le16( '\\' ) ) ) - lth++; - if ( lth > 0 ) - inum = ntfs_fix_file_name( curni, &path[pos], lth ); - else - inum = ( u64 ) - 1; - if ( !lth - || ( ( curni != ni ) - && ntfs_inode_close( curni ) ) - || ( inum == ( u64 ) - 1 ) ) - ok = FALSE; - else - { - curni = ntfs_inode_open( ni->vol, MREF( inum ) ); - if ( !curni ) - ok = FALSE; - else - { - if ( ok && ( ( pos + lth ) < count ) ) - { - path[pos + lth] = const_cpu_to_le16( '/' ); - pos += lth + 1; - } - else - { - pos += lth; - if ( ( ni->mrec->flags ^ curni->mrec->flags ) - & MFT_RECORD_IS_DIRECTORY ) - ok = FALSE; - if ( ntfs_inode_close( curni ) ) - ok = FALSE; - } - } - } - } - } - } + pos = 0; + ok = TRUE; + curni = ntfs_dir_parent_inode(ni); + while (curni && ok && (pos < (count - 1)) && --max) { + if ((count >= (pos + 2)) + && (path[pos] == const_cpu_to_le16('.')) + && (path[pos+1] == const_cpu_to_le16('\\'))) { + path[1] = const_cpu_to_le16('/'); + pos += 2; + } else { + if ((count >= (pos + 3)) + && (path[pos] == const_cpu_to_le16('.')) + &&(path[pos+1] == const_cpu_to_le16('.')) + && (path[pos+2] == const_cpu_to_le16('\\'))) { + path[2] = const_cpu_to_le16('/'); + pos += 3; + newni = ntfs_dir_parent_inode(curni); + if (curni != ni) + ntfs_inode_close(curni); + curni = newni; + if (!curni) + ok = FALSE; + } else { + lth = 0; + while (((pos + lth) < count) + && (path[pos + lth] != const_cpu_to_le16('\\'))) + lth++; + if (lth > 0) + inum = ntfs_fix_file_name(curni,&path[pos],lth); + else + inum = (u64)-1; + if (!lth + || ((curni != ni) + && ntfs_inode_close(curni)) + || (inum == (u64)-1)) + ok = FALSE; + else { + curni = ntfs_inode_open(ni->vol, MREF(inum)); + if (!curni) + ok = FALSE; + else { + if (ok && ((pos + lth) < count)) { + path[pos + lth] = const_cpu_to_le16('/'); + pos += lth + 1; + } else { + pos += lth; + if ((ni->mrec->flags ^ curni->mrec->flags) + & MFT_RECORD_IS_DIRECTORY) + ok = FALSE; + if (ntfs_inode_close(curni)) + ok = FALSE; + } + } + } + } + } + } - if ( ok && ( ntfs_ucstombs( path, count, &target, 0 ) < 0 ) ) - { - free( target ); // needed ? - target = ( char* )NULL; - } - return ( target ); + if (ok && (ntfs_ucstombs(path, count, &target, 0) < 0)) { + free(target); // needed ? + target = (char*)NULL; + } + return (target); } /* - * Check whether a drive letter has been defined in .NTFS-3G + * Check whether a drive letter has been defined in .NTFS-3G * - * Returns 1 if found, - * 0 if not found, - * -1 if there was an error (described by errno) + * Returns 1 if found, + * 0 if not found, + * -1 if there was an error (described by errno) */ -static int ntfs_drive_letter( ntfs_volume *vol, ntfschar letter ) +static int ntfs_drive_letter(ntfs_volume *vol, ntfschar letter) { - char defines[NTFS_MAX_NAME_LEN + 5]; - char *drive; - int ret; - int sz; - int olderrno; - ntfs_inode *ni; + char defines[NTFS_MAX_NAME_LEN + 5]; + char *drive; + int ret; + int sz; + int olderrno; + ntfs_inode *ni; - ret = -1; - drive = ( char* )NULL; - sz = ntfs_ucstombs( &letter, 1, &drive, 0 ); - if ( sz > 0 ) - { - strcpy( defines, mappingdir ); - if ( ( *drive >= 'a' ) && ( *drive <= 'z' ) ) - *drive += 'A' - 'a'; - strcat( defines, drive ); - strcat( defines, ":" ); - olderrno = errno; - ni = ntfs_pathname_to_inode( vol, NULL, defines ); - if ( ni && !ntfs_inode_close( ni ) ) - ret = 1; - else if ( errno == ENOENT ) - { - ret = 0; - /* avoid errno pollution */ - errno = olderrno; - } - } - if ( drive ) - free( drive ); - return ( ret ); + ret = -1; + drive = (char*)NULL; + sz = ntfs_ucstombs(&letter, 1, &drive, 0); + if (sz > 0) { + strcpy(defines,mappingdir); + if ((*drive >= 'a') && (*drive <= 'z')) + *drive += 'A' - 'a'; + strcat(defines,drive); + strcat(defines,":"); + olderrno = errno; + ni = ntfs_pathname_to_inode(vol, NULL, defines); + if (ni && !ntfs_inode_close(ni)) + ret = 1; + else + if (errno == ENOENT) { + ret = 0; + /* avoid errno pollution */ + errno = olderrno; + } + } + if (drive) + free(drive); + return (ret); } /* - * Do some sanity checks on reparse data + * Do some sanity checks on reparse data * - * The only general check is about the size (at least the tag must - * be present) - * If the reparse data looks like a junction point or symbolic - * link, more checks can be done. + * The only general check is about the size (at least the tag must + * be present) + * If the reparse data looks like a junction point or symbolic + * link, more checks can be done. * */ -static BOOL valid_reparse_data( ntfs_inode *ni, - const REPARSE_POINT *reparse_attr, size_t size ) +static BOOL valid_reparse_data(ntfs_inode *ni, + const REPARSE_POINT *reparse_attr, size_t size) { - BOOL ok; - unsigned int offs; - unsigned int lth; - const struct MOUNT_POINT_REPARSE_DATA *mount_point_data; - const struct SYMLINK_REPARSE_DATA *symlink_data; + BOOL ok; + unsigned int offs; + unsigned int lth; + const struct MOUNT_POINT_REPARSE_DATA *mount_point_data; + const struct SYMLINK_REPARSE_DATA *symlink_data; - ok = ni && reparse_attr - && ( size >= sizeof( REPARSE_POINT ) ) - && ( ( ( size_t )le16_to_cpu( reparse_attr->reparse_data_length ) - + sizeof( REPARSE_POINT ) ) == size ); - if ( ok ) - { - switch ( reparse_attr->reparse_tag ) - { - case IO_REPARSE_TAG_MOUNT_POINT : - mount_point_data = ( const struct MOUNT_POINT_REPARSE_DATA* ) - reparse_attr->reparse_data; - offs = le16_to_cpu( mount_point_data->subst_name_offset ); - lth = le16_to_cpu( mount_point_data->subst_name_length ); - /* consistency checks */ - if ( !( ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ) - || ( ( size_t )( ( sizeof( REPARSE_POINT ) - + sizeof( struct MOUNT_POINT_REPARSE_DATA ) - + offs + lth ) ) > size ) ) - ok = FALSE; - break; - case IO_REPARSE_TAG_SYMLINK : - symlink_data = ( const struct SYMLINK_REPARSE_DATA* ) - reparse_attr->reparse_data; - offs = le16_to_cpu( symlink_data->subst_name_offset ); - lth = le16_to_cpu( symlink_data->subst_name_length ); - if ( ( size_t )( ( sizeof( REPARSE_POINT ) - + sizeof( struct SYMLINK_REPARSE_DATA ) - + offs + lth ) ) > size ) - ok = FALSE; - break; - default : - break; - } - } - if ( !ok ) - errno = EINVAL; - return ( ok ); + ok = ni && reparse_attr + && (size >= sizeof(REPARSE_POINT)) + && (((size_t)le16_to_cpu(reparse_attr->reparse_data_length) + + sizeof(REPARSE_POINT)) == size); + if (ok) { + switch (reparse_attr->reparse_tag) { + case IO_REPARSE_TAG_MOUNT_POINT : + mount_point_data = (const struct MOUNT_POINT_REPARSE_DATA*) + reparse_attr->reparse_data; + offs = le16_to_cpu(mount_point_data->subst_name_offset); + lth = le16_to_cpu(mount_point_data->subst_name_length); + /* consistency checks */ + if (!(ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + || ((size_t)((sizeof(REPARSE_POINT) + + sizeof(struct MOUNT_POINT_REPARSE_DATA) + + offs + lth)) > size)) + ok = FALSE; + break; + case IO_REPARSE_TAG_SYMLINK : + symlink_data = (const struct SYMLINK_REPARSE_DATA*) + reparse_attr->reparse_data; + offs = le16_to_cpu(symlink_data->subst_name_offset); + lth = le16_to_cpu(symlink_data->subst_name_length); + if ((size_t)((sizeof(REPARSE_POINT) + + sizeof(struct SYMLINK_REPARSE_DATA) + + offs + lth)) > size) + ok = FALSE; + break; + default : + break; + } + } + if (!ok) + errno = EINVAL; + return (ok); } /* - * Check and translate the target of a junction point or - * a full absolute symbolic link. + * Check and translate the target of a junction point or + * a full absolute symbolic link. * - * A full target definition begins with "\??\" or "\\?\" + * A full target definition begins with "\??\" or "\\?\" * - * The fully defined target is redefined as a relative link, - * - either to the target if found on the same device. - * - or into the /.NTFS-3G directory for the user to define - * In the first situation, the target is translated to case-sensitive path. + * The fully defined target is redefined as a relative link, + * - either to the target if found on the same device. + * - or into the /.NTFS-3G directory for the user to define + * In the first situation, the target is translated to case-sensitive path. * - * returns the target converted to a relative symlink - * or NULL if there were some problem, as described by errno + * returns the target converted to a relative symlink + * or NULL if there were some problem, as described by errno */ -static char *ntfs_get_fulllink( ntfs_volume *vol, ntfschar *junction, - int count, const char *mnt_point, BOOL isdir ) +static char *ntfs_get_fulllink(ntfs_volume *vol, ntfschar *junction, + int count, const char *mnt_point, BOOL isdir) { - char *target; - char *fulltarget; - int sz; - char *q; - enum { DIR_JUNCTION, VOL_JUNCTION, NO_JUNCTION } kind; + char *target; + char *fulltarget; + int sz; + char *q; + enum { DIR_JUNCTION, VOL_JUNCTION, NO_JUNCTION } kind; - target = ( char* )NULL; - fulltarget = ( char* )NULL; - /* - * For a valid directory junction we want \??\x:\ - * where \ is an individual char and x a non-null char - */ - if ( ( count >= 7 ) - && !memcmp( junction, dir_junction_head, 8 ) - && junction[4] - && ( junction[5] == const_cpu_to_le16( ':' ) ) - && ( junction[6] == const_cpu_to_le16( '\\' ) ) ) - kind = DIR_JUNCTION; - else - /* - * For a valid volume junction we want \\?\Volume{ - * and a final \ (where \ is an individual char) - */ - if ( ( count >= 12 ) - && !memcmp( junction, vol_junction_head, 22 ) - && ( junction[count-1] == const_cpu_to_le16( '\\' ) ) ) - kind = VOL_JUNCTION; - else - kind = NO_JUNCTION; - /* - * Directory junction with an explicit path and - * no specific definition for the drive letter : - * try to interpret as a target on the same volume - */ - if ( ( kind == DIR_JUNCTION ) - && ( count >= 7 ) - && junction[7] - && !ntfs_drive_letter( vol, junction[4] ) ) - { - target = search_absolute( vol, &junction[7], count - 7, isdir ); - if ( target ) - { - fulltarget = ( char* )ntfs_malloc( strlen( mnt_point ) - + strlen( target ) + 2 ); - if ( fulltarget ) - { - strcpy( fulltarget, mnt_point ); - strcat( fulltarget, "/" ); - strcat( fulltarget, target ); - } - free( target ); - } - } - /* - * Volume junctions or directory junctions with - * target not found on current volume : - * link to /.NTFS-3G/target which the user can - * define as a symbolic link to the real target - */ - if ( ( ( kind == DIR_JUNCTION ) && !fulltarget ) - || ( kind == VOL_JUNCTION ) ) - { - sz = ntfs_ucstombs( &junction[4], - ( kind == VOL_JUNCTION ? count - 5 : count - 4 ), - &target, 0 ); - if ( ( sz > 0 ) && target ) - { - /* reverse slashes */ - for ( q = target; *q; q++ ) - if ( *q == '\\' ) - *q = '/'; - /* force uppercase drive letter */ - if ( ( target[1] == ':' ) - && ( target[0] >= 'a' ) - && ( target[0] <= 'z' ) ) - target[0] += 'A' - 'a'; - fulltarget = ( char* )ntfs_malloc( strlen( mnt_point ) - + sizeof( mappingdir ) + strlen( target ) + 1 ); - if ( fulltarget ) - { - strcpy( fulltarget, mnt_point ); - strcat( fulltarget, "/" ); - strcat( fulltarget, mappingdir ); - strcat( fulltarget, target ); - } - } - if ( target ) - free( target ); - } - return ( fulltarget ); + target = (char*)NULL; + fulltarget = (char*)NULL; + /* + * For a valid directory junction we want \??\x:\ + * where \ is an individual char and x a non-null char + */ + if ((count >= 7) + && !memcmp(junction,dir_junction_head,8) + && junction[4] + && (junction[5] == const_cpu_to_le16(':')) + && (junction[6] == const_cpu_to_le16('\\'))) + kind = DIR_JUNCTION; + else + /* + * For a valid volume junction we want \\?\Volume{ + * and a final \ (where \ is an individual char) + */ + if ((count >= 12) + && !memcmp(junction,vol_junction_head,22) + && (junction[count-1] == const_cpu_to_le16('\\'))) + kind = VOL_JUNCTION; + else + kind = NO_JUNCTION; + /* + * Directory junction with an explicit path and + * no specific definition for the drive letter : + * try to interpret as a target on the same volume + */ + if ((kind == DIR_JUNCTION) + && (count >= 7) + && junction[7] + && !ntfs_drive_letter(vol, junction[4])) { + target = search_absolute(vol,&junction[7],count - 7, isdir); + if (target) { + fulltarget = (char*)ntfs_malloc(strlen(mnt_point) + + strlen(target) + 2); + if (fulltarget) { + strcpy(fulltarget,mnt_point); + strcat(fulltarget,"/"); + strcat(fulltarget,target); + } + free(target); + } + } + /* + * Volume junctions or directory junctions with + * target not found on current volume : + * link to /.NTFS-3G/target which the user can + * define as a symbolic link to the real target + */ + if (((kind == DIR_JUNCTION) && !fulltarget) + || (kind == VOL_JUNCTION)) { + sz = ntfs_ucstombs(&junction[4], + (kind == VOL_JUNCTION ? count - 5 : count - 4), + &target, 0); + if ((sz > 0) && target) { + /* reverse slashes */ + for (q=target; *q; q++) + if (*q == '\\') + *q = '/'; + /* force uppercase drive letter */ + if ((target[1] == ':') + && (target[0] >= 'a') + && (target[0] <= 'z')) + target[0] += 'A' - 'a'; + fulltarget = (char*)ntfs_malloc(strlen(mnt_point) + + sizeof(mappingdir) + strlen(target) + 1); + if (fulltarget) { + strcpy(fulltarget,mnt_point); + strcat(fulltarget,"/"); + strcat(fulltarget,mappingdir); + strcat(fulltarget,target); + } + } + if (target) + free(target); + } + return (fulltarget); } /* - * Check and translate the target of an absolute symbolic link. + * Check and translate the target of an absolute symbolic link. * - * An absolute target definition begins with "\" or "x:\" + * An absolute target definition begins with "\" or "x:\" * - * The absolute target is redefined as a relative link, - * - either to the target if found on the same device. - * - or into the /.NTFS-3G directory for the user to define - * In the first situation, the target is translated to case-sensitive path. + * The absolute target is redefined as a relative link, + * - either to the target if found on the same device. + * - or into the /.NTFS-3G directory for the user to define + * In the first situation, the target is translated to case-sensitive path. * - * returns the target converted to a relative symlink - * or NULL if there were some problem, as described by errno + * returns the target converted to a relative symlink + * or NULL if there were some problem, as described by errno */ -static char *ntfs_get_abslink( ntfs_volume *vol, ntfschar *junction, - int count, const char *mnt_point, BOOL isdir ) +static char *ntfs_get_abslink(ntfs_volume *vol, ntfschar *junction, + int count, const char *mnt_point, BOOL isdir) { - char *target; - char *fulltarget; - int sz; - char *q; - enum { FULL_PATH, ABS_PATH, REJECTED_PATH } kind; + char *target; + char *fulltarget; + int sz; + char *q; + enum { FULL_PATH, ABS_PATH, REJECTED_PATH } kind; - target = ( char* )NULL; - fulltarget = ( char* )NULL; - /* - * For a full valid path we want x:\ - * where \ is an individual char and x a non-null char - */ - if ( ( count >= 3 ) - && junction[0] - && ( junction[1] == const_cpu_to_le16( ':' ) ) - && ( junction[2] == const_cpu_to_le16( '\\' ) ) ) - kind = FULL_PATH; - else - /* - * For an absolute path we want an initial \ - */ - if ( ( count >= 0 ) - && ( junction[0] == const_cpu_to_le16( '\\' ) ) ) - kind = ABS_PATH; - else - kind = REJECTED_PATH; - /* - * Full path, with a drive letter and - * no specific definition for the drive letter : - * try to interpret as a target on the same volume. - * Do the same for an abs path with no drive letter. - */ - if ( ( ( kind == FULL_PATH ) - && ( count >= 3 ) - && junction[3] - && !ntfs_drive_letter( vol, junction[0] ) ) - || ( kind == ABS_PATH ) ) - { - if ( kind == ABS_PATH ) - target = search_absolute( vol, &junction[1], - count - 1, isdir ); - else - target = search_absolute( vol, &junction[3], - count - 3, isdir ); - if ( target ) - { - fulltarget = ( char* )ntfs_malloc( strlen( mnt_point ) - + strlen( target ) + 2 ); - if ( fulltarget ) - { - strcpy( fulltarget, mnt_point ); - strcat( fulltarget, "/" ); - strcat( fulltarget, target ); - } - free( target ); - } - } - /* - * full path with target not found on current volume : - * link to /.NTFS-3G/target which the user can - * define as a symbolic link to the real target - */ - if ( ( kind == FULL_PATH ) && !fulltarget ) - { - sz = ntfs_ucstombs( &junction[0], - count, &target, 0 ); - if ( ( sz > 0 ) && target ) - { - /* reverse slashes */ - for ( q = target; *q; q++ ) - if ( *q == '\\' ) - *q = '/'; - /* force uppercase drive letter */ - if ( ( target[1] == ':' ) - && ( target[0] >= 'a' ) - && ( target[0] <= 'z' ) ) - target[0] += 'A' - 'a'; - fulltarget = ( char* )ntfs_malloc( strlen( mnt_point ) - + sizeof( mappingdir ) + strlen( target ) + 1 ); - if ( fulltarget ) - { - strcpy( fulltarget, mnt_point ); - strcat( fulltarget, "/" ); - strcat( fulltarget, mappingdir ); - strcat( fulltarget, target ); - } - } - if ( target ) - free( target ); - } - return ( fulltarget ); + target = (char*)NULL; + fulltarget = (char*)NULL; + /* + * For a full valid path we want x:\ + * where \ is an individual char and x a non-null char + */ + if ((count >= 3) + && junction[0] + && (junction[1] == const_cpu_to_le16(':')) + && (junction[2] == const_cpu_to_le16('\\'))) + kind = FULL_PATH; + else + /* + * For an absolute path we want an initial \ + */ + if ((count >= 0) + && (junction[0] == const_cpu_to_le16('\\'))) + kind = ABS_PATH; + else + kind = REJECTED_PATH; + /* + * Full path, with a drive letter and + * no specific definition for the drive letter : + * try to interpret as a target on the same volume. + * Do the same for an abs path with no drive letter. + */ + if (((kind == FULL_PATH) + && (count >= 3) + && junction[3] + && !ntfs_drive_letter(vol, junction[0])) + || (kind == ABS_PATH)) { + if (kind == ABS_PATH) + target = search_absolute(vol, &junction[1], + count - 1, isdir); + else + target = search_absolute(vol, &junction[3], + count - 3, isdir); + if (target) { + fulltarget = (char*)ntfs_malloc(strlen(mnt_point) + + strlen(target) + 2); + if (fulltarget) { + strcpy(fulltarget,mnt_point); + strcat(fulltarget,"/"); + strcat(fulltarget,target); + } + free(target); + } + } + /* + * full path with target not found on current volume : + * link to /.NTFS-3G/target which the user can + * define as a symbolic link to the real target + */ + if ((kind == FULL_PATH) && !fulltarget) { + sz = ntfs_ucstombs(&junction[0], + count,&target, 0); + if ((sz > 0) && target) { + /* reverse slashes */ + for (q=target; *q; q++) + if (*q == '\\') + *q = '/'; + /* force uppercase drive letter */ + if ((target[1] == ':') + && (target[0] >= 'a') + && (target[0] <= 'z')) + target[0] += 'A' - 'a'; + fulltarget = (char*)ntfs_malloc(strlen(mnt_point) + + sizeof(mappingdir) + strlen(target) + 1); + if (fulltarget) { + strcpy(fulltarget,mnt_point); + strcat(fulltarget,"/"); + strcat(fulltarget,mappingdir); + strcat(fulltarget,target); + } + } + if (target) + free(target); + } + return (fulltarget); } /* - * Check and translate the target of a relative symbolic link. + * Check and translate the target of a relative symbolic link. * - * A relative target definition does not begin with "\" + * A relative target definition does not begin with "\" * - * The original definition of relative target is kept, it is just - * translated to a case-sensitive path. + * The original definition of relative target is kept, it is just + * translated to a case-sensitive path. * - * returns the target converted to a relative symlink - * or NULL if there were some problem, as described by errno + * returns the target converted to a relative symlink + * or NULL if there were some problem, as described by errno */ -static char *ntfs_get_rellink( ntfs_inode *ni, ntfschar *junction, int count ) +static char *ntfs_get_rellink(ntfs_inode *ni, ntfschar *junction, int count) { - char *target; + char *target; - target = search_relative( ni, junction, count ); - return ( target ); + target = search_relative(ni,junction,count); + return (target); } /* - * Get the target for a junction point or symbolic link - * Should only be called for files or directories with reparse data + * Get the target for a junction point or symbolic link + * Should only be called for files or directories with reparse data * - * returns the target converted to a relative path, or NULL - * if some error occurred, as described by errno - * errno is EOPNOTSUPP if the reparse point is not a valid - * symbolic link or directory junction + * returns the target converted to a relative path, or NULL + * if some error occurred, as described by errno + * errno is EOPNOTSUPP if the reparse point is not a valid + * symbolic link or directory junction */ -char *ntfs_make_symlink( ntfs_inode *ni, const char *mnt_point, - int *pattr_size ) +char *ntfs_make_symlink(ntfs_inode *ni, const char *mnt_point, + int *pattr_size) { - s64 attr_size = 0; - char *target; - unsigned int offs; - unsigned int lth; - ntfs_volume *vol; - REPARSE_POINT *reparse_attr; - struct MOUNT_POINT_REPARSE_DATA *mount_point_data; - struct SYMLINK_REPARSE_DATA *symlink_data; - enum { FULL_TARGET, ABS_TARGET, REL_TARGET } kind; - ntfschar *p; - BOOL bad; - BOOL isdir; + s64 attr_size = 0; + char *target; + unsigned int offs; + unsigned int lth; + ntfs_volume *vol; + REPARSE_POINT *reparse_attr; + struct MOUNT_POINT_REPARSE_DATA *mount_point_data; + struct SYMLINK_REPARSE_DATA *symlink_data; + enum { FULL_TARGET, ABS_TARGET, REL_TARGET } kind; + ntfschar *p; + BOOL bad; + BOOL isdir; - target = ( char* )NULL; - bad = TRUE; - isdir = ( ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ) - != const_cpu_to_le16( 0 ); - vol = ni->vol; - reparse_attr = ( REPARSE_POINT* )ntfs_attr_readall( ni, - AT_REPARSE_POINT, ( ntfschar* )NULL, 0, &attr_size ); - if ( reparse_attr && attr_size - && valid_reparse_data( ni, reparse_attr, attr_size ) ) - { - switch ( reparse_attr->reparse_tag ) - { - case IO_REPARSE_TAG_MOUNT_POINT : - mount_point_data = ( struct MOUNT_POINT_REPARSE_DATA* ) - reparse_attr->reparse_data; - offs = le16_to_cpu( mount_point_data->subst_name_offset ); - lth = le16_to_cpu( mount_point_data->subst_name_length ); - /* reparse data consistency has been checked */ - target = ntfs_get_fulllink( vol, - ( ntfschar* ) & mount_point_data->path_buffer[offs], - lth / 2, mnt_point, isdir ); - if ( target ) - bad = FALSE; - break; - case IO_REPARSE_TAG_SYMLINK : - symlink_data = ( struct SYMLINK_REPARSE_DATA* ) - reparse_attr->reparse_data; - offs = le16_to_cpu( symlink_data->subst_name_offset ); - lth = le16_to_cpu( symlink_data->subst_name_length ); - p = ( ntfschar* ) & symlink_data->path_buffer[offs]; - /* - * Predetermine the kind of target, - * the called function has to make a full check - */ - if ( *p++ == const_cpu_to_le16( '\\' ) ) - { - if ( ( *p == const_cpu_to_le16( '?' ) ) - || ( *p == const_cpu_to_le16( '\\' ) ) ) - kind = FULL_TARGET; - else - kind = ABS_TARGET; - } - else if ( *p == const_cpu_to_le16( ':' ) ) - kind = ABS_TARGET; - else - kind = REL_TARGET; - p--; - /* reparse data consistency has been checked */ - switch ( kind ) - { - case FULL_TARGET : - if ( !( symlink_data->flags - & const_cpu_to_le32( 1 ) ) ) - { - target = ntfs_get_fulllink( vol, - p, lth / 2, - mnt_point, isdir ); - if ( target ) - bad = FALSE; - } - break; - case ABS_TARGET : - if ( symlink_data->flags - & const_cpu_to_le32( 1 ) ) - { - target = ntfs_get_abslink( vol, - p, lth / 2, - mnt_point, isdir ); - if ( target ) - bad = FALSE; - } - break; - case REL_TARGET : - if ( symlink_data->flags - & const_cpu_to_le32( 1 ) ) - { - target = ntfs_get_rellink( ni, - p, lth / 2 ); - if ( target ) - bad = FALSE; - } - break; - } - break; - } - free( reparse_attr ); - } - *pattr_size = attr_size; - if ( bad ) - errno = EOPNOTSUPP; - return ( target ); + target = (char*)NULL; + bad = TRUE; + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + != const_cpu_to_le16(0); + vol = ni->vol; + reparse_attr = (REPARSE_POINT*)ntfs_attr_readall(ni, + AT_REPARSE_POINT,(ntfschar*)NULL, 0, &attr_size); + if (reparse_attr && attr_size + && valid_reparse_data(ni, reparse_attr, attr_size)) { + switch (reparse_attr->reparse_tag) { + case IO_REPARSE_TAG_MOUNT_POINT : + mount_point_data = (struct MOUNT_POINT_REPARSE_DATA*) + reparse_attr->reparse_data; + offs = le16_to_cpu(mount_point_data->subst_name_offset); + lth = le16_to_cpu(mount_point_data->subst_name_length); + /* reparse data consistency has been checked */ + target = ntfs_get_fulllink(vol, + (ntfschar*)&mount_point_data->path_buffer[offs], + lth/2, mnt_point, isdir); + if (target) + bad = FALSE; + break; + case IO_REPARSE_TAG_SYMLINK : + symlink_data = (struct SYMLINK_REPARSE_DATA*) + reparse_attr->reparse_data; + offs = le16_to_cpu(symlink_data->subst_name_offset); + lth = le16_to_cpu(symlink_data->subst_name_length); + p = (ntfschar*)&symlink_data->path_buffer[offs]; + /* + * Predetermine the kind of target, + * the called function has to make a full check + */ + if (*p++ == const_cpu_to_le16('\\')) { + if ((*p == const_cpu_to_le16('?')) + || (*p == const_cpu_to_le16('\\'))) + kind = FULL_TARGET; + else + kind = ABS_TARGET; + } else + if (*p == const_cpu_to_le16(':')) + kind = ABS_TARGET; + else + kind = REL_TARGET; + p--; + /* reparse data consistency has been checked */ + switch (kind) { + case FULL_TARGET : + if (!(symlink_data->flags + & const_cpu_to_le32(1))) { + target = ntfs_get_fulllink(vol, + p, lth/2, + mnt_point, isdir); + if (target) + bad = FALSE; + } + break; + case ABS_TARGET : + if (symlink_data->flags + & const_cpu_to_le32(1)) { + target = ntfs_get_abslink(vol, + p, lth/2, + mnt_point, isdir); + if (target) + bad = FALSE; + } + break; + case REL_TARGET : + if (symlink_data->flags + & const_cpu_to_le32(1)) { + target = ntfs_get_rellink(ni, + p, lth/2); + if (target) + bad = FALSE; + } + break; + } + break; + } + free(reparse_attr); + } + *pattr_size = attr_size; + if (bad) + errno = EOPNOTSUPP; + return (target); } /* - * Check whether a reparse point looks like a junction point - * or a symbolic link. - * Should only be called for files or directories with reparse data + * Check whether a reparse point looks like a junction point + * or a symbolic link. + * Should only be called for files or directories with reparse data * - * The validity of the target is not checked. + * The validity of the target is not checked. */ -BOOL ntfs_possible_symlink( ntfs_inode *ni ) +BOOL ntfs_possible_symlink(ntfs_inode *ni) { - s64 attr_size = 0; - REPARSE_POINT *reparse_attr; - BOOL possible; + s64 attr_size = 0; + REPARSE_POINT *reparse_attr; + BOOL possible; - possible = FALSE; - reparse_attr = ( REPARSE_POINT* )ntfs_attr_readall( ni, - AT_REPARSE_POINT, ( ntfschar* )NULL, 0, &attr_size ); - if ( reparse_attr && attr_size ) - { - switch ( reparse_attr->reparse_tag ) - { - case IO_REPARSE_TAG_MOUNT_POINT : - case IO_REPARSE_TAG_SYMLINK : - possible = TRUE; - default : ; - } - free( reparse_attr ); - } - return ( possible ); + possible = FALSE; + reparse_attr = (REPARSE_POINT*)ntfs_attr_readall(ni, + AT_REPARSE_POINT,(ntfschar*)NULL, 0, &attr_size); + if (reparse_attr && attr_size) { + switch (reparse_attr->reparse_tag) { + case IO_REPARSE_TAG_MOUNT_POINT : + case IO_REPARSE_TAG_SYMLINK : + possible = TRUE; + default : ; + } + free(reparse_attr); + } + return (possible); } -#ifdef HAVE_SETXATTR /* extended attributes interface required */ +#ifdef HAVE_SETXATTR /* extended attributes interface required */ /* - * Set the index for new reparse data + * Set the index for new reparse data * - * Returns 0 if success - * -1 if failure, explained by errno + * Returns 0 if success + * -1 if failure, explained by errno */ -static int set_reparse_index( ntfs_inode *ni, ntfs_index_context *xr, - le32 reparse_tag ) +static int set_reparse_index(ntfs_inode *ni, ntfs_index_context *xr, + le32 reparse_tag) { - struct REPARSE_INDEX indx; - u64 file_id_cpu; - le64 file_id; - le16 seqn; + struct REPARSE_INDEX indx; + u64 file_id_cpu; + le64 file_id; + le16 seqn; - seqn = ni->mrec->sequence_number; - file_id_cpu = MK_MREF( ni->mft_no, le16_to_cpu( seqn ) ); - file_id = cpu_to_le64( file_id_cpu ); - indx.header.data_offset = const_cpu_to_le16( - sizeof( INDEX_ENTRY_HEADER ) - + sizeof( REPARSE_INDEX_KEY ) ); - indx.header.data_length = const_cpu_to_le16( 0 ); - indx.header.reservedV = const_cpu_to_le32( 0 ); - indx.header.length = const_cpu_to_le16( - sizeof( struct REPARSE_INDEX ) ); - indx.header.key_length = const_cpu_to_le16( - sizeof( REPARSE_INDEX_KEY ) ); - indx.header.flags = const_cpu_to_le16( 0 ); - indx.header.reserved = const_cpu_to_le16( 0 ); - indx.key.reparse_tag = reparse_tag; - /* danger on processors which require proper alignment ! */ - memcpy( &indx.key.file_id, &file_id, 8 ); - indx.filling = const_cpu_to_le32( 0 ); - ntfs_index_ctx_reinit( xr ); - return ( ntfs_ie_add( xr, ( INDEX_ENTRY* )&indx ) ); + seqn = ni->mrec->sequence_number; + file_id_cpu = MK_MREF(ni->mft_no,le16_to_cpu(seqn)); + file_id = cpu_to_le64(file_id_cpu); + indx.header.data_offset = const_cpu_to_le16( + sizeof(INDEX_ENTRY_HEADER) + + sizeof(REPARSE_INDEX_KEY)); + indx.header.data_length = const_cpu_to_le16(0); + indx.header.reservedV = const_cpu_to_le32(0); + indx.header.length = const_cpu_to_le16( + sizeof(struct REPARSE_INDEX)); + indx.header.key_length = const_cpu_to_le16( + sizeof(REPARSE_INDEX_KEY)); + indx.header.flags = const_cpu_to_le16(0); + indx.header.reserved = const_cpu_to_le16(0); + indx.key.reparse_tag = reparse_tag; + /* danger on processors which require proper alignment ! */ + memcpy(&indx.key.file_id, &file_id, 8); + indx.filling = const_cpu_to_le32(0); + ntfs_index_ctx_reinit(xr); + return (ntfs_ie_add(xr,(INDEX_ENTRY*)&indx)); } #endif /* HAVE_SETXATTR */ /* - * Remove a reparse data index entry if attribute present + * Remove a reparse data index entry if attribute present * - * Returns the size of existing reparse data - * (the existing reparse tag is returned) - * -1 if failure, explained by errno + * Returns the size of existing reparse data + * (the existing reparse tag is returned) + * -1 if failure, explained by errno */ -static int remove_reparse_index( ntfs_attr *na, ntfs_index_context *xr, - le32 *preparse_tag ) +static int remove_reparse_index(ntfs_attr *na, ntfs_index_context *xr, + le32 *preparse_tag) { - REPARSE_INDEX_KEY key; - u64 file_id_cpu; - le64 file_id; - s64 size; - le16 seqn; - int ret; + REPARSE_INDEX_KEY key; + u64 file_id_cpu; + le64 file_id; + s64 size; + le16 seqn; + int ret; - ret = na->data_size; - if ( ret ) - { - /* read the existing reparse_tag */ - size = ntfs_attr_pread( na, 0, 4, preparse_tag ); - if ( size == 4 ) - { - seqn = na->ni->mrec->sequence_number; - file_id_cpu = MK_MREF( na->ni->mft_no, le16_to_cpu( seqn ) ); - file_id = cpu_to_le64( file_id_cpu ); - key.reparse_tag = *preparse_tag; - /* danger on processors which require proper alignment ! */ - memcpy( &key.file_id, &file_id, 8 ); - if ( !ntfs_index_lookup( &key, sizeof( REPARSE_INDEX_KEY ), xr ) - && ntfs_index_rm( xr ) ) - ret = -1; - } - else - { - ret = -1; - errno = ENODATA; - } - } - return ( ret ); + ret = na->data_size; + if (ret) { + /* read the existing reparse_tag */ + size = ntfs_attr_pread(na, 0, 4, preparse_tag); + if (size == 4) { + seqn = na->ni->mrec->sequence_number; + file_id_cpu = MK_MREF(na->ni->mft_no,le16_to_cpu(seqn)); + file_id = cpu_to_le64(file_id_cpu); + key.reparse_tag = *preparse_tag; + /* danger on processors which require proper alignment ! */ + memcpy(&key.file_id, &file_id, 8); + if (!ntfs_index_lookup(&key, sizeof(REPARSE_INDEX_KEY), xr) + && ntfs_index_rm(xr)) + ret = -1; + } else { + ret = -1; + errno = ENODATA; + } + } + return (ret); } /* - * Open the $Extend/$Reparse file and its index + * Open the $Extend/$Reparse file and its index * - * Return the index context if opened - * or NULL if an error occurred (errno tells why) + * Return the index context if opened + * or NULL if an error occurred (errno tells why) * - * The index has to be freed and inode closed when not needed any more. + * The index has to be freed and inode closed when not needed any more. */ -static ntfs_index_context *open_reparse_index( ntfs_volume *vol ) +static ntfs_index_context *open_reparse_index(ntfs_volume *vol) { - u64 inum; - ntfs_inode *ni; - ntfs_inode *dir_ni; - ntfs_index_context *xr; + u64 inum; + ntfs_inode *ni; + ntfs_inode *dir_ni; + ntfs_index_context *xr; - /* do not use path_name_to inode - could reopen root */ - dir_ni = ntfs_inode_open( vol, FILE_Extend ); - ni = ( ntfs_inode* )NULL; - if ( dir_ni ) - { - inum = ntfs_inode_lookup_by_mbsname( dir_ni, "$Reparse" ); - if ( inum != ( u64 ) - 1 ) - ni = ntfs_inode_open( vol, inum ); - ntfs_inode_close( dir_ni ); - } - if ( ni ) - { - xr = ntfs_index_ctx_get( ni, reparse_index_name, 2 ); - if ( !xr ) - { - ntfs_inode_close( ni ); - } - } - else - xr = ( ntfs_index_context* )NULL; - return ( xr ); + /* do not use path_name_to inode - could reopen root */ + dir_ni = ntfs_inode_open(vol, FILE_Extend); + ni = (ntfs_inode*)NULL; + if (dir_ni) { + inum = ntfs_inode_lookup_by_mbsname(dir_ni,"$Reparse"); + if (inum != (u64)-1) + ni = ntfs_inode_open(vol, inum); + ntfs_inode_close(dir_ni); + } + if (ni) { + xr = ntfs_index_ctx_get(ni, reparse_index_name, 2); + if (!xr) { + ntfs_inode_close(ni); + } + } else + xr = (ntfs_index_context*)NULL; + return (xr); } -#ifdef HAVE_SETXATTR /* extended attributes interface required */ +#ifdef HAVE_SETXATTR /* extended attributes interface required */ /* - * Update the reparse data and index + * Update the reparse data and index * - * The reparse data attribute should have been created, and - * an existing index is expected if there is an existing value. + * The reparse data attribute should have been created, and + * an existing index is expected if there is an existing value. * - * Returns 0 if success - * -1 if failure, explained by errno - * If could not remove the existing index, nothing is done, - * If could not write the new data, no index entry is inserted - * If failed to insert the index, data is removed + * Returns 0 if success + * -1 if failure, explained by errno + * If could not remove the existing index, nothing is done, + * If could not write the new data, no index entry is inserted + * If failed to insert the index, data is removed */ -static int update_reparse_data( ntfs_inode *ni, ntfs_index_context *xr, - const char *value, size_t size ) +static int update_reparse_data(ntfs_inode *ni, ntfs_index_context *xr, + const char *value, size_t size) { - int res; - int written; - int oldsize; - ntfs_attr *na; - le32 reparse_tag; + int res; + int written; + int oldsize; + ntfs_attr *na; + le32 reparse_tag; - res = 0; - na = ntfs_attr_open( ni, AT_REPARSE_POINT, AT_UNNAMED, 0 ); - if ( na ) - { - /* remove the existing reparse data */ - oldsize = remove_reparse_index( na, xr, &reparse_tag ); - if ( oldsize < 0 ) - res = -1; - else - { - /* resize attribute */ - res = ntfs_attr_truncate( na, ( s64 )size ); - /* overwrite value if any */ - if ( !res && value ) - { - written = ( int )ntfs_attr_pwrite( na, - ( s64 )0, ( s64 )size, value ); - if ( written != ( s64 )size ) - { - ntfs_log_error( "Failed to update " - "reparse data\n" ); - errno = EIO; - res = -1; - } - } - if ( !res - && set_reparse_index( ni, xr, - ( ( const REPARSE_POINT* )value )->reparse_tag ) - && ( oldsize > 0 ) ) - { - /* - * If cannot index, try to remove the reparse - * data and log the error. There will be an - * inconsistency if removal fails. - */ - ntfs_attr_rm( na ); - ntfs_log_error( "Failed to index reparse data." - " Possible corruption.\n" ); - } - } - ntfs_attr_close( na ); - NInoSetDirty( ni ); - } - else - res = -1; - return ( res ); + res = 0; + na = ntfs_attr_open(ni, AT_REPARSE_POINT, AT_UNNAMED, 0); + if (na) { + /* remove the existing reparse data */ + oldsize = remove_reparse_index(na,xr,&reparse_tag); + if (oldsize < 0) + res = -1; + else { + /* resize attribute */ + res = ntfs_attr_truncate(na, (s64)size); + /* overwrite value if any */ + if (!res && value) { + written = (int)ntfs_attr_pwrite(na, + (s64)0, (s64)size, value); + if (written != (s64)size) { + ntfs_log_error("Failed to update " + "reparse data\n"); + errno = EIO; + res = -1; + } + } + if (!res + && set_reparse_index(ni,xr, + ((const REPARSE_POINT*)value)->reparse_tag) + && (oldsize > 0)) { + /* + * If cannot index, try to remove the reparse + * data and log the error. There will be an + * inconsistency if removal fails. + */ + ntfs_attr_rm(na); + ntfs_log_error("Failed to index reparse data." + " Possible corruption.\n"); + } + } + ntfs_attr_close(na); + NInoSetDirty(ni); + } else + res = -1; + return (res); } #endif /* HAVE_SETXATTR */ /* - * Delete a reparse index entry + * Delete a reparse index entry * - * Returns 0 if success - * -1 if failure, explained by errno + * Returns 0 if success + * -1 if failure, explained by errno */ -int ntfs_delete_reparse_index( ntfs_inode *ni ) +int ntfs_delete_reparse_index(ntfs_inode *ni) { - ntfs_index_context *xr; - ntfs_inode *xrni; - ntfs_attr *na; - le32 reparse_tag; - int res; + ntfs_index_context *xr; + ntfs_inode *xrni; + ntfs_attr *na; + le32 reparse_tag; + int res; - res = 0; - na = ntfs_attr_open( ni, AT_REPARSE_POINT, AT_UNNAMED, 0 ); - if ( na ) - { - /* - * read the existing reparse data (the tag is enough) - * and un-index it - */ - xr = open_reparse_index( ni->vol ); - if ( xr ) - { - if ( remove_reparse_index( na, xr, &reparse_tag ) < 0 ) - res = -1; - xrni = xr->ni; - ntfs_index_entry_mark_dirty( xr ); - NInoSetDirty( xrni ); - ntfs_index_ctx_put( xr ); - ntfs_inode_close( xrni ); - } - ntfs_attr_close( na ); - } - return ( res ); + res = 0; + na = ntfs_attr_open(ni, AT_REPARSE_POINT, AT_UNNAMED, 0); + if (na) { + /* + * read the existing reparse data (the tag is enough) + * and un-index it + */ + xr = open_reparse_index(ni->vol); + if (xr) { + if (remove_reparse_index(na,xr,&reparse_tag) < 0) + res = -1; + xrni = xr->ni; + ntfs_index_entry_mark_dirty(xr); + NInoSetDirty(xrni); + ntfs_index_ctx_put(xr); + ntfs_inode_close(xrni); + } + ntfs_attr_close(na); + } + return (res); } -#ifdef HAVE_SETXATTR /* extended attributes interface required */ +#ifdef HAVE_SETXATTR /* extended attributes interface required */ /* - * Get the ntfs reparse data into an extended attribute + * Get the ntfs reparse data into an extended attribute * - * Returns the reparse data size - * and the buffer is updated if it is long enough + * Returns the reparse data size + * and the buffer is updated if it is long enough */ -int ntfs_get_ntfs_reparse_data( ntfs_inode *ni, char *value, size_t size ) +int ntfs_get_ntfs_reparse_data(ntfs_inode *ni, char *value, size_t size) { - REPARSE_POINT *reparse_attr; - s64 attr_size; + REPARSE_POINT *reparse_attr; + s64 attr_size; - attr_size = 0; /* default to no data and no error */ - if ( ni ) - { - if ( ni->flags & FILE_ATTR_REPARSE_POINT ) - { - reparse_attr = ( REPARSE_POINT* )ntfs_attr_readall( ni, - AT_REPARSE_POINT, ( ntfschar* )NULL, 0, &attr_size ); - if ( reparse_attr ) - { - if ( attr_size <= ( s64 )size ) - { - if ( value ) - memcpy( value, reparse_attr, - attr_size ); - else - errno = EINVAL; - } - free( reparse_attr ); - } - } - else - errno = ENODATA; - } - return ( attr_size ? ( int )attr_size : -errno ); + attr_size = 0; /* default to no data and no error */ + if (ni) { + if (ni->flags & FILE_ATTR_REPARSE_POINT) { + reparse_attr = (REPARSE_POINT*)ntfs_attr_readall(ni, + AT_REPARSE_POINT,(ntfschar*)NULL, 0, &attr_size); + if (reparse_attr) { + if (attr_size <= (s64)size) { + if (value) + memcpy(value,reparse_attr, + attr_size); + else + errno = EINVAL; + } + free(reparse_attr); + } + } else + errno = ENODATA; + } + return (attr_size ? (int)attr_size : -errno); } /* - * Set the reparse data from an extended attribute + * Set the reparse data from an extended attribute * - * Warning : the new data is not checked + * Warning : the new data is not checked * - * Returns 0, or -1 if there is a problem + * Returns 0, or -1 if there is a problem */ -int ntfs_set_ntfs_reparse_data( ntfs_inode *ni, - const char *value, size_t size, int flags ) +int ntfs_set_ntfs_reparse_data(ntfs_inode *ni, + const char *value, size_t size, int flags) { - int res; - u8 dummy; - ntfs_inode *xrni; - ntfs_index_context *xr; + int res; + u8 dummy; + ntfs_inode *xrni; + ntfs_index_context *xr; - res = 0; - if ( ni && valid_reparse_data( ni, ( const REPARSE_POINT* )value, size ) ) - { - xr = open_reparse_index( ni->vol ); - if ( xr ) - { - if ( !ntfs_attr_exist( ni, AT_REPARSE_POINT, - AT_UNNAMED, 0 ) ) - { - if ( !( flags & XATTR_REPLACE ) ) - { - /* - * no reparse data attribute : add one, - * apparently, this does not feed the new value in - * Note : NTFS version must be >= 3 - */ - if ( ni->vol->major_ver >= 3 ) - { - res = ntfs_attr_add( ni, - AT_REPARSE_POINT, - AT_UNNAMED, 0, &dummy, - ( s64 )0 ); - if ( !res ) - { - ni->flags |= - FILE_ATTR_REPARSE_POINT; - NInoFileNameSetDirty( ni ); - } - NInoSetDirty( ni ); - } - else - { - errno = EOPNOTSUPP; - res = -1; - } - } - else - { - errno = ENODATA; - res = -1; - } - } - else - { - if ( flags & XATTR_CREATE ) - { - errno = EEXIST; - res = -1; - } - } - if ( !res ) - { - /* update value and index */ - res = update_reparse_data( ni, xr, value, size ); - } - xrni = xr->ni; - ntfs_index_entry_mark_dirty( xr ); - NInoSetDirty( xrni ); - ntfs_index_ctx_put( xr ); - ntfs_inode_close( xrni ); - } - else - { - res = -1; - } - } - else - { - errno = EINVAL; - res = -1; - } - return ( res ? -1 : 0 ); + res = 0; + if (ni && valid_reparse_data(ni, (const REPARSE_POINT*)value, size)) { + xr = open_reparse_index(ni->vol); + if (xr) { + if (!ntfs_attr_exist(ni,AT_REPARSE_POINT, + AT_UNNAMED,0)) { + if (!(flags & XATTR_REPLACE)) { + /* + * no reparse data attribute : add one, + * apparently, this does not feed the new value in + * Note : NTFS version must be >= 3 + */ + if (ni->vol->major_ver >= 3) { + res = ntfs_attr_add(ni, + AT_REPARSE_POINT, + AT_UNNAMED,0,&dummy, + (s64)0); + if (!res) { + ni->flags |= + FILE_ATTR_REPARSE_POINT; + NInoFileNameSetDirty(ni); + } + NInoSetDirty(ni); + } else { + errno = EOPNOTSUPP; + res = -1; + } + } else { + errno = ENODATA; + res = -1; + } + } else { + if (flags & XATTR_CREATE) { + errno = EEXIST; + res = -1; + } + } + if (!res) { + /* update value and index */ + res = update_reparse_data(ni,xr,value,size); + } + xrni = xr->ni; + ntfs_index_entry_mark_dirty(xr); + NInoSetDirty(xrni); + ntfs_index_ctx_put(xr); + ntfs_inode_close(xrni); + } else { + res = -1; + } + } else { + errno = EINVAL; + res = -1; + } + return (res ? -1 : 0); } /* - * Remove the reparse data + * Remove the reparse data * - * Returns 0, or -1 if there is a problem + * Returns 0, or -1 if there is a problem */ -int ntfs_remove_ntfs_reparse_data( ntfs_inode *ni ) +int ntfs_remove_ntfs_reparse_data(ntfs_inode *ni) { - int res; - int olderrno; - ntfs_attr *na; - ntfs_inode *xrni; - ntfs_index_context *xr; - le32 reparse_tag; + int res; + int olderrno; + ntfs_attr *na; + ntfs_inode *xrni; + ntfs_index_context *xr; + le32 reparse_tag; - res = 0; - if ( ni ) - { - /* - * open and delete the reparse data - */ - na = ntfs_attr_open( ni, AT_REPARSE_POINT, - AT_UNNAMED, 0 ); - if ( na ) - { - /* first remove index (reparse data needed) */ - xr = open_reparse_index( ni->vol ); - if ( xr ) - { - if ( remove_reparse_index( na, xr, - &reparse_tag ) < 0 ) - { - res = -1; - } - else - { - /* now remove attribute */ - res = ntfs_attr_rm( na ); - if ( !res ) - { - ni->flags &= - ~FILE_ATTR_REPARSE_POINT; - NInoFileNameSetDirty( ni ); - } - else - { - /* - * If we could not remove the - * attribute, try to restore the - * index and log the error. There - * will be an inconsistency if - * the reindexing fails. - */ - set_reparse_index( ni, xr, - reparse_tag ); - ntfs_log_error( - "Failed to remove reparse data." - " Possible corruption.\n" ); - } - } - xrni = xr->ni; - ntfs_index_entry_mark_dirty( xr ); - NInoSetDirty( xrni ); - ntfs_index_ctx_put( xr ); - ntfs_inode_close( xrni ); - } - olderrno = errno; - ntfs_attr_close( na ); - /* avoid errno pollution */ - if ( errno == ENOENT ) - errno = olderrno; - } - else - { - errno = ENODATA; - res = -1; - } - NInoSetDirty( ni ); - } - else - { - errno = EINVAL; - res = -1; - } - return ( res ? -1 : 0 ); + res = 0; + if (ni) { + /* + * open and delete the reparse data + */ + na = ntfs_attr_open(ni, AT_REPARSE_POINT, + AT_UNNAMED,0); + if (na) { + /* first remove index (reparse data needed) */ + xr = open_reparse_index(ni->vol); + if (xr) { + if (remove_reparse_index(na,xr, + &reparse_tag) < 0) { + res = -1; + } else { + /* now remove attribute */ + res = ntfs_attr_rm(na); + if (!res) { + ni->flags &= + ~FILE_ATTR_REPARSE_POINT; + NInoFileNameSetDirty(ni); + } else { + /* + * If we could not remove the + * attribute, try to restore the + * index and log the error. There + * will be an inconsistency if + * the reindexing fails. + */ + set_reparse_index(ni, xr, + reparse_tag); + ntfs_log_error( + "Failed to remove reparse data." + " Possible corruption.\n"); + } + } + xrni = xr->ni; + ntfs_index_entry_mark_dirty(xr); + NInoSetDirty(xrni); + ntfs_index_ctx_put(xr); + ntfs_inode_close(xrni); + } + olderrno = errno; + ntfs_attr_close(na); + /* avoid errno pollution */ + if (errno == ENOENT) + errno = olderrno; + } else { + errno = ENODATA; + res = -1; + } + NInoSetDirty(ni); + } else { + errno = EINVAL; + res = -1; + } + return (res ? -1 : 0); } #endif /* HAVE_SETXATTR */ diff --git a/source/libntfs/reparse.h b/source/libntfs/reparse.h index 4623fad4..35f4aa45 100644 --- a/source/libntfs/reparse.h +++ b/source/libntfs/reparse.h @@ -24,16 +24,16 @@ #ifndef REPARSE_H #define REPARSE_H -char *ntfs_make_symlink( ntfs_inode *ni, const char *mnt_point, - int *pattr_size ); -BOOL ntfs_possible_symlink( ntfs_inode *ni ); +char *ntfs_make_symlink(ntfs_inode *ni, const char *mnt_point, + int *pattr_size); +BOOL ntfs_possible_symlink(ntfs_inode *ni); -int ntfs_get_ntfs_reparse_data( ntfs_inode *ni, char *value, size_t size ); +int ntfs_get_ntfs_reparse_data(ntfs_inode *ni, char *value, size_t size); -int ntfs_set_ntfs_reparse_data( ntfs_inode *ni, const char *value, - size_t size, int flags ); -int ntfs_remove_ntfs_reparse_data( ntfs_inode *ni ); +int ntfs_set_ntfs_reparse_data(ntfs_inode *ni, const char *value, + size_t size, int flags); +int ntfs_remove_ntfs_reparse_data(ntfs_inode *ni); -int ntfs_delete_reparse_index( ntfs_inode *ni ); +int ntfs_delete_reparse_index(ntfs_inode *ni); #endif /* REPARSE_H */ diff --git a/source/libntfs/runlist.c b/source/libntfs/runlist.c index 2fe86eb7..cea24672 100644 --- a/source/libntfs/runlist.c +++ b/source/libntfs/runlist.c @@ -60,10 +60,10 @@ * * Returns: */ -static void ntfs_rl_mm( runlist_element *base, int dst, int src, int size ) +static void ntfs_rl_mm(runlist_element *base, int dst, int src, int size) { - if ( ( dst != src ) && ( size > 0 ) ) - memmove( base + dst, base + src, size * sizeof( *base ) ); + if ((dst != src) && (size > 0)) + memmove(base + dst, base + src, size * sizeof(*base)); } /** @@ -78,138 +78,132 @@ static void ntfs_rl_mm( runlist_element *base, int dst, int src, int size ) * * Returns: */ -static void ntfs_rl_mc( runlist_element *dstbase, int dst, - runlist_element *srcbase, int src, int size ) +static void ntfs_rl_mc(runlist_element *dstbase, int dst, + runlist_element *srcbase, int src, int size) { - if ( size > 0 ) - memcpy( dstbase + dst, srcbase + src, size * sizeof( *dstbase ) ); + if (size > 0) + memcpy(dstbase + dst, srcbase + src, size * sizeof(*dstbase)); } /** * ntfs_rl_realloc - Reallocate memory for runlists - * @rl: original runlist - * @old_size: number of runlist elements in the original runlist @rl - * @new_size: number of runlist elements we need space for + * @rl: original runlist + * @old_size: number of runlist elements in the original runlist @rl + * @new_size: number of runlist elements we need space for * * As the runlists grow, more memory will be required. To prevent large * numbers of small reallocations of memory, this function returns a 4kiB block * of memory. * - * N.B. If the new allocation doesn't require a different number of 4kiB - * blocks in memory, the function will return the original pointer. + * N.B. If the new allocation doesn't require a different number of 4kiB + * blocks in memory, the function will return the original pointer. * * On success, return a pointer to the newly allocated, or recycled, memory. * On error, return NULL with errno set to the error code. */ -static runlist_element *ntfs_rl_realloc( runlist_element *rl, int old_size, - int new_size ) +static runlist_element *ntfs_rl_realloc(runlist_element *rl, int old_size, + int new_size) { - old_size = ( old_size * sizeof( runlist_element ) + 0xfff ) & ~0xfff; - new_size = ( new_size * sizeof( runlist_element ) + 0xfff ) & ~0xfff; - if ( old_size == new_size ) - return rl; - return realloc( rl, new_size ); + old_size = (old_size * sizeof(runlist_element) + 0xfff) & ~0xfff; + new_size = (new_size * sizeof(runlist_element) + 0xfff) & ~0xfff; + if (old_size == new_size) + return rl; + return realloc(rl, new_size); } /* - * Extend a runlist by some entry count - * The runlist may have to be reallocated + * Extend a runlist by some entry count + * The runlist may have to be reallocated * - * Returns the reallocated runlist - * or NULL if reallocation was not possible (with errno set) - * the runlist is left unchanged if the reallocation fails + * Returns the reallocated runlist + * or NULL if reallocation was not possible (with errno set) + * the runlist is left unchanged if the reallocation fails */ -runlist_element *ntfs_rl_extend( ntfs_attr *na, runlist_element *rl, - int more_entries ) +runlist_element *ntfs_rl_extend(ntfs_attr *na, runlist_element *rl, + int more_entries) { - runlist_element *newrl; - int last; - int irl; + runlist_element *newrl; + int last; + int irl; - if ( na->rl && rl ) - { - irl = ( int )( rl - na->rl ); - last = irl; - while ( na->rl[last].length ) - last++; - newrl = ntfs_rl_realloc( na->rl, last + 1, last + more_entries + 1 ); - if ( !newrl ) - { - errno = ENOMEM; - rl = ( runlist_element* )NULL; - } - else - na->rl = newrl; - rl = &newrl[irl]; - } - else - { - ntfs_log_error( "Cannot extend unmapped runlist" ); - errno = EIO; - rl = ( runlist_element* )NULL; - } - return ( rl ); + if (na->rl && rl) { + irl = (int)(rl - na->rl); + last = irl; + while (na->rl[last].length) + last++; + newrl = ntfs_rl_realloc(na->rl,last+1,last+more_entries+1); + if (!newrl) { + errno = ENOMEM; + rl = (runlist_element*)NULL; + } else + na->rl = newrl; + rl = &newrl[irl]; + } else { + ntfs_log_error("Cannot extend unmapped runlist"); + errno = EIO; + rl = (runlist_element*)NULL; + } + return (rl); } /** * ntfs_rl_are_mergeable - test if two runlists can be joined together - * @dst: original runlist - * @src: new runlist to test for mergeability with @dst + * @dst: original runlist + * @src: new runlist to test for mergeability with @dst * * Test if two runlists can be joined together. For this, their VCNs and LCNs * must be adjacent. * * Return: TRUE Success, the runlists can be merged. - * FALSE Failure, the runlists cannot be merged. + * FALSE Failure, the runlists cannot be merged. */ -static BOOL ntfs_rl_are_mergeable( runlist_element *dst, runlist_element *src ) +static BOOL ntfs_rl_are_mergeable(runlist_element *dst, runlist_element *src) { - if ( !dst || !src ) - { - ntfs_log_debug( "Eeek. ntfs_rl_are_mergeable() invoked with NULL " - "pointer!\n" ); - return FALSE; - } + if (!dst || !src) { + ntfs_log_debug("Eeek. ntfs_rl_are_mergeable() invoked with NULL " + "pointer!\n"); + return FALSE; + } - /* We can merge unmapped regions even if they are misaligned. */ - if ( ( dst->lcn == LCN_RL_NOT_MAPPED ) && ( src->lcn == LCN_RL_NOT_MAPPED ) ) - return TRUE; - /* If the runs are misaligned, we cannot merge them. */ - if ( ( dst->vcn + dst->length ) != src->vcn ) - return FALSE; - /* If both runs are non-sparse and contiguous, we can merge them. */ - if ( ( dst->lcn >= 0 ) && ( src->lcn >= 0 ) && - ( ( dst->lcn + dst->length ) == src->lcn ) ) - return TRUE; - /* If we are merging two holes, we can merge them. */ - if ( ( dst->lcn == LCN_HOLE ) && ( src->lcn == LCN_HOLE ) ) - return TRUE; - /* Cannot merge. */ - return FALSE; + /* We can merge unmapped regions even if they are misaligned. */ + if ((dst->lcn == LCN_RL_NOT_MAPPED) && (src->lcn == LCN_RL_NOT_MAPPED)) + return TRUE; + /* If the runs are misaligned, we cannot merge them. */ + if ((dst->vcn + dst->length) != src->vcn) + return FALSE; + /* If both runs are non-sparse and contiguous, we can merge them. */ + if ((dst->lcn >= 0) && (src->lcn >= 0) && + ((dst->lcn + dst->length) == src->lcn)) + return TRUE; + /* If we are merging two holes, we can merge them. */ + if ((dst->lcn == LCN_HOLE) && (src->lcn == LCN_HOLE)) + return TRUE; + /* Cannot merge. */ + return FALSE; } /** * __ntfs_rl_merge - merge two runlists without testing if they can be merged - * @dst: original, destination runlist - * @src: new runlist to merge with @dst + * @dst: original, destination runlist + * @src: new runlist to merge with @dst * * Merge the two runlists, writing into the destination runlist @dst. The * caller must make sure the runlists can be merged or this will corrupt the * destination runlist. */ -static void __ntfs_rl_merge( runlist_element *dst, runlist_element *src ) +static void __ntfs_rl_merge(runlist_element *dst, runlist_element *src) { - dst->length += src->length; + dst->length += src->length; } /** * ntfs_rl_append - append a runlist after a given element - * @dst: original runlist to be worked on - * @dsize: number of elements in @dst (including end marker) - * @src: runlist to be inserted into @dst - * @ssize: number of elements in @src (excluding end marker) - * @loc: append the new runlist @src after this element in @dst + * @dst: original runlist to be worked on + * @dsize: number of elements in @dst (including end marker) + * @src: runlist to be inserted into @dst + * @ssize: number of elements in @src (excluding end marker) + * @loc: append the new runlist @src after this element in @dst * * Append the runlist @src after element @loc in @dst. Merge the right end of * the new runlist, if necessary. Adjust the size of the hole before the @@ -223,61 +217,60 @@ static void __ntfs_rl_merge( runlist_element *dst, runlist_element *src ) * On error, return NULL, with errno set to the error code. Both runlists are * left unmodified. */ -static runlist_element *ntfs_rl_append( runlist_element *dst, int dsize, - runlist_element *src, int ssize, int loc ) +static runlist_element *ntfs_rl_append(runlist_element *dst, int dsize, + runlist_element *src, int ssize, int loc) { - BOOL right = FALSE; /* Right end of @src needs merging */ - int marker; /* End of the inserted runs */ + BOOL right = FALSE; /* Right end of @src needs merging */ + int marker; /* End of the inserted runs */ - if ( !dst || !src ) - { - ntfs_log_debug( "Eeek. ntfs_rl_append() invoked with NULL " - "pointer!\n" ); - errno = EINVAL; - return NULL; - } + if (!dst || !src) { + ntfs_log_debug("Eeek. ntfs_rl_append() invoked with NULL " + "pointer!\n"); + errno = EINVAL; + return NULL; + } - /* First, check if the right hand end needs merging. */ - if ( ( loc + 1 ) < dsize ) - right = ntfs_rl_are_mergeable( src + ssize - 1, dst + loc + 1 ); + /* First, check if the right hand end needs merging. */ + if ((loc + 1) < dsize) + right = ntfs_rl_are_mergeable(src + ssize - 1, dst + loc + 1); - /* Space required: @dst size + @src size, less one if we merged. */ - dst = ntfs_rl_realloc( dst, dsize, dsize + ssize - right ); - if ( !dst ) - return NULL; - /* - * We are guaranteed to succeed from here so can start modifying the - * original runlists. - */ + /* Space required: @dst size + @src size, less one if we merged. */ + dst = ntfs_rl_realloc(dst, dsize, dsize + ssize - right); + if (!dst) + return NULL; + /* + * We are guaranteed to succeed from here so can start modifying the + * original runlists. + */ - /* First, merge the right hand end, if necessary. */ - if ( right ) - __ntfs_rl_merge( src + ssize - 1, dst + loc + 1 ); + /* First, merge the right hand end, if necessary. */ + if (right) + __ntfs_rl_merge(src + ssize - 1, dst + loc + 1); - /* marker - First run after the @src runs that have been inserted */ - marker = loc + ssize + 1; + /* marker - First run after the @src runs that have been inserted */ + marker = loc + ssize + 1; - /* Move the tail of @dst out of the way, then copy in @src. */ - ntfs_rl_mm( dst, marker, loc + 1 + right, dsize - loc - 1 - right ); - ntfs_rl_mc( dst, loc + 1, src, 0, ssize ); + /* Move the tail of @dst out of the way, then copy in @src. */ + ntfs_rl_mm(dst, marker, loc + 1 + right, dsize - loc - 1 - right); + ntfs_rl_mc(dst, loc + 1, src, 0, ssize); - /* Adjust the size of the preceding hole. */ - dst[loc].length = dst[loc + 1].vcn - dst[loc].vcn; + /* Adjust the size of the preceding hole. */ + dst[loc].length = dst[loc + 1].vcn - dst[loc].vcn; - /* We may have changed the length of the file, so fix the end marker */ - if ( dst[marker].lcn == LCN_ENOENT ) - dst[marker].vcn = dst[marker-1].vcn + dst[marker-1].length; + /* We may have changed the length of the file, so fix the end marker */ + if (dst[marker].lcn == LCN_ENOENT) + dst[marker].vcn = dst[marker-1].vcn + dst[marker-1].length; - return dst; + return dst; } /** * ntfs_rl_insert - insert a runlist into another - * @dst: original runlist to be worked on - * @dsize: number of elements in @dst (including end marker) - * @src: new runlist to be inserted - * @ssize: number of elements in @src (excluding end marker) - * @loc: insert the new runlist @src before this element in @dst + * @dst: original runlist to be worked on + * @dsize: number of elements in @dst (including end marker) + * @src: new runlist to be inserted + * @ssize: number of elements in @src (excluding end marker) + * @loc: insert the new runlist @src before this element in @dst * * Insert the runlist @src before element @loc in the runlist @dst. Merge the * left end of the new runlist, if necessary. Adjust the size of the hole @@ -291,96 +284,90 @@ static runlist_element *ntfs_rl_append( runlist_element *dst, int dsize, * On error, return NULL, with errno set to the error code. Both runlists are * left unmodified. */ -static runlist_element *ntfs_rl_insert( runlist_element *dst, int dsize, - runlist_element *src, int ssize, int loc ) +static runlist_element *ntfs_rl_insert(runlist_element *dst, int dsize, + runlist_element *src, int ssize, int loc) { - BOOL left = FALSE; /* Left end of @src needs merging */ - BOOL disc = FALSE; /* Discontinuity between @dst and @src */ - int marker; /* End of the inserted runs */ + BOOL left = FALSE; /* Left end of @src needs merging */ + BOOL disc = FALSE; /* Discontinuity between @dst and @src */ + int marker; /* End of the inserted runs */ - if ( !dst || !src ) - { - ntfs_log_debug( "Eeek. ntfs_rl_insert() invoked with NULL " - "pointer!\n" ); - errno = EINVAL; - return NULL; - } + if (!dst || !src) { + ntfs_log_debug("Eeek. ntfs_rl_insert() invoked with NULL " + "pointer!\n"); + errno = EINVAL; + return NULL; + } - /* disc => Discontinuity between the end of @dst and the start of @src. - * This means we might need to insert a "notmapped" run. - */ - if ( loc == 0 ) - disc = ( src[0].vcn > 0 ); - else - { - s64 merged_length; + /* disc => Discontinuity between the end of @dst and the start of @src. + * This means we might need to insert a "notmapped" run. + */ + if (loc == 0) + disc = (src[0].vcn > 0); + else { + s64 merged_length; - left = ntfs_rl_are_mergeable( dst + loc - 1, src ); + left = ntfs_rl_are_mergeable(dst + loc - 1, src); - merged_length = dst[loc - 1].length; - if ( left ) - merged_length += src->length; + merged_length = dst[loc - 1].length; + if (left) + merged_length += src->length; - disc = ( src[0].vcn > dst[loc - 1].vcn + merged_length ); - } + disc = (src[0].vcn > dst[loc - 1].vcn + merged_length); + } - /* Space required: @dst size + @src size, less one if we merged, plus - * one if there was a discontinuity. - */ - dst = ntfs_rl_realloc( dst, dsize, dsize + ssize - left + disc ); - if ( !dst ) - return NULL; - /* - * We are guaranteed to succeed from here so can start modifying the - * original runlist. - */ + /* Space required: @dst size + @src size, less one if we merged, plus + * one if there was a discontinuity. + */ + dst = ntfs_rl_realloc(dst, dsize, dsize + ssize - left + disc); + if (!dst) + return NULL; + /* + * We are guaranteed to succeed from here so can start modifying the + * original runlist. + */ - if ( left ) - __ntfs_rl_merge( dst + loc - 1, src ); + if (left) + __ntfs_rl_merge(dst + loc - 1, src); - /* - * marker - First run after the @src runs that have been inserted - * Nominally: marker = @loc + @ssize (location + number of runs in @src) - * If "left", then the first run in @src has been merged with one in @dst. - * If "disc", then @dst and @src don't meet and we need an extra run to fill the gap. - */ - marker = loc + ssize - left + disc; + /* + * marker - First run after the @src runs that have been inserted + * Nominally: marker = @loc + @ssize (location + number of runs in @src) + * If "left", then the first run in @src has been merged with one in @dst. + * If "disc", then @dst and @src don't meet and we need an extra run to fill the gap. + */ + marker = loc + ssize - left + disc; - /* Move the tail of @dst out of the way, then copy in @src. */ - ntfs_rl_mm( dst, marker, loc, dsize - loc ); - ntfs_rl_mc( dst, loc + disc, src, left, ssize - left ); + /* Move the tail of @dst out of the way, then copy in @src. */ + ntfs_rl_mm(dst, marker, loc, dsize - loc); + ntfs_rl_mc(dst, loc + disc, src, left, ssize - left); - /* Adjust the VCN of the first run after the insertion ... */ - dst[marker].vcn = dst[marker - 1].vcn + dst[marker - 1].length; - /* ... and the length. */ - if ( dst[marker].lcn == LCN_HOLE || dst[marker].lcn == LCN_RL_NOT_MAPPED ) - dst[marker].length = dst[marker + 1].vcn - dst[marker].vcn; + /* Adjust the VCN of the first run after the insertion ... */ + dst[marker].vcn = dst[marker - 1].vcn + dst[marker - 1].length; + /* ... and the length. */ + if (dst[marker].lcn == LCN_HOLE || dst[marker].lcn == LCN_RL_NOT_MAPPED) + dst[marker].length = dst[marker + 1].vcn - dst[marker].vcn; - /* Writing beyond the end of the file and there's a discontinuity. */ - if ( disc ) - { - if ( loc > 0 ) - { - dst[loc].vcn = dst[loc - 1].vcn + dst[loc - 1].length; - dst[loc].length = dst[loc + 1].vcn - dst[loc].vcn; - } - else - { - dst[loc].vcn = 0; - dst[loc].length = dst[loc + 1].vcn; - } - dst[loc].lcn = LCN_RL_NOT_MAPPED; - } - return dst; + /* Writing beyond the end of the file and there's a discontinuity. */ + if (disc) { + if (loc > 0) { + dst[loc].vcn = dst[loc - 1].vcn + dst[loc - 1].length; + dst[loc].length = dst[loc + 1].vcn - dst[loc].vcn; + } else { + dst[loc].vcn = 0; + dst[loc].length = dst[loc + 1].vcn; + } + dst[loc].lcn = LCN_RL_NOT_MAPPED; + } + return dst; } /** * ntfs_rl_replace - overwrite a runlist element with another runlist - * @dst: original runlist to be worked on - * @dsize: number of elements in @dst (including end marker) - * @src: new runlist to be inserted - * @ssize: number of elements in @src (excluding end marker) - * @loc: index in runlist @dst to overwrite with @src + * @dst: original runlist to be worked on + * @dsize: number of elements in @dst (including end marker) + * @src: new runlist to be inserted + * @ssize: number of elements in @src (excluding end marker) + * @loc: index in runlist @dst to overwrite with @src * * Replace the runlist element @dst at @loc with @src. Merge the left and * right ends of the inserted runlist, if necessary. @@ -393,83 +380,81 @@ static runlist_element *ntfs_rl_insert( runlist_element *dst, int dsize, * On error, return NULL, with errno set to the error code. Both runlists are * left unmodified. */ -static runlist_element *ntfs_rl_replace( runlist_element *dst, int dsize, - runlist_element *src, int ssize, - int loc ) +static runlist_element *ntfs_rl_replace(runlist_element *dst, int dsize, + runlist_element *src, int ssize, + int loc) { - signed delta; - BOOL left = FALSE; /* Left end of @src needs merging */ - BOOL right = FALSE; /* Right end of @src needs merging */ - int tail; /* Start of tail of @dst */ - int marker; /* End of the inserted runs */ + signed delta; + BOOL left = FALSE; /* Left end of @src needs merging */ + BOOL right = FALSE; /* Right end of @src needs merging */ + int tail; /* Start of tail of @dst */ + int marker; /* End of the inserted runs */ - if ( !dst || !src ) - { - ntfs_log_debug( "Eeek. ntfs_rl_replace() invoked with NULL " - "pointer!\n" ); - errno = EINVAL; - return NULL; - } + if (!dst || !src) { + ntfs_log_debug("Eeek. ntfs_rl_replace() invoked with NULL " + "pointer!\n"); + errno = EINVAL; + return NULL; + } - /* First, see if the left and right ends need merging. */ - if ( ( loc + 1 ) < dsize ) - right = ntfs_rl_are_mergeable( src + ssize - 1, dst + loc + 1 ); - if ( loc > 0 ) - left = ntfs_rl_are_mergeable( dst + loc - 1, src ); + /* First, see if the left and right ends need merging. */ + if ((loc + 1) < dsize) + right = ntfs_rl_are_mergeable(src + ssize - 1, dst + loc + 1); + if (loc > 0) + left = ntfs_rl_are_mergeable(dst + loc - 1, src); - /* Allocate some space. We'll need less if the left, right, or both - * ends get merged. The -1 accounts for the run being replaced. - */ - delta = ssize - 1 - left - right; - if ( delta > 0 ) - { - dst = ntfs_rl_realloc( dst, dsize, dsize + delta ); - if ( !dst ) - return NULL; - } - /* - * We are guaranteed to succeed from here so can start modifying the - * original runlists. - */ + /* Allocate some space. We'll need less if the left, right, or both + * ends get merged. The -1 accounts for the run being replaced. + */ + delta = ssize - 1 - left - right; + if (delta > 0) { + dst = ntfs_rl_realloc(dst, dsize, dsize + delta); + if (!dst) + return NULL; + } + /* + * We are guaranteed to succeed from here so can start modifying the + * original runlists. + */ - /* First, merge the left and right ends, if necessary. */ - if ( right ) - __ntfs_rl_merge( src + ssize - 1, dst + loc + 1 ); - if ( left ) - __ntfs_rl_merge( dst + loc - 1, src ); + /* First, merge the left and right ends, if necessary. */ + if (right) + __ntfs_rl_merge(src + ssize - 1, dst + loc + 1); + if (left) + __ntfs_rl_merge(dst + loc - 1, src); - /* - * tail - Offset of the tail of @dst - * Nominally: @tail = @loc + 1 (location, skipping the replaced run) - * If "right", then one of @dst's runs is already merged into @src. - */ - tail = loc + right + 1; + /* + * tail - Offset of the tail of @dst + * Nominally: @tail = @loc + 1 (location, skipping the replaced run) + * If "right", then one of @dst's runs is already merged into @src. + */ + tail = loc + right + 1; - /* - * marker - First run after the @src runs that have been inserted - * Nominally: @marker = @loc + @ssize (location + number of runs in @src) - * If "left", then the first run in @src has been merged with one in @dst. - */ - marker = loc + ssize - left; + /* + * marker - First run after the @src runs that have been inserted + * Nominally: @marker = @loc + @ssize (location + number of runs in @src) + * If "left", then the first run in @src has been merged with one in @dst. + */ + marker = loc + ssize - left; - /* Move the tail of @dst out of the way, then copy in @src. */ - ntfs_rl_mm( dst, marker, tail, dsize - tail ); - ntfs_rl_mc( dst, loc, src, left, ssize - left ); + /* Move the tail of @dst out of the way, then copy in @src. */ + ntfs_rl_mm(dst, marker, tail, dsize - tail); + ntfs_rl_mc(dst, loc, src, left, ssize - left); - /* We may have changed the length of the file, so fix the end marker */ - if ( ( ( dsize - tail ) > 0 ) && ( dst[marker].lcn == LCN_ENOENT ) ) - dst[marker].vcn = dst[marker - 1].vcn + dst[marker - 1].length; + /* We may have changed the length of the file, so fix the end marker */ + if (((dsize - tail) > 0) && (dst[marker].lcn == LCN_ENOENT)) + dst[marker].vcn = dst[marker - 1].vcn + dst[marker - 1].length; - return dst; + return dst; } /** * ntfs_rl_split - insert a runlist into the centre of a hole - * @dst: original runlist to be worked on - * @dsize: number of elements in @dst (including end marker) - * @src: new runlist to be inserted - * @ssize: number of elements in @src (excluding end marker) - * @loc: index in runlist @dst at which to split and insert @src + * @dst: original runlist to be worked on + * @dsize: number of elements in @dst (including end marker) + * @src: new runlist to be inserted + * @ssize: number of elements in @src (excluding end marker) + * @loc: index in runlist @dst at which to split and insert @src * * Split the runlist @dst at @loc into two and insert @new in between the two * fragments. No merging of runlists is necessary. Adjust the size of the @@ -483,268 +468,251 @@ static runlist_element *ntfs_rl_replace( runlist_element *dst, int dsize, * On error, return NULL, with errno set to the error code. Both runlists are * left unmodified. */ -static runlist_element *ntfs_rl_split( runlist_element *dst, int dsize, - runlist_element *src, int ssize, int loc ) +static runlist_element *ntfs_rl_split(runlist_element *dst, int dsize, + runlist_element *src, int ssize, int loc) { - if ( !dst || !src ) - { - ntfs_log_debug( "Eeek. ntfs_rl_split() invoked with NULL pointer!\n" ); - errno = EINVAL; - return NULL; - } + if (!dst || !src) { + ntfs_log_debug("Eeek. ntfs_rl_split() invoked with NULL pointer!\n"); + errno = EINVAL; + return NULL; + } - /* Space required: @dst size + @src size + one new hole. */ - dst = ntfs_rl_realloc( dst, dsize, dsize + ssize + 1 ); - if ( !dst ) - return dst; - /* - * We are guaranteed to succeed from here so can start modifying the - * original runlists. - */ + /* Space required: @dst size + @src size + one new hole. */ + dst = ntfs_rl_realloc(dst, dsize, dsize + ssize + 1); + if (!dst) + return dst; + /* + * We are guaranteed to succeed from here so can start modifying the + * original runlists. + */ - /* Move the tail of @dst out of the way, then copy in @src. */ - ntfs_rl_mm( dst, loc + 1 + ssize, loc, dsize - loc ); - ntfs_rl_mc( dst, loc + 1, src, 0, ssize ); + /* Move the tail of @dst out of the way, then copy in @src. */ + ntfs_rl_mm(dst, loc + 1 + ssize, loc, dsize - loc); + ntfs_rl_mc(dst, loc + 1, src, 0, ssize); - /* Adjust the size of the holes either size of @src. */ - dst[loc].length = dst[loc+1].vcn - dst[loc].vcn; - dst[loc+ssize+1].vcn = dst[loc+ssize].vcn + dst[loc+ssize].length; - dst[loc+ssize+1].length = dst[loc+ssize+2].vcn - dst[loc+ssize+1].vcn; + /* Adjust the size of the holes either size of @src. */ + dst[loc].length = dst[loc+1].vcn - dst[loc].vcn; + dst[loc+ssize+1].vcn = dst[loc+ssize].vcn + dst[loc+ssize].length; + dst[loc+ssize+1].length = dst[loc+ssize+2].vcn - dst[loc+ssize+1].vcn; - return dst; + return dst; } /** * ntfs_runlists_merge_i - see ntfs_runlists_merge */ -static runlist_element *ntfs_runlists_merge_i( runlist_element *drl, - runlist_element *srl ) +static runlist_element *ntfs_runlists_merge_i(runlist_element *drl, + runlist_element *srl) { - int di, si; /* Current index into @[ds]rl. */ - int sstart; /* First index with lcn > LCN_RL_NOT_MAPPED. */ - int dins; /* Index into @drl at which to insert @srl. */ - int dend, send; /* Last index into @[ds]rl. */ - int dfinal, sfinal; /* The last index into @[ds]rl with - lcn >= LCN_HOLE. */ - int marker = 0; - VCN marker_vcn = 0; + int di, si; /* Current index into @[ds]rl. */ + int sstart; /* First index with lcn > LCN_RL_NOT_MAPPED. */ + int dins; /* Index into @drl at which to insert @srl. */ + int dend, send; /* Last index into @[ds]rl. */ + int dfinal, sfinal; /* The last index into @[ds]rl with + lcn >= LCN_HOLE. */ + int marker = 0; + VCN marker_vcn = 0; - ntfs_log_debug( "dst:\n" ); - ntfs_debug_runlist_dump( drl ); - ntfs_log_debug( "src:\n" ); - ntfs_debug_runlist_dump( srl ); + ntfs_log_debug("dst:\n"); + ntfs_debug_runlist_dump(drl); + ntfs_log_debug("src:\n"); + ntfs_debug_runlist_dump(srl); - /* Check for silly calling... */ - if ( !srl ) - return drl; + /* Check for silly calling... */ + if (!srl) + return drl; - /* Check for the case where the first mapping is being done now. */ - if ( !drl ) - { - drl = srl; - /* Complete the source runlist if necessary. */ - if ( drl[0].vcn ) - { - /* Scan to the end of the source runlist. */ - for ( dend = 0; drl[dend].length; dend++ ) - ; - dend++; - drl = ntfs_rl_realloc( drl, dend, dend + 1 ); - if ( !drl ) - return drl; - /* Insert start element at the front of the runlist. */ - ntfs_rl_mm( drl, 1, 0, dend ); - drl[0].vcn = 0; - drl[0].lcn = LCN_RL_NOT_MAPPED; - drl[0].length = drl[1].vcn; - } - goto finished; - } + /* Check for the case where the first mapping is being done now. */ + if (!drl) { + drl = srl; + /* Complete the source runlist if necessary. */ + if (drl[0].vcn) { + /* Scan to the end of the source runlist. */ + for (dend = 0; drl[dend].length; dend++) + ; + dend++; + drl = ntfs_rl_realloc(drl, dend, dend + 1); + if (!drl) + return drl; + /* Insert start element at the front of the runlist. */ + ntfs_rl_mm(drl, 1, 0, dend); + drl[0].vcn = 0; + drl[0].lcn = LCN_RL_NOT_MAPPED; + drl[0].length = drl[1].vcn; + } + goto finished; + } - si = di = 0; + si = di = 0; - /* Skip any unmapped start element(s) in the source runlist. */ - while ( srl[si].length && srl[si].lcn < ( LCN )LCN_HOLE ) - si++; + /* Skip any unmapped start element(s) in the source runlist. */ + while (srl[si].length && srl[si].lcn < (LCN)LCN_HOLE) + si++; - /* Can't have an entirely unmapped source runlist. */ - if ( !srl[si].length ) - { - errno = EINVAL; - ntfs_log_perror( "%s: unmapped source runlist", __FUNCTION__ ); - return NULL; - } + /* Can't have an entirely unmapped source runlist. */ + if (!srl[si].length) { + errno = EINVAL; + ntfs_log_perror("%s: unmapped source runlist", __FUNCTION__); + return NULL; + } - /* Record the starting points. */ - sstart = si; + /* Record the starting points. */ + sstart = si; - /* - * Skip forward in @drl until we reach the position where @srl needs to - * be inserted. If we reach the end of @drl, @srl just needs to be - * appended to @drl. - */ - for ( ; drl[di].length; di++ ) - { - if ( drl[di].vcn + drl[di].length > srl[sstart].vcn ) - break; - } - dins = di; + /* + * Skip forward in @drl until we reach the position where @srl needs to + * be inserted. If we reach the end of @drl, @srl just needs to be + * appended to @drl. + */ + for (; drl[di].length; di++) { + if (drl[di].vcn + drl[di].length > srl[sstart].vcn) + break; + } + dins = di; - /* Sanity check for illegal overlaps. */ - if ( ( drl[di].vcn == srl[si].vcn ) && ( drl[di].lcn >= 0 ) && - ( srl[si].lcn >= 0 ) ) - { - errno = ERANGE; - ntfs_log_perror( "Run lists overlap. Cannot merge" ); - return NULL; - } + /* Sanity check for illegal overlaps. */ + if ((drl[di].vcn == srl[si].vcn) && (drl[di].lcn >= 0) && + (srl[si].lcn >= 0)) { + errno = ERANGE; + ntfs_log_perror("Run lists overlap. Cannot merge"); + return NULL; + } - /* Scan to the end of both runlists in order to know their sizes. */ - for ( send = si; srl[send].length; send++ ) - ; - for ( dend = di; drl[dend].length; dend++ ) - ; + /* Scan to the end of both runlists in order to know their sizes. */ + for (send = si; srl[send].length; send++) + ; + for (dend = di; drl[dend].length; dend++) + ; - if ( srl[send].lcn == ( LCN )LCN_ENOENT ) - marker_vcn = srl[marker = send].vcn; + if (srl[send].lcn == (LCN)LCN_ENOENT) + marker_vcn = srl[marker = send].vcn; - /* Scan to the last element with lcn >= LCN_HOLE. */ - for ( sfinal = send; sfinal >= 0 && srl[sfinal].lcn < LCN_HOLE; sfinal-- ) - ; - for ( dfinal = dend; dfinal >= 0 && drl[dfinal].lcn < LCN_HOLE; dfinal-- ) - ; + /* Scan to the last element with lcn >= LCN_HOLE. */ + for (sfinal = send; sfinal >= 0 && srl[sfinal].lcn < LCN_HOLE; sfinal--) + ; + for (dfinal = dend; dfinal >= 0 && drl[dfinal].lcn < LCN_HOLE; dfinal--) + ; - { - BOOL start; - BOOL finish; - int ds = dend + 1; /* Number of elements in drl & srl */ - int ss = sfinal - sstart + 1; + { + BOOL start; + BOOL finish; + int ds = dend + 1; /* Number of elements in drl & srl */ + int ss = sfinal - sstart + 1; - start = ( ( drl[dins].lcn < LCN_RL_NOT_MAPPED ) || /* End of file */ - ( drl[dins].vcn == srl[sstart].vcn ) ); /* Start of hole */ - finish = ( ( drl[dins].lcn >= LCN_RL_NOT_MAPPED ) && /* End of file */ - ( ( drl[dins].vcn + drl[dins].length ) <= /* End of hole */ - ( srl[send - 1].vcn + srl[send - 1].length ) ) ); + start = ((drl[dins].lcn < LCN_RL_NOT_MAPPED) || /* End of file */ + (drl[dins].vcn == srl[sstart].vcn)); /* Start of hole */ + finish = ((drl[dins].lcn >= LCN_RL_NOT_MAPPED) && /* End of file */ + ((drl[dins].vcn + drl[dins].length) <= /* End of hole */ + (srl[send - 1].vcn + srl[send - 1].length))); - /* Or we'll lose an end marker */ - if ( finish && !drl[dins].length ) - ss++; - if ( marker && ( drl[dins].vcn + drl[dins].length > srl[send - 1].vcn ) ) - finish = FALSE; + /* Or we'll lose an end marker */ + if (finish && !drl[dins].length) + ss++; + if (marker && (drl[dins].vcn + drl[dins].length > srl[send - 1].vcn)) + finish = FALSE; - ntfs_log_debug( "dfinal = %i, dend = %i\n", dfinal, dend ); - ntfs_log_debug( "sstart = %i, sfinal = %i, send = %i\n", sstart, sfinal, send ); - ntfs_log_debug( "start = %i, finish = %i\n", start, finish ); - ntfs_log_debug( "ds = %i, ss = %i, dins = %i\n", ds, ss, dins ); + ntfs_log_debug("dfinal = %i, dend = %i\n", dfinal, dend); + ntfs_log_debug("sstart = %i, sfinal = %i, send = %i\n", sstart, sfinal, send); + ntfs_log_debug("start = %i, finish = %i\n", start, finish); + ntfs_log_debug("ds = %i, ss = %i, dins = %i\n", ds, ss, dins); - if ( start ) - { - if ( finish ) - drl = ntfs_rl_replace( drl, ds, srl + sstart, ss, dins ); - else - drl = ntfs_rl_insert( drl, ds, srl + sstart, ss, dins ); - } - else - { - if ( finish ) - drl = ntfs_rl_append( drl, ds, srl + sstart, ss, dins ); - else - drl = ntfs_rl_split( drl, ds, srl + sstart, ss, dins ); - } - if ( !drl ) - { - ntfs_log_perror( "Merge failed" ); - return drl; - } - free( srl ); - if ( marker ) - { - ntfs_log_debug( "Triggering marker code.\n" ); - for ( ds = dend; drl[ds].length; ds++ ) - ; - /* We only need to care if @srl ended after @drl. */ - if ( drl[ds].vcn <= marker_vcn ) - { - int slots = 0; + if (start) { + if (finish) + drl = ntfs_rl_replace(drl, ds, srl + sstart, ss, dins); + else + drl = ntfs_rl_insert(drl, ds, srl + sstart, ss, dins); + } else { + if (finish) + drl = ntfs_rl_append(drl, ds, srl + sstart, ss, dins); + else + drl = ntfs_rl_split(drl, ds, srl + sstart, ss, dins); + } + if (!drl) { + ntfs_log_perror("Merge failed"); + return drl; + } + free(srl); + if (marker) { + ntfs_log_debug("Triggering marker code.\n"); + for (ds = dend; drl[ds].length; ds++) + ; + /* We only need to care if @srl ended after @drl. */ + if (drl[ds].vcn <= marker_vcn) { + int slots = 0; - if ( drl[ds].vcn == marker_vcn ) - { - ntfs_log_debug( "Old marker = %lli, replacing with " - "LCN_ENOENT.\n", - ( long long )drl[ds].lcn ); - drl[ds].lcn = ( LCN )LCN_ENOENT; - goto finished; - } - /* - * We need to create an unmapped runlist element in - * @drl or extend an existing one before adding the - * ENOENT terminator. - */ - if ( drl[ds].lcn == ( LCN )LCN_ENOENT ) - { - ds--; - slots = 1; - } - if ( drl[ds].lcn != ( LCN )LCN_RL_NOT_MAPPED ) - { - /* Add an unmapped runlist element. */ - if ( !slots ) - { - /* FIXME/TODO: We need to have the - * extra memory already! (AIA) - */ - drl = ntfs_rl_realloc( drl, ds, ds + 2 ); - if ( !drl ) - goto critical_error; - slots = 2; - } - ds++; - /* Need to set vcn if it isn't set already. */ - if ( slots != 1 ) - drl[ds].vcn = drl[ds - 1].vcn + - drl[ds - 1].length; - drl[ds].lcn = ( LCN )LCN_RL_NOT_MAPPED; - /* We now used up a slot. */ - slots--; - } - drl[ds].length = marker_vcn - drl[ds].vcn; - /* Finally add the ENOENT terminator. */ - ds++; - if ( !slots ) - { - /* FIXME/TODO: We need to have the extra - * memory already! (AIA) - */ - drl = ntfs_rl_realloc( drl, ds, ds + 1 ); - if ( !drl ) - goto critical_error; - } - drl[ds].vcn = marker_vcn; - drl[ds].lcn = ( LCN )LCN_ENOENT; - drl[ds].length = ( s64 )0; - } - } - } + if (drl[ds].vcn == marker_vcn) { + ntfs_log_debug("Old marker = %lli, replacing with " + "LCN_ENOENT.\n", + (long long)drl[ds].lcn); + drl[ds].lcn = (LCN)LCN_ENOENT; + goto finished; + } + /* + * We need to create an unmapped runlist element in + * @drl or extend an existing one before adding the + * ENOENT terminator. + */ + if (drl[ds].lcn == (LCN)LCN_ENOENT) { + ds--; + slots = 1; + } + if (drl[ds].lcn != (LCN)LCN_RL_NOT_MAPPED) { + /* Add an unmapped runlist element. */ + if (!slots) { + /* FIXME/TODO: We need to have the + * extra memory already! (AIA) + */ + drl = ntfs_rl_realloc(drl, ds, ds + 2); + if (!drl) + goto critical_error; + slots = 2; + } + ds++; + /* Need to set vcn if it isn't set already. */ + if (slots != 1) + drl[ds].vcn = drl[ds - 1].vcn + + drl[ds - 1].length; + drl[ds].lcn = (LCN)LCN_RL_NOT_MAPPED; + /* We now used up a slot. */ + slots--; + } + drl[ds].length = marker_vcn - drl[ds].vcn; + /* Finally add the ENOENT terminator. */ + ds++; + if (!slots) { + /* FIXME/TODO: We need to have the extra + * memory already! (AIA) + */ + drl = ntfs_rl_realloc(drl, ds, ds + 1); + if (!drl) + goto critical_error; + } + drl[ds].vcn = marker_vcn; + drl[ds].lcn = (LCN)LCN_ENOENT; + drl[ds].length = (s64)0; + } + } + } finished: - /* The merge was completed successfully. */ - ntfs_log_debug( "Merged runlist:\n" ); - ntfs_debug_runlist_dump( drl ); - return drl; + /* The merge was completed successfully. */ + ntfs_log_debug("Merged runlist:\n"); + ntfs_debug_runlist_dump(drl); + return drl; critical_error: - /* Critical error! We cannot afford to fail here. */ - ntfs_log_perror( "libntfs: Critical error" ); - ntfs_log_debug( "Forcing segmentation fault!\n" ); - marker_vcn = ( ( runlist* )NULL )->lcn; - return drl; + /* Critical error! We cannot afford to fail here. */ + ntfs_log_perror("libntfs: Critical error"); + ntfs_log_debug("Forcing segmentation fault!\n"); + marker_vcn = ((runlist*)NULL)->lcn; + return drl; } /** * ntfs_runlists_merge - merge two runlists into one - * @drl: original runlist to be worked on - * @srl: new runlist to be merged into @drl + * @drl: original runlist to be worked on + * @srl: new runlist to be merged into @drl * * First we sanity check the two runlists @srl and @drl to make sure that they * are sensible and can be merged. The runlist @srl must be either after the @@ -755,10 +723,10 @@ critical_error: * 2. When new clusters are allocated to fill a hole or extend a file. * * There are four possible ways @srl can be merged. It can: - * - be inserted at the beginning of a hole, - * - split the hole in two and be inserted between the two fragments, - * - be appended at the end of a hole, or it can - * - replace the whole hole. + * - be inserted at the beginning of a hole, + * - split the hole in two and be inserted between the two fragments, + * - be appended at the end of a hole, or it can + * - replace the whole hole. * It can also be appended to the end of the runlist, which is just a variant * of the insert case. * @@ -769,26 +737,26 @@ critical_error: * * On error, return NULL, with errno set to the error code. Both runlists are * left unmodified. The following error codes are defined: - * ENOMEM Not enough memory to allocate runlist array. - * EINVAL Invalid parameters were passed in. - * ERANGE The runlists overlap and cannot be merged. + * ENOMEM Not enough memory to allocate runlist array. + * EINVAL Invalid parameters were passed in. + * ERANGE The runlists overlap and cannot be merged. */ -runlist_element *ntfs_runlists_merge( runlist_element *drl, - runlist_element *srl ) +runlist_element *ntfs_runlists_merge(runlist_element *drl, + runlist_element *srl) { - runlist_element *rl; - - ntfs_log_enter( "Entering\n" ); - rl = ntfs_runlists_merge_i( drl, srl ); - ntfs_log_leave( "\n" ); - return rl; + runlist_element *rl; + + ntfs_log_enter("Entering\n"); + rl = ntfs_runlists_merge_i(drl, srl); + ntfs_log_leave("\n"); + return rl; } /** * ntfs_mapping_pairs_decompress - convert mapping pairs array to runlist - * @vol: ntfs volume on which the attribute resides - * @attr: attribute record whose mapping pairs array to decompress - * @old_rl: optional runlist in which to insert @attr's runlist + * @vol: ntfs volume on which the attribute resides + * @attr: attribute record whose mapping pairs array to decompress + * @old_rl: optional runlist in which to insert @attr's runlist * * Decompress the attribute @attr's mapping pairs array into a runlist. On * success, return the decompressed runlist. @@ -801,276 +769,255 @@ runlist_element *ntfs_runlists_merge( runlist_element *drl, * unmodified in that case. * * The following error codes are defined: - * ENOMEM Not enough memory to allocate runlist array. - * EIO Corrupt runlist. - * EINVAL Invalid parameters were passed in. - * ERANGE The two runlists overlap. + * ENOMEM Not enough memory to allocate runlist array. + * EIO Corrupt runlist. + * EINVAL Invalid parameters were passed in. + * ERANGE The two runlists overlap. * * FIXME: For now we take the conceptionally simplest approach of creating the * new runlist disregarding the already existing one and then splicing the * two into one, if that is possible (we check for overlap and discard the new * runlist if overlap present before returning NULL, with errno = ERANGE). */ -static runlist_element *ntfs_mapping_pairs_decompress_i( const ntfs_volume *vol, - const ATTR_RECORD *attr, runlist_element *old_rl ) +static runlist_element *ntfs_mapping_pairs_decompress_i(const ntfs_volume *vol, + const ATTR_RECORD *attr, runlist_element *old_rl) { - VCN vcn; /* Current vcn. */ - LCN lcn; /* Current lcn. */ - s64 deltaxcn; /* Change in [vl]cn. */ - runlist_element *rl; /* The output runlist. */ - const u8 *buf; /* Current position in mapping pairs array. */ - const u8 *attr_end; /* End of attribute. */ - int err, rlsize; /* Size of runlist buffer. */ - u16 rlpos; /* Current runlist position in units of - runlist_elements. */ - u8 b; /* Current byte offset in buf. */ + VCN vcn; /* Current vcn. */ + LCN lcn; /* Current lcn. */ + s64 deltaxcn; /* Change in [vl]cn. */ + runlist_element *rl; /* The output runlist. */ + const u8 *buf; /* Current position in mapping pairs array. */ + const u8 *attr_end; /* End of attribute. */ + int err, rlsize; /* Size of runlist buffer. */ + u16 rlpos; /* Current runlist position in units of + runlist_elements. */ + u8 b; /* Current byte offset in buf. */ - ntfs_log_trace( "Entering for attr 0x%x.\n", - ( unsigned )le32_to_cpu( attr->type ) ); - /* Make sure attr exists and is non-resident. */ - if ( !attr || !attr->non_resident || - sle64_to_cpu( attr->lowest_vcn ) < ( VCN )0 ) - { - errno = EINVAL; - return NULL; - } - /* Start at vcn = lowest_vcn and lcn 0. */ - vcn = sle64_to_cpu( attr->lowest_vcn ); - lcn = 0; - /* Get start of the mapping pairs array. */ - buf = ( const u8* )attr + le16_to_cpu( attr->mapping_pairs_offset ); - attr_end = ( const u8* )attr + le32_to_cpu( attr->length ); - if ( buf < ( const u8* )attr || buf > attr_end ) - { - ntfs_log_debug( "Corrupt attribute.\n" ); - errno = EIO; - return NULL; - } - /* Current position in runlist array. */ - rlpos = 0; - /* Allocate first 4kiB block and set current runlist size to 4kiB. */ - rlsize = 0x1000; - rl = ntfs_malloc( rlsize ); - if ( !rl ) - return NULL; - /* Insert unmapped starting element if necessary. */ - if ( vcn ) - { - rl->vcn = ( VCN )0; - rl->lcn = ( LCN )LCN_RL_NOT_MAPPED; - rl->length = vcn; - rlpos++; - } - while ( buf < attr_end && *buf ) - { - /* - * Allocate more memory if needed, including space for the - * not-mapped and terminator elements. - */ - if ( ( int )( ( rlpos + 3 ) * sizeof( *old_rl ) ) > rlsize ) - { - runlist_element *rl2; + ntfs_log_trace("Entering for attr 0x%x.\n", + (unsigned)le32_to_cpu(attr->type)); + /* Make sure attr exists and is non-resident. */ + if (!attr || !attr->non_resident || + sle64_to_cpu(attr->lowest_vcn) < (VCN)0) { + errno = EINVAL; + return NULL; + } + /* Start at vcn = lowest_vcn and lcn 0. */ + vcn = sle64_to_cpu(attr->lowest_vcn); + lcn = 0; + /* Get start of the mapping pairs array. */ + buf = (const u8*)attr + le16_to_cpu(attr->mapping_pairs_offset); + attr_end = (const u8*)attr + le32_to_cpu(attr->length); + if (buf < (const u8*)attr || buf > attr_end) { + ntfs_log_debug("Corrupt attribute.\n"); + errno = EIO; + return NULL; + } + /* Current position in runlist array. */ + rlpos = 0; + /* Allocate first 4kiB block and set current runlist size to 4kiB. */ + rlsize = 0x1000; + rl = ntfs_malloc(rlsize); + if (!rl) + return NULL; + /* Insert unmapped starting element if necessary. */ + if (vcn) { + rl->vcn = (VCN)0; + rl->lcn = (LCN)LCN_RL_NOT_MAPPED; + rl->length = vcn; + rlpos++; + } + while (buf < attr_end && *buf) { + /* + * Allocate more memory if needed, including space for the + * not-mapped and terminator elements. + */ + if ((int)((rlpos + 3) * sizeof(*old_rl)) > rlsize) { + runlist_element *rl2; - rlsize += 0x1000; - rl2 = realloc( rl, rlsize ); - if ( !rl2 ) - { - int eo = errno; - free( rl ); - errno = eo; - return NULL; - } - rl = rl2; - } - /* Enter the current vcn into the current runlist element. */ - rl[rlpos].vcn = vcn; - /* - * Get the change in vcn, i.e. the run length in clusters. - * Doing it this way ensures that we signextend negative values. - * A negative run length doesn't make any sense, but hey, I - * didn't make up the NTFS specs and Windows NT4 treats the run - * length as a signed value so that's how it is... - */ - b = *buf & 0xf; - if ( b ) - { - if ( buf + b > attr_end ) - goto io_error; - for ( deltaxcn = ( s8 )buf[b--]; b; b-- ) - deltaxcn = ( deltaxcn << 8 ) + buf[b]; - } - else /* The length entry is compulsory. */ - { - ntfs_log_debug( "Missing length entry in mapping pairs " - "array.\n" ); - deltaxcn = ( s64 ) - 1; - } - /* - * Assume a negative length to indicate data corruption and - * hence clean-up and return NULL. - */ - if ( deltaxcn < 0 ) - { - ntfs_log_debug( "Invalid length in mapping pairs array.\n" ); - goto err_out; - } - /* - * Enter the current run length into the current runlist - * element. - */ - rl[rlpos].length = deltaxcn; - /* Increment the current vcn by the current run length. */ - vcn += deltaxcn; - /* - * There might be no lcn change at all, as is the case for - * sparse clusters on NTFS 3.0+, in which case we set the lcn - * to LCN_HOLE. - */ - if ( !( *buf & 0xf0 ) ) - rl[rlpos].lcn = ( LCN )LCN_HOLE; - else - { - /* Get the lcn change which really can be negative. */ - u8 b2 = *buf & 0xf; - b = b2 + ( ( *buf >> 4 ) & 0xf ); - if ( buf + b > attr_end ) - goto io_error; - for ( deltaxcn = ( s8 )buf[b--]; b > b2; b-- ) - deltaxcn = ( deltaxcn << 8 ) + buf[b]; - /* Change the current lcn to it's new value. */ - lcn += deltaxcn; + rlsize += 0x1000; + rl2 = realloc(rl, rlsize); + if (!rl2) { + int eo = errno; + free(rl); + errno = eo; + return NULL; + } + rl = rl2; + } + /* Enter the current vcn into the current runlist element. */ + rl[rlpos].vcn = vcn; + /* + * Get the change in vcn, i.e. the run length in clusters. + * Doing it this way ensures that we signextend negative values. + * A negative run length doesn't make any sense, but hey, I + * didn't make up the NTFS specs and Windows NT4 treats the run + * length as a signed value so that's how it is... + */ + b = *buf & 0xf; + if (b) { + if (buf + b > attr_end) + goto io_error; + for (deltaxcn = (s8)buf[b--]; b; b--) + deltaxcn = (deltaxcn << 8) + buf[b]; + } else { /* The length entry is compulsory. */ + ntfs_log_debug("Missing length entry in mapping pairs " + "array.\n"); + deltaxcn = (s64)-1; + } + /* + * Assume a negative length to indicate data corruption and + * hence clean-up and return NULL. + */ + if (deltaxcn < 0) { + ntfs_log_debug("Invalid length in mapping pairs array.\n"); + goto err_out; + } + /* + * Enter the current run length into the current runlist + * element. + */ + rl[rlpos].length = deltaxcn; + /* Increment the current vcn by the current run length. */ + vcn += deltaxcn; + /* + * There might be no lcn change at all, as is the case for + * sparse clusters on NTFS 3.0+, in which case we set the lcn + * to LCN_HOLE. + */ + if (!(*buf & 0xf0)) + rl[rlpos].lcn = (LCN)LCN_HOLE; + else { + /* Get the lcn change which really can be negative. */ + u8 b2 = *buf & 0xf; + b = b2 + ((*buf >> 4) & 0xf); + if (buf + b > attr_end) + goto io_error; + for (deltaxcn = (s8)buf[b--]; b > b2; b--) + deltaxcn = (deltaxcn << 8) + buf[b]; + /* Change the current lcn to it's new value. */ + lcn += deltaxcn; #ifdef DEBUG - /* - * On NTFS 1.2-, apparently can have lcn == -1 to - * indicate a hole. But we haven't verified ourselves - * whether it is really the lcn or the deltaxcn that is - * -1. So if either is found give us a message so we - * can investigate it further! - */ - if ( vol->major_ver < 3 ) - { - if ( deltaxcn == ( LCN ) - 1 ) - ntfs_log_debug( "lcn delta == -1\n" ); - if ( lcn == ( LCN ) - 1 ) - ntfs_log_debug( "lcn == -1\n" ); - } + /* + * On NTFS 1.2-, apparently can have lcn == -1 to + * indicate a hole. But we haven't verified ourselves + * whether it is really the lcn or the deltaxcn that is + * -1. So if either is found give us a message so we + * can investigate it further! + */ + if (vol->major_ver < 3) { + if (deltaxcn == (LCN)-1) + ntfs_log_debug("lcn delta == -1\n"); + if (lcn == (LCN)-1) + ntfs_log_debug("lcn == -1\n"); + } #endif - /* Check lcn is not below -1. */ - if ( lcn < ( LCN ) - 1 ) - { - ntfs_log_debug( "Invalid LCN < -1 in mapping pairs " - "array.\n" ); - goto err_out; - } - /* Enter the current lcn into the runlist element. */ - rl[rlpos].lcn = lcn; - } - /* Get to the next runlist element. */ - rlpos++; - /* Increment the buffer position to the next mapping pair. */ - buf += ( *buf & 0xf ) + ( ( *buf >> 4 ) & 0xf ) + 1; - } - if ( buf >= attr_end ) - goto io_error; - /* - * If there is a highest_vcn specified, it must be equal to the final - * vcn in the runlist - 1, or something has gone badly wrong. - */ - deltaxcn = sle64_to_cpu( attr->highest_vcn ); - if ( deltaxcn && vcn - 1 != deltaxcn ) - { + /* Check lcn is not below -1. */ + if (lcn < (LCN)-1) { + ntfs_log_debug("Invalid LCN < -1 in mapping pairs " + "array.\n"); + goto err_out; + } + /* Enter the current lcn into the runlist element. */ + rl[rlpos].lcn = lcn; + } + /* Get to the next runlist element. */ + rlpos++; + /* Increment the buffer position to the next mapping pair. */ + buf += (*buf & 0xf) + ((*buf >> 4) & 0xf) + 1; + } + if (buf >= attr_end) + goto io_error; + /* + * If there is a highest_vcn specified, it must be equal to the final + * vcn in the runlist - 1, or something has gone badly wrong. + */ + deltaxcn = sle64_to_cpu(attr->highest_vcn); + if (deltaxcn && vcn - 1 != deltaxcn) { mpa_err: - ntfs_log_debug( "Corrupt mapping pairs array in non-resident " - "attribute.\n" ); - goto err_out; - } - /* Setup not mapped runlist element if this is the base extent. */ - if ( !attr->lowest_vcn ) - { - VCN max_cluster; + ntfs_log_debug("Corrupt mapping pairs array in non-resident " + "attribute.\n"); + goto err_out; + } + /* Setup not mapped runlist element if this is the base extent. */ + if (!attr->lowest_vcn) { + VCN max_cluster; - max_cluster = ( ( sle64_to_cpu( attr->allocated_size ) + - vol->cluster_size - 1 ) >> - vol->cluster_size_bits ) - 1; - /* - * A highest_vcn of zero means this is a single extent - * attribute so simply terminate the runlist with LCN_ENOENT). - */ - if ( deltaxcn ) - { - /* - * If there is a difference between the highest_vcn and - * the highest cluster, the runlist is either corrupt - * or, more likely, there are more extents following - * this one. - */ - if ( deltaxcn < max_cluster ) - { - ntfs_log_debug( "More extents to follow; deltaxcn = " - "0x%llx, max_cluster = 0x%llx\n", - ( long long )deltaxcn, - ( long long )max_cluster ); - rl[rlpos].vcn = vcn; - vcn += rl[rlpos].length = max_cluster - deltaxcn; - rl[rlpos].lcn = ( LCN )LCN_RL_NOT_MAPPED; - rlpos++; - } - else if ( deltaxcn > max_cluster ) - { - ntfs_log_debug( "Corrupt attribute. deltaxcn = " - "0x%llx, max_cluster = 0x%llx\n", - ( long long )deltaxcn, - ( long long )max_cluster ); - goto mpa_err; - } - } - rl[rlpos].lcn = ( LCN )LCN_ENOENT; - } - else /* Not the base extent. There may be more extents to follow. */ - rl[rlpos].lcn = ( LCN )LCN_RL_NOT_MAPPED; + max_cluster = ((sle64_to_cpu(attr->allocated_size) + + vol->cluster_size - 1) >> + vol->cluster_size_bits) - 1; + /* + * A highest_vcn of zero means this is a single extent + * attribute so simply terminate the runlist with LCN_ENOENT). + */ + if (deltaxcn) { + /* + * If there is a difference between the highest_vcn and + * the highest cluster, the runlist is either corrupt + * or, more likely, there are more extents following + * this one. + */ + if (deltaxcn < max_cluster) { + ntfs_log_debug("More extents to follow; deltaxcn = " + "0x%llx, max_cluster = 0x%llx\n", + (long long)deltaxcn, + (long long)max_cluster); + rl[rlpos].vcn = vcn; + vcn += rl[rlpos].length = max_cluster - deltaxcn; + rl[rlpos].lcn = (LCN)LCN_RL_NOT_MAPPED; + rlpos++; + } else if (deltaxcn > max_cluster) { + ntfs_log_debug("Corrupt attribute. deltaxcn = " + "0x%llx, max_cluster = 0x%llx\n", + (long long)deltaxcn, + (long long)max_cluster); + goto mpa_err; + } + } + rl[rlpos].lcn = (LCN)LCN_ENOENT; + } else /* Not the base extent. There may be more extents to follow. */ + rl[rlpos].lcn = (LCN)LCN_RL_NOT_MAPPED; - /* Setup terminating runlist element. */ - rl[rlpos].vcn = vcn; - rl[rlpos].length = ( s64 )0; - /* If no existing runlist was specified, we are done. */ - if ( !old_rl ) - { - ntfs_log_debug( "Mapping pairs array successfully decompressed:\n" ); - ntfs_debug_runlist_dump( rl ); - return rl; - } - /* Now combine the new and old runlists checking for overlaps. */ - old_rl = ntfs_runlists_merge( old_rl, rl ); - if ( old_rl ) - return old_rl; - err = errno; - free( rl ); - ntfs_log_debug( "Failed to merge runlists.\n" ); - errno = err; - return NULL; + /* Setup terminating runlist element. */ + rl[rlpos].vcn = vcn; + rl[rlpos].length = (s64)0; + /* If no existing runlist was specified, we are done. */ + if (!old_rl) { + ntfs_log_debug("Mapping pairs array successfully decompressed:\n"); + ntfs_debug_runlist_dump(rl); + return rl; + } + /* Now combine the new and old runlists checking for overlaps. */ + old_rl = ntfs_runlists_merge(old_rl, rl); + if (old_rl) + return old_rl; + err = errno; + free(rl); + ntfs_log_debug("Failed to merge runlists.\n"); + errno = err; + return NULL; io_error: - ntfs_log_debug( "Corrupt attribute.\n" ); + ntfs_log_debug("Corrupt attribute.\n"); err_out: - free( rl ); - errno = EIO; - return NULL; + free(rl); + errno = EIO; + return NULL; } -runlist_element *ntfs_mapping_pairs_decompress( const ntfs_volume *vol, - const ATTR_RECORD *attr, runlist_element *old_rl ) +runlist_element *ntfs_mapping_pairs_decompress(const ntfs_volume *vol, + const ATTR_RECORD *attr, runlist_element *old_rl) { - runlist_element *rle; - - ntfs_log_enter( "Entering\n" ); - rle = ntfs_mapping_pairs_decompress_i( vol, attr, old_rl ); - ntfs_log_leave( "\n" ); - return rle; + runlist_element *rle; + + ntfs_log_enter("Entering\n"); + rle = ntfs_mapping_pairs_decompress_i(vol, attr, old_rl); + ntfs_log_leave("\n"); + return rle; } /** * ntfs_rl_vcn_to_lcn - convert a vcn into a lcn given a runlist - * @rl: runlist to use for conversion - * @vcn: vcn to convert + * @rl: 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 @rl to map vcns to their @@ -1078,58 +1025,56 @@ runlist_element *ntfs_mapping_pairs_decompress( const ntfs_volume *vol, * * Since lcns must be >= 0, we use negative return values with special meaning: * - * Return value Meaning / Description + * Return value Meaning / Description * ================================================== - * -1 = LCN_HOLE Hole / not allocated on disk. - * -2 = LCN_RL_NOT_MAPPED This is part of the runlist which has not been - * inserted into the runlist yet. - * -3 = LCN_ENOENT There is no such vcn in the attribute. - * -4 = LCN_EINVAL Input parameter error. + * -1 = LCN_HOLE Hole / not allocated on disk. + * -2 = LCN_RL_NOT_MAPPED This is part of the runlist which has not been + * inserted into the runlist yet. + * -3 = LCN_ENOENT There is no such vcn in the attribute. + * -4 = LCN_EINVAL Input parameter error. */ -LCN ntfs_rl_vcn_to_lcn( const runlist_element *rl, const VCN vcn ) +LCN ntfs_rl_vcn_to_lcn(const runlist_element *rl, const VCN vcn) { - int i; + int i; - if ( vcn < ( VCN )0 ) - return ( LCN )LCN_EINVAL; - /* - * If rl is NULL, assume that we have found an unmapped runlist. The - * caller can then attempt to map it and fail appropriately if - * necessary. - */ - if ( !rl ) - return ( LCN )LCN_RL_NOT_MAPPED; + if (vcn < (VCN)0) + return (LCN)LCN_EINVAL; + /* + * If rl is NULL, assume that we have found an unmapped runlist. The + * caller can then attempt to map it and fail appropriately if + * necessary. + */ + if (!rl) + return (LCN)LCN_RL_NOT_MAPPED; - /* Catch out of lower bounds vcn. */ - if ( vcn < rl[0].vcn ) - return ( LCN )LCN_ENOENT; + /* Catch out of lower bounds vcn. */ + if (vcn < rl[0].vcn) + return (LCN)LCN_ENOENT; - for ( i = 0; rl[i].length; i++ ) - { - if ( vcn < rl[i+1].vcn ) - { - if ( rl[i].lcn >= ( LCN )0 ) - return rl[i].lcn + ( vcn - rl[i].vcn ); - return rl[i].lcn; - } - } - /* - * The terminator element is setup to the correct value, i.e. one of - * LCN_HOLE, LCN_RL_NOT_MAPPED, or LCN_ENOENT. - */ - if ( rl[i].lcn < ( LCN )0 ) - return rl[i].lcn; - /* Just in case... We could replace this with BUG() some day. */ - return ( LCN )LCN_ENOENT; + for (i = 0; rl[i].length; i++) { + if (vcn < rl[i+1].vcn) { + if (rl[i].lcn >= (LCN)0) + return rl[i].lcn + (vcn - rl[i].vcn); + return rl[i].lcn; + } + } + /* + * The terminator element is setup to the correct value, i.e. one of + * LCN_HOLE, LCN_RL_NOT_MAPPED, or LCN_ENOENT. + */ + if (rl[i].lcn < (LCN)0) + return rl[i].lcn; + /* Just in case... We could replace this with BUG() some day. */ + return (LCN)LCN_ENOENT; } /** * ntfs_rl_pread - gather read from disk - * @vol: ntfs volume to read from - * @rl: runlist specifying where to read the data from - * @pos: byte position within runlist @rl at which to begin the read - * @count: number of bytes to read - * @b: data buffer into which to read from disk + * @vol: ntfs volume to read from + * @rl: runlist specifying where to read the data from + * @pos: byte position within runlist @rl at which to begin the read + * @count: number of bytes to read + * @b: data buffer into which to read from disk * * This function will read @count bytes from the volume @vol to the data buffer * @b gathering the data as specified by the runlist @rl. The read begins at @@ -1147,84 +1092,80 @@ LCN ntfs_rl_vcn_to_lcn( const runlist_element *rl, const VCN vcn ) * NOTE: If we encounter EOF while reading we return EIO because we assume that * the run list must point to valid locations within the ntfs volume. */ -s64 ntfs_rl_pread( const ntfs_volume *vol, const runlist_element *rl, - const s64 pos, s64 count, void *b ) +s64 ntfs_rl_pread(const ntfs_volume *vol, const runlist_element *rl, + const s64 pos, s64 count, void *b) { - s64 bytes_read, to_read, ofs, total; - int err = EIO; + s64 bytes_read, to_read, ofs, total; + int err = EIO; - if ( !vol || !rl || pos < 0 || count < 0 ) - { - errno = EINVAL; - ntfs_log_perror( "Failed to read runlist [vol: %p rl: %p " - "pos: %lld count: %lld]", vol, rl, - ( long long )pos, ( long long )count ); - return -1; - } - if ( !count ) - return count; - /* Seek in @rl to the run containing @pos. */ - for ( ofs = 0; rl->length && ( ofs + ( rl->length << - vol->cluster_size_bits ) <= pos ); rl++ ) - ofs += ( rl->length << vol->cluster_size_bits ); - /* Offset in the run at which to begin reading. */ - ofs = pos - ofs; - for ( total = 0LL; count; rl++, ofs = 0 ) - { - if ( !rl->length ) - goto rl_err_out; - if ( rl->lcn < ( LCN )0 ) - { - if ( rl->lcn != ( LCN )LCN_HOLE ) - goto rl_err_out; - /* It is a hole. Just fill buffer @b with zeroes. */ - to_read = min( count, ( rl->length << - vol->cluster_size_bits ) - ofs ); - memset( b, 0, to_read ); - /* Update counters and proceed with next run. */ - total += to_read; - count -= to_read; - b = ( u8* )b + to_read; - continue; - } - /* It is a real lcn, read it from the volume. */ - to_read = min( count, ( rl->length << vol->cluster_size_bits ) - - ofs ); + if (!vol || !rl || pos < 0 || count < 0) { + errno = EINVAL; + ntfs_log_perror("Failed to read runlist [vol: %p rl: %p " + "pos: %lld count: %lld]", vol, rl, + (long long)pos, (long long)count); + return -1; + } + if (!count) + return count; + /* Seek in @rl to the run containing @pos. */ + for (ofs = 0; rl->length && (ofs + (rl->length << + vol->cluster_size_bits) <= pos); rl++) + ofs += (rl->length << vol->cluster_size_bits); + /* Offset in the run at which to begin reading. */ + ofs = pos - ofs; + for (total = 0LL; count; rl++, ofs = 0) { + if (!rl->length) + goto rl_err_out; + if (rl->lcn < (LCN)0) { + if (rl->lcn != (LCN)LCN_HOLE) + goto rl_err_out; + /* It is a hole. Just fill buffer @b with zeroes. */ + to_read = min(count, (rl->length << + vol->cluster_size_bits) - ofs); + memset(b, 0, to_read); + /* Update counters and proceed with next run. */ + total += to_read; + count -= to_read; + b = (u8*)b + to_read; + continue; + } + /* It is a real lcn, read it from the volume. */ + to_read = min(count, (rl->length << vol->cluster_size_bits) - + ofs); retry: - bytes_read = ntfs_pread( vol->dev, ( rl->lcn << - vol->cluster_size_bits ) + ofs, to_read, b ); - /* If everything ok, update progress counters and continue. */ - if ( bytes_read > 0 ) - { - total += bytes_read; - count -= bytes_read; - b = ( u8* )b + bytes_read; - continue; - } - /* If the syscall was interrupted, try again. */ - if ( bytes_read == ( s64 ) - 1 && errno == EINTR ) - goto retry; - if ( bytes_read == ( s64 ) - 1 ) - err = errno; - goto rl_err_out; - } - /* Finally, return the number of bytes read. */ - return total; + bytes_read = ntfs_pread(vol->dev, (rl->lcn << + vol->cluster_size_bits) + ofs, to_read, b); + /* If everything ok, update progress counters and continue. */ + if (bytes_read > 0) { + total += bytes_read; + count -= bytes_read; + b = (u8*)b + bytes_read; + continue; + } + /* If the syscall was interrupted, try again. */ + if (bytes_read == (s64)-1 && errno == EINTR) + goto retry; + if (bytes_read == (s64)-1) + err = errno; + goto rl_err_out; + } + /* Finally, return the number of bytes read. */ + return total; rl_err_out: - if ( total ) - return total; - errno = err; - return -1; + if (total) + return total; + errno = err; + return -1; } /** * ntfs_rl_pwrite - scatter write to disk - * @vol: ntfs volume to write to - * @rl: runlist entry specifying where to write the data to - * @ofs: offset in file for runlist element indicated in @rl - * @pos: byte position from runlist beginning at which to begin the write - * @count: number of bytes to write - * @b: data buffer to write to disk + * @vol: ntfs volume to write to + * @rl: runlist entry specifying where to write the data to + * @ofs: offset in file for runlist element indicated in @rl + * @pos: byte position from runlist beginning at which to begin the write + * @count: number of bytes to write + * @b: data buffer to write to disk * * This function will write @count bytes from data buffer @b to the volume @vol * scattering the data as specified by the runlist @rl. The write begins at @@ -1240,88 +1181,83 @@ rl_err_out: * appropriately to the return code of ntfs_pwrite(), or to to EINVAL in case * of invalid arguments. */ -s64 ntfs_rl_pwrite( const ntfs_volume *vol, const runlist_element *rl, - s64 ofs, const s64 pos, s64 count, void *b ) +s64 ntfs_rl_pwrite(const ntfs_volume *vol, const runlist_element *rl, + s64 ofs, const s64 pos, s64 count, void *b) { - s64 written, to_write, total = 0; - int err = EIO; + s64 written, to_write, total = 0; + int err = EIO; - if ( !vol || !rl || pos < 0 || count < 0 ) - { - errno = EINVAL; - ntfs_log_perror( "Failed to write runlist [vol: %p rl: %p " - "pos: %lld count: %lld]", vol, rl, - ( long long )pos, ( long long )count ); - goto errno_set; - } - if ( !count ) - goto out; - /* Seek in @rl to the run containing @pos. */ - while ( rl->length && ( ofs + ( rl->length << - vol->cluster_size_bits ) <= pos ) ) - { - ofs += ( rl->length << vol->cluster_size_bits ); - rl++; - } - /* Offset in the run at which to begin writing. */ - ofs = pos - ofs; - for ( total = 0LL; count; rl++, ofs = 0 ) - { - if ( !rl->length ) - goto rl_err_out; - if ( rl->lcn < ( LCN )0 ) - { + if (!vol || !rl || pos < 0 || count < 0) { + errno = EINVAL; + ntfs_log_perror("Failed to write runlist [vol: %p rl: %p " + "pos: %lld count: %lld]", vol, rl, + (long long)pos, (long long)count); + goto errno_set; + } + if (!count) + goto out; + /* Seek in @rl to the run containing @pos. */ + while (rl->length && (ofs + (rl->length << + vol->cluster_size_bits) <= pos)) { + ofs += (rl->length << vol->cluster_size_bits); + rl++; + } + /* Offset in the run at which to begin writing. */ + ofs = pos - ofs; + for (total = 0LL; count; rl++, ofs = 0) { + if (!rl->length) + goto rl_err_out; + if (rl->lcn < (LCN)0) { - if ( rl->lcn != ( LCN )LCN_HOLE ) - goto rl_err_out; - - to_write = min( count, ( rl->length << - vol->cluster_size_bits ) - ofs ); - - total += to_write; - count -= to_write; - b = ( u8* )b + to_write; - continue; - } - /* It is a real lcn, write it to the volume. */ - to_write = min( count, ( rl->length << vol->cluster_size_bits ) - - ofs ); + if (rl->lcn != (LCN)LCN_HOLE) + goto rl_err_out; + + to_write = min(count, (rl->length << + vol->cluster_size_bits) - ofs); + + total += to_write; + count -= to_write; + b = (u8*)b + to_write; + continue; + } + /* It is a real lcn, write it to the volume. */ + to_write = min(count, (rl->length << vol->cluster_size_bits) - + ofs); retry: - if ( !NVolReadOnly( vol ) ) - written = ntfs_pwrite( vol->dev, ( rl->lcn << - vol->cluster_size_bits ) + ofs, - to_write, b ); - else - written = to_write; - /* If everything ok, update progress counters and continue. */ - if ( written > 0 ) - { - total += written; - count -= written; - b = ( u8* )b + written; - continue; - } - /* If the syscall was interrupted, try again. */ - if ( written == ( s64 ) - 1 && errno == EINTR ) - goto retry; - if ( written == ( s64 ) - 1 ) - err = errno; - goto rl_err_out; - } + if (!NVolReadOnly(vol)) + written = ntfs_pwrite(vol->dev, (rl->lcn << + vol->cluster_size_bits) + ofs, + to_write, b); + else + written = to_write; + /* If everything ok, update progress counters and continue. */ + if (written > 0) { + total += written; + count -= written; + b = (u8*)b + written; + continue; + } + /* If the syscall was interrupted, try again. */ + if (written == (s64)-1 && errno == EINTR) + goto retry; + if (written == (s64)-1) + err = errno; + goto rl_err_out; + } out: - return total; + return total; rl_err_out: - if ( total ) - goto out; - errno = err; + if (total) + goto out; + errno = err; errno_set: - total = -1; - goto out; + total = -1; + goto out; } /** * ntfs_get_nr_significant_bytes - get number of bytes needed to store a number - * @n: number for which to get the number of bytes for + * @n: number for which to get the number of bytes for * * Return the number of bytes required to store @n unambiguously as * a signed number. @@ -1332,31 +1268,28 @@ errno_set: * * Return the number of bytes written. This function cannot fail. */ -int ntfs_get_nr_significant_bytes( const s64 n ) +int ntfs_get_nr_significant_bytes(const s64 n) { - u64 l; - int i; + u64 l; + int i; - l = ( n < 0 ? ~n : n ); - i = 1; - if ( l >= 128 ) - { - l >>= 7; - do - { - i++; - l >>= 8; - } - while ( l ); - } - return i; + l = (n < 0 ? ~n : n); + i = 1; + if (l >= 128) { + l >>= 7; + do { + i++; + l >>= 8; + } while (l); + } + return i; } /** * ntfs_get_size_for_mapping_pairs - get bytes needed for mapping pairs array - * @vol: ntfs volume (needed for the ntfs version) - * @rl: runlist for which to determine the size of the mapping pairs - * @start_vcn: vcn at which to start the mapping pairs array + * @vol: ntfs volume (needed for the ntfs version) + * @rl: runlist for which to determine the size of the mapping pairs + * @start_vcn: vcn at which to start the mapping pairs array * * Walk the runlist @rl and calculate the size in bytes of the mapping pairs * array corresponding to the runlist @rl, starting at vcn @start_vcn. This @@ -1367,115 +1300,107 @@ int ntfs_get_nr_significant_bytes( const s64 n ) * * Return the calculated size in bytes on success. On error, return -1 with * errno set to the error code. The following error codes are defined: - * EINVAL - Run list contains unmapped elements. Make sure to only pass - * fully mapped runlists to this function. - * - @start_vcn is invalid. - * EIO - The runlist is corrupt. + * EINVAL - Run list contains unmapped elements. Make sure to only pass + * fully mapped runlists to this function. + * - @start_vcn is invalid. + * EIO - The runlist is corrupt. */ -int ntfs_get_size_for_mapping_pairs( const ntfs_volume *vol, - const runlist_element *rl, const VCN start_vcn, int max_size ) +int ntfs_get_size_for_mapping_pairs(const ntfs_volume *vol, + const runlist_element *rl, const VCN start_vcn, int max_size) { - LCN prev_lcn; - int rls; + LCN prev_lcn; + int rls; - if ( start_vcn < 0 ) - { - ntfs_log_trace( "start_vcn %lld (should be >= 0)\n", - ( long long ) start_vcn ); - errno = EINVAL; - goto errno_set; - } - if ( !rl ) - { - if ( start_vcn ) - { - ntfs_log_trace( "rl NULL, start_vcn %lld (should be > 0)\n", - ( long long ) start_vcn ); - errno = EINVAL; - goto errno_set; - } - rls = 1; - goto out; - } - /* Skip to runlist element containing @start_vcn. */ - while ( rl->length && start_vcn >= rl[1].vcn ) - rl++; - if ( ( !rl->length && start_vcn > rl->vcn ) || start_vcn < rl->vcn ) - { - errno = EINVAL; - goto errno_set; - } - prev_lcn = 0; - /* Always need the terminating zero byte. */ - rls = 1; - /* Do the first partial run if present. */ - if ( start_vcn > rl->vcn ) - { - s64 delta; + if (start_vcn < 0) { + ntfs_log_trace("start_vcn %lld (should be >= 0)\n", + (long long) start_vcn); + errno = EINVAL; + goto errno_set; + } + if (!rl) { + if (start_vcn) { + ntfs_log_trace("rl NULL, start_vcn %lld (should be > 0)\n", + (long long) start_vcn); + errno = EINVAL; + goto errno_set; + } + rls = 1; + goto out; + } + /* Skip to runlist element containing @start_vcn. */ + while (rl->length && start_vcn >= rl[1].vcn) + rl++; + if ((!rl->length && start_vcn > rl->vcn) || start_vcn < rl->vcn) { + errno = EINVAL; + goto errno_set; + } + prev_lcn = 0; + /* Always need the terminating zero byte. */ + rls = 1; + /* Do the first partial run if present. */ + if (start_vcn > rl->vcn) { + s64 delta; - /* We know rl->length != 0 already. */ - if ( rl->length < 0 || rl->lcn < LCN_HOLE ) - goto err_out; - delta = start_vcn - rl->vcn; - /* Header byte + length. */ - rls += 1 + ntfs_get_nr_significant_bytes( rl->length - delta ); - /* - * If the logical cluster number (lcn) denotes a hole and we - * are on NTFS 3.0+, we don't store it at all, i.e. we need - * zero space. On earlier NTFS versions we just store the lcn. - * Note: this assumes that on NTFS 1.2-, holes are stored with - * an lcn of -1 and not a delta_lcn of -1 (unless both are -1). - */ - if ( rl->lcn >= 0 || vol->major_ver < 3 ) - { - prev_lcn = rl->lcn; - if ( rl->lcn >= 0 ) - prev_lcn += delta; - /* Change in lcn. */ - rls += ntfs_get_nr_significant_bytes( prev_lcn ); - } - /* Go to next runlist element. */ - rl++; - } - /* Do the full runs. */ - for ( ; rl->length && ( rls <= max_size ); rl++ ) - { - if ( rl->length < 0 || rl->lcn < LCN_HOLE ) - goto err_out; - /* Header byte + length. */ - rls += 1 + ntfs_get_nr_significant_bytes( rl->length ); - /* - * If the logical cluster number (lcn) denotes a hole and we - * are on NTFS 3.0+, we don't store it at all, i.e. we need - * zero space. On earlier NTFS versions we just store the lcn. - * Note: this assumes that on NTFS 1.2-, holes are stored with - * an lcn of -1 and not a delta_lcn of -1 (unless both are -1). - */ - if ( rl->lcn >= 0 || vol->major_ver < 3 ) - { - /* Change in lcn. */ - rls += ntfs_get_nr_significant_bytes( rl->lcn - - prev_lcn ); - prev_lcn = rl->lcn; - } - } -out: - return rls; + /* We know rl->length != 0 already. */ + if (rl->length < 0 || rl->lcn < LCN_HOLE) + goto err_out; + delta = start_vcn - rl->vcn; + /* Header byte + length. */ + rls += 1 + ntfs_get_nr_significant_bytes(rl->length - delta); + /* + * If the logical cluster number (lcn) denotes a hole and we + * are on NTFS 3.0+, we don't store it at all, i.e. we need + * zero space. On earlier NTFS versions we just store the lcn. + * Note: this assumes that on NTFS 1.2-, holes are stored with + * an lcn of -1 and not a delta_lcn of -1 (unless both are -1). + */ + if (rl->lcn >= 0 || vol->major_ver < 3) { + prev_lcn = rl->lcn; + if (rl->lcn >= 0) + prev_lcn += delta; + /* Change in lcn. */ + rls += ntfs_get_nr_significant_bytes(prev_lcn); + } + /* Go to next runlist element. */ + rl++; + } + /* Do the full runs. */ + for (; rl->length && (rls <= max_size); rl++) { + if (rl->length < 0 || rl->lcn < LCN_HOLE) + goto err_out; + /* Header byte + length. */ + rls += 1 + ntfs_get_nr_significant_bytes(rl->length); + /* + * If the logical cluster number (lcn) denotes a hole and we + * are on NTFS 3.0+, we don't store it at all, i.e. we need + * zero space. On earlier NTFS versions we just store the lcn. + * Note: this assumes that on NTFS 1.2-, holes are stored with + * an lcn of -1 and not a delta_lcn of -1 (unless both are -1). + */ + if (rl->lcn >= 0 || vol->major_ver < 3) { + /* Change in lcn. */ + rls += ntfs_get_nr_significant_bytes(rl->lcn - + prev_lcn); + prev_lcn = rl->lcn; + } + } +out: + return rls; err_out: - if ( rl->lcn == LCN_RL_NOT_MAPPED ) - errno = EINVAL; - else - errno = EIO; -errno_set: - rls = -1; - goto out; + if (rl->lcn == LCN_RL_NOT_MAPPED) + errno = EINVAL; + else + errno = EIO; +errno_set: + rls = -1; + goto out; } /** * ntfs_write_significant_bytes - write the significant bytes of a number - * @dst: destination buffer to write to - * @dst_max: pointer to last byte of destination buffer for bounds checking - * @n: number whose significant bytes to write + * @dst: destination buffer to write to + * @dst_max: pointer to last byte of destination buffer for bounds checking + * @n: number whose significant bytes to write * * Store in @dst, the minimum bytes of the number @n which are required to * identify @n unambiguously as a signed number, taking care not to exceed @@ -1489,52 +1414,47 @@ errno_set: * Return the number of bytes written on success. On error, i.e. the * destination buffer @dst is too small, return -1 with errno set ENOSPC. */ -int ntfs_write_significant_bytes( u8 *dst, const u8 *dst_max, const s64 n ) +int ntfs_write_significant_bytes(u8 *dst, const u8 *dst_max, const s64 n) { - s64 l = n; - int i; - s8 j; + s64 l = n; + int i; + s8 j; - i = 0; - do - { - if ( dst > dst_max ) - goto err_out; - *dst++ = l & 0xffLL; - l >>= 8; - i++; - } - while ( l != 0LL && l != -1LL ); - j = ( n >> 8 * ( i - 1 ) ) & 0xff; - /* If the sign bit is wrong, we need an extra byte. */ - if ( n < 0LL && j >= 0 ) - { - if ( dst > dst_max ) - goto err_out; - i++; - *dst = ( u8 ) - 1; - } - else if ( n > 0LL && j < 0 ) - { - if ( dst > dst_max ) - goto err_out; - i++; - *dst = 0; - } - return i; + i = 0; + do { + if (dst > dst_max) + goto err_out; + *dst++ = l & 0xffLL; + l >>= 8; + i++; + } while (l != 0LL && l != -1LL); + j = (n >> 8 * (i - 1)) & 0xff; + /* If the sign bit is wrong, we need an extra byte. */ + if (n < 0LL && j >= 0) { + if (dst > dst_max) + goto err_out; + i++; + *dst = (u8)-1; + } else if (n > 0LL && j < 0) { + if (dst > dst_max) + goto err_out; + i++; + *dst = 0; + } + return i; err_out: - errno = ENOSPC; - return -1; + errno = ENOSPC; + return -1; } /** * ntfs_mapping_pairs_build - build the mapping pairs array from a runlist - * @vol: ntfs volume (needed for the ntfs version) - * @dst: destination buffer to which to write the mapping pairs array - * @dst_len: size of destination buffer @dst in bytes - * @rl: runlist for which to build the mapping pairs array - * @start_vcn: vcn at which to start the mapping pairs array - * @stop_vcn: first vcn outside destination buffer on success or ENOSPC error + * @vol: ntfs volume (needed for the ntfs version) + * @dst: destination buffer to which to write the mapping pairs array + * @dst_len: size of destination buffer @dst in bytes + * @rl: runlist for which to build the mapping pairs array + * @start_vcn: vcn at which to start the mapping pairs array + * @stop_vcn: first vcn outside destination buffer on success or ENOSPC error * * Create the mapping pairs array from the runlist @rl, starting at vcn * @start_vcn and save the array in @dst. @dst_len is the size of @dst in @@ -1552,162 +1472,155 @@ err_out: * * Return 0 on success. On error, return -1 with errno set to the error code. * The following error codes are defined: - * EINVAL - Run list contains unmapped elements. Make sure to only pass - * fully mapped runlists to this function. - * - @start_vcn is invalid. - * EIO - The runlist is corrupt. - * ENOSPC - The destination buffer is too small. + * EINVAL - Run list contains unmapped elements. Make sure to only pass + * fully mapped runlists to this function. + * - @start_vcn is invalid. + * EIO - The runlist is corrupt. + * ENOSPC - The destination buffer is too small. */ -int ntfs_mapping_pairs_build( const ntfs_volume *vol, u8 *dst, - const int dst_len, const runlist_element *rl, - const VCN start_vcn, runlist_element const **stop_rl ) +int ntfs_mapping_pairs_build(const ntfs_volume *vol, u8 *dst, + const int dst_len, const runlist_element *rl, + const VCN start_vcn, runlist_element const **stop_rl) { - LCN prev_lcn; - u8 *dst_max, *dst_next; - s8 len_len, lcn_len; - int ret = 0; + LCN prev_lcn; + u8 *dst_max, *dst_next; + s8 len_len, lcn_len; + int ret = 0; - if ( start_vcn < 0 ) - goto val_err; - if ( !rl ) - { - if ( start_vcn ) - goto val_err; - if ( stop_rl ) - *stop_rl = rl; - if ( dst_len < 1 ) - goto nospc_err; - goto ok; - } - /* Skip to runlist element containing @start_vcn. */ - while ( rl->length && start_vcn >= rl[1].vcn ) - rl++; - if ( ( !rl->length && start_vcn > rl->vcn ) || start_vcn < rl->vcn ) - goto val_err; - /* - * @dst_max is used for bounds checking in - * ntfs_write_significant_bytes(). - */ - dst_max = dst + dst_len - 1; - prev_lcn = 0; - /* Do the first partial run if present. */ - if ( start_vcn > rl->vcn ) - { - s64 delta; + if (start_vcn < 0) + goto val_err; + if (!rl) { + if (start_vcn) + goto val_err; + if (stop_rl) + *stop_rl = rl; + if (dst_len < 1) + goto nospc_err; + goto ok; + } + /* Skip to runlist element containing @start_vcn. */ + while (rl->length && start_vcn >= rl[1].vcn) + rl++; + if ((!rl->length && start_vcn > rl->vcn) || start_vcn < rl->vcn) + goto val_err; + /* + * @dst_max is used for bounds checking in + * ntfs_write_significant_bytes(). + */ + dst_max = dst + dst_len - 1; + prev_lcn = 0; + /* Do the first partial run if present. */ + if (start_vcn > rl->vcn) { + s64 delta; - /* We know rl->length != 0 already. */ - if ( rl->length < 0 || rl->lcn < LCN_HOLE ) - goto err_out; - delta = start_vcn - rl->vcn; - /* Write length. */ - len_len = ntfs_write_significant_bytes( dst + 1, dst_max, - rl->length - delta ); - if ( len_len < 0 ) - goto size_err; - /* - * If the logical cluster number (lcn) denotes a hole and we - * are on NTFS 3.0+, we don't store it at all, i.e. we need - * zero space. On earlier NTFS versions we just write the lcn - * change. FIXME: Do we need to write the lcn change or just - * the lcn in that case? Not sure as I have never seen this - * case on NT4. - We assume that we just need to write the lcn - * change until someone tells us otherwise... (AIA) - */ - if ( rl->lcn >= 0 || vol->major_ver < 3 ) - { - prev_lcn = rl->lcn; - if ( rl->lcn >= 0 ) - prev_lcn += delta; - /* Write change in lcn. */ - lcn_len = ntfs_write_significant_bytes( dst + 1 + - len_len, dst_max, prev_lcn ); - if ( lcn_len < 0 ) - goto size_err; - } - else - lcn_len = 0; - dst_next = dst + len_len + lcn_len + 1; - if ( dst_next > dst_max ) - goto size_err; - /* Update header byte. */ - *dst = lcn_len << 4 | len_len; - /* Position at next mapping pairs array element. */ - dst = dst_next; - /* Go to next runlist element. */ - rl++; - } - /* Do the full runs. */ - for ( ; rl->length; rl++ ) - { - if ( rl->length < 0 || rl->lcn < LCN_HOLE ) - goto err_out; - /* Write length. */ - len_len = ntfs_write_significant_bytes( dst + 1, dst_max, - rl->length ); - if ( len_len < 0 ) - goto size_err; - /* - * If the logical cluster number (lcn) denotes a hole and we - * are on NTFS 3.0+, we don't store it at all, i.e. we need - * zero space. On earlier NTFS versions we just write the lcn - * change. FIXME: Do we need to write the lcn change or just - * the lcn in that case? Not sure as I have never seen this - * case on NT4. - We assume that we just need to write the lcn - * change until someone tells us otherwise... (AIA) - */ - if ( rl->lcn >= 0 || vol->major_ver < 3 ) - { - /* Write change in lcn. */ - lcn_len = ntfs_write_significant_bytes( dst + 1 + - len_len, dst_max, rl->lcn - prev_lcn ); - if ( lcn_len < 0 ) - goto size_err; - prev_lcn = rl->lcn; - } - else - lcn_len = 0; - dst_next = dst + len_len + lcn_len + 1; - if ( dst_next > dst_max ) - goto size_err; - /* Update header byte. */ - *dst = lcn_len << 4 | len_len; - /* Position at next mapping pairs array element. */ - dst += 1 + len_len + lcn_len; - } - /* Set stop vcn. */ - if ( stop_rl ) - *stop_rl = rl; -ok: - /* Add terminator byte. */ - *dst = 0; + /* We know rl->length != 0 already. */ + if (rl->length < 0 || rl->lcn < LCN_HOLE) + goto err_out; + delta = start_vcn - rl->vcn; + /* Write length. */ + len_len = ntfs_write_significant_bytes(dst + 1, dst_max, + rl->length - delta); + if (len_len < 0) + goto size_err; + /* + * If the logical cluster number (lcn) denotes a hole and we + * are on NTFS 3.0+, we don't store it at all, i.e. we need + * zero space. On earlier NTFS versions we just write the lcn + * change. FIXME: Do we need to write the lcn change or just + * the lcn in that case? Not sure as I have never seen this + * case on NT4. - We assume that we just need to write the lcn + * change until someone tells us otherwise... (AIA) + */ + if (rl->lcn >= 0 || vol->major_ver < 3) { + prev_lcn = rl->lcn; + if (rl->lcn >= 0) + prev_lcn += delta; + /* Write change in lcn. */ + lcn_len = ntfs_write_significant_bytes(dst + 1 + + len_len, dst_max, prev_lcn); + if (lcn_len < 0) + goto size_err; + } else + lcn_len = 0; + dst_next = dst + len_len + lcn_len + 1; + if (dst_next > dst_max) + goto size_err; + /* Update header byte. */ + *dst = lcn_len << 4 | len_len; + /* Position at next mapping pairs array element. */ + dst = dst_next; + /* Go to next runlist element. */ + rl++; + } + /* Do the full runs. */ + for (; rl->length; rl++) { + if (rl->length < 0 || rl->lcn < LCN_HOLE) + goto err_out; + /* Write length. */ + len_len = ntfs_write_significant_bytes(dst + 1, dst_max, + rl->length); + if (len_len < 0) + goto size_err; + /* + * If the logical cluster number (lcn) denotes a hole and we + * are on NTFS 3.0+, we don't store it at all, i.e. we need + * zero space. On earlier NTFS versions we just write the lcn + * change. FIXME: Do we need to write the lcn change or just + * the lcn in that case? Not sure as I have never seen this + * case on NT4. - We assume that we just need to write the lcn + * change until someone tells us otherwise... (AIA) + */ + if (rl->lcn >= 0 || vol->major_ver < 3) { + /* Write change in lcn. */ + lcn_len = ntfs_write_significant_bytes(dst + 1 + + len_len, dst_max, rl->lcn - prev_lcn); + if (lcn_len < 0) + goto size_err; + prev_lcn = rl->lcn; + } else + lcn_len = 0; + dst_next = dst + len_len + lcn_len + 1; + if (dst_next > dst_max) + goto size_err; + /* Update header byte. */ + *dst = lcn_len << 4 | len_len; + /* Position at next mapping pairs array element. */ + dst += 1 + len_len + lcn_len; + } + /* Set stop vcn. */ + if (stop_rl) + *stop_rl = rl; +ok: + /* Add terminator byte. */ + *dst = 0; out: - return ret; + return ret; size_err: - /* Set stop vcn. */ - if ( stop_rl ) - *stop_rl = rl; - /* Add terminator byte. */ - *dst = 0; + /* Set stop vcn. */ + if (stop_rl) + *stop_rl = rl; + /* Add terminator byte. */ + *dst = 0; nospc_err: - errno = ENOSPC; - goto errno_set; + errno = ENOSPC; + goto errno_set; val_err: - errno = EINVAL; - goto errno_set; + errno = EINVAL; + goto errno_set; err_out: - if ( rl->lcn == LCN_RL_NOT_MAPPED ) - errno = EINVAL; - else - errno = EIO; + if (rl->lcn == LCN_RL_NOT_MAPPED) + errno = EINVAL; + else + errno = EIO; errno_set: - ret = -1; - goto out; + ret = -1; + goto out; } /** * ntfs_rl_truncate - truncate a runlist starting at a specified vcn - * @arl: address of runlist to truncate - * @start_vcn: first vcn which should be cut off + * @arl: address of runlist to truncate + * @start_vcn: first vcn which should be cut off * * Truncate the runlist *@arl starting at vcn @start_vcn as well as the memory * buffer holding the runlist. @@ -1717,144 +1630,131 @@ errno_set: * NOTE: @arl is the address of the runlist. We need the address so we can * modify the pointer to the runlist with the new, reallocated memory buffer. */ -int ntfs_rl_truncate( runlist **arl, const VCN start_vcn ) +int ntfs_rl_truncate(runlist **arl, const VCN start_vcn) { - runlist *rl; - BOOL is_end = FALSE; + runlist *rl; + BOOL is_end = FALSE; - if ( !arl || !*arl ) - { - errno = EINVAL; - if ( !arl ) - ntfs_log_perror( "rl_truncate error: arl: %p", arl ); - else - ntfs_log_perror( "rl_truncate error:" - " arl: %p *arl: %p", arl, *arl ); - return -1; - } - - rl = *arl; - - if ( start_vcn < rl->vcn ) - { - errno = EINVAL; - ntfs_log_perror( "Start_vcn lies outside front of runlist" ); - return -1; - } - - /* Find the starting vcn in the run list. */ - while ( rl->length ) - { - if ( start_vcn < rl[1].vcn ) - break; - rl++; - } - - if ( !rl->length ) - { - errno = EIO; - ntfs_log_trace( "Truncating already truncated runlist?\n" ); - return -1; - } - - /* Truncate the run. */ - rl->length = start_vcn - rl->vcn; - - /* - * If a run was partially truncated, make the following runlist - * element a terminator instead of the truncated runlist - * element itself. - */ - if ( rl->length ) - { - ++rl; - if ( !rl->length ) - is_end = TRUE; - rl->vcn = start_vcn; - rl->length = 0; - } - rl->lcn = ( LCN )LCN_ENOENT; - /** - * Reallocate memory if necessary. - * FIXME: Below code is broken, because runlist allocations must be - * a multiply of 4096. The code caused crashes and corruptions. - */ - /* - if (!is_end) { - size_t new_size = (rl - *arl + 1) * sizeof(runlist_element); - rl = realloc(*arl, new_size); - if (rl) - *arl = rl; - } - */ - return 0; + if (!arl || !*arl) { + errno = EINVAL; + if (!arl) + ntfs_log_perror("rl_truncate error: arl: %p", arl); + else + ntfs_log_perror("rl_truncate error:" + " arl: %p *arl: %p", arl, *arl); + return -1; + } + + rl = *arl; + + if (start_vcn < rl->vcn) { + errno = EINVAL; + ntfs_log_perror("Start_vcn lies outside front of runlist"); + return -1; + } + + /* Find the starting vcn in the run list. */ + while (rl->length) { + if (start_vcn < rl[1].vcn) + break; + rl++; + } + + if (!rl->length) { + errno = EIO; + ntfs_log_trace("Truncating already truncated runlist?\n"); + return -1; + } + + /* Truncate the run. */ + rl->length = start_vcn - rl->vcn; + + /* + * If a run was partially truncated, make the following runlist + * element a terminator instead of the truncated runlist + * element itself. + */ + if (rl->length) { + ++rl; + if (!rl->length) + is_end = TRUE; + rl->vcn = start_vcn; + rl->length = 0; + } + rl->lcn = (LCN)LCN_ENOENT; + /** + * Reallocate memory if necessary. + * FIXME: Below code is broken, because runlist allocations must be + * a multiply of 4096. The code caused crashes and corruptions. + */ +/* + if (!is_end) { + size_t new_size = (rl - *arl + 1) * sizeof(runlist_element); + rl = realloc(*arl, new_size); + if (rl) + *arl = rl; + } +*/ + return 0; } /** * ntfs_rl_sparse - check whether runlist have sparse regions or not. - * @rl: runlist to check + * @rl: runlist to check * * Return 1 if have, 0 if not, -1 on error with errno set to the error code. */ -int ntfs_rl_sparse( runlist *rl ) +int ntfs_rl_sparse(runlist *rl) { - runlist *rlc; + runlist *rlc; - if ( !rl ) - { - errno = EINVAL; - ntfs_log_perror( "%s: ", __FUNCTION__ ); - return -1; - } + if (!rl) { + errno = EINVAL; + ntfs_log_perror("%s: ", __FUNCTION__); + return -1; + } - for ( rlc = rl; rlc->length; rlc++ ) - if ( rlc->lcn < 0 ) - { - if ( rlc->lcn != LCN_HOLE ) - { - errno = EINVAL; - ntfs_log_perror( "%s: bad runlist", __FUNCTION__ ); - return -1; - } - return 1; - } - return 0; + for (rlc = rl; rlc->length; rlc++) + if (rlc->lcn < 0) { + if (rlc->lcn != LCN_HOLE) { + errno = EINVAL; + ntfs_log_perror("%s: bad runlist", __FUNCTION__); + return -1; + } + return 1; + } + return 0; } /** * ntfs_rl_get_compressed_size - calculate length of non sparse regions - * @vol: ntfs volume (need for cluster size) - * @rl: runlist to calculate for + * @vol: ntfs volume (need for cluster size) + * @rl: runlist to calculate for * * Return compressed size or -1 on error with errno set to the error code. */ -s64 ntfs_rl_get_compressed_size( ntfs_volume *vol, runlist *rl ) +s64 ntfs_rl_get_compressed_size(ntfs_volume *vol, runlist *rl) { - runlist *rlc; - s64 ret = 0; + runlist *rlc; + s64 ret = 0; - if ( !rl ) - { - errno = EINVAL; - ntfs_log_perror( "%s: ", __FUNCTION__ ); - return -1; - } + if (!rl) { + errno = EINVAL; + ntfs_log_perror("%s: ", __FUNCTION__); + return -1; + } - for ( rlc = rl; rlc->length; rlc++ ) - { - if ( rlc->lcn < 0 ) - { - if ( rlc->lcn != LCN_HOLE ) - { - errno = EINVAL; - ntfs_log_perror( "%s: bad runlist", __FUNCTION__ ); - return -1; - } - } - else - ret += rlc->length; - } - return ret << vol->cluster_size_bits; + for (rlc = rl; rlc->length; rlc++) { + if (rlc->lcn < 0) { + if (rlc->lcn != LCN_HOLE) { + errno = EINVAL; + ntfs_log_perror("%s: bad runlist", __FUNCTION__); + return -1; + } + } else + ret += rlc->length; + } + return ret << vol->cluster_size_bits; } @@ -1862,10 +1762,10 @@ s64 ntfs_rl_get_compressed_size( ntfs_volume *vol, runlist *rl ) /** * test_rl_helper */ -#define MKRL(R,V,L,S) \ - (R)->vcn = V; \ - (R)->lcn = L; \ - (R)->length = S; +#define MKRL(R,V,L,S) \ + (R)->vcn = V; \ + (R)->lcn = L; \ + (R)->length = S; /* } */ @@ -1877,53 +1777,48 @@ s64 ntfs_rl_get_compressed_size( ntfs_volume *vol, runlist *rl ) * * Returns: */ -static void test_rl_dump_runlist( const runlist_element *rl ) +static void test_rl_dump_runlist(const runlist_element *rl) { - int abbr = 0; /* abbreviate long lists */ - int len = 0; - int i; - const char *lcn_str[5] = { "HOLE", "NOTMAP", "ENOENT", "XXXX" }; + int abbr = 0; /* abbreviate long lists */ + int len = 0; + int i; + const char *lcn_str[5] = { "HOLE", "NOTMAP", "ENOENT", "XXXX" }; - if ( !rl ) - { - printf( " Run list not present.\n" ); - return; - } + if (!rl) { + printf(" Run list not present.\n"); + return; + } - if ( abbr ) - for ( len = 0; rl[len].length; len++ ) ; + if (abbr) + for (len = 0; rl[len].length; len++) ; - printf( " VCN LCN len\n" ); - for ( i = 0; ; i++, rl++ ) - { - LCN lcn = rl->lcn; + printf(" VCN LCN len\n"); + for (i = 0; ; i++, rl++) { + LCN lcn = rl->lcn; - if ( ( abbr ) && ( len > 20 ) ) - { - if ( i == 4 ) - printf( " ...\n" ); - if ( ( i > 3 ) && ( i < ( len - 3 ) ) ) - continue; - } + if ((abbr) && (len > 20)) { + if (i == 4) + printf(" ...\n"); + if ((i > 3) && (i < (len - 3))) + continue; + } - if ( lcn < ( LCN )0 ) - { - int ind = -lcn - 1; + if (lcn < (LCN)0) { + int ind = -lcn - 1; - if ( ind > -LCN_ENOENT - 1 ) - ind = 3; - printf( "%8lld %8s %8lld\n", - rl->vcn, lcn_str[ind], rl->length ); - } - else - printf( "%8lld %8lld %8lld\n", - rl->vcn, rl->lcn, rl->length ); - if ( !rl->length ) - break; - } - if ( ( abbr ) && ( len > 20 ) ) - printf( " (%d entries)\n", len + 1 ); - printf( "\n" ); + if (ind > -LCN_ENOENT - 1) + ind = 3; + printf("%8lld %8s %8lld\n", + rl->vcn, lcn_str[ind], rl->length); + } else + printf("%8lld %8lld %8lld\n", + rl->vcn, rl->lcn, rl->length); + if (!rl->length) + break; + } + if ((abbr) && (len > 20)) + printf(" (%d entries)\n", len+1); + printf("\n"); } /** @@ -1935,21 +1830,21 @@ static void test_rl_dump_runlist( const runlist_element *rl ) * * Returns: */ -static runlist_element * test_rl_runlists_merge( runlist_element *drl, runlist_element *srl ) +static runlist_element * test_rl_runlists_merge(runlist_element *drl, runlist_element *srl) { - runlist_element *res = NULL; + runlist_element *res = NULL; - printf( "dst:\n" ); - test_rl_dump_runlist( drl ); - printf( "src:\n" ); - test_rl_dump_runlist( srl ); + printf("dst:\n"); + test_rl_dump_runlist(drl); + printf("src:\n"); + test_rl_dump_runlist(srl); - res = ntfs_runlists_merge( drl, srl ); + res = ntfs_runlists_merge(drl, srl); - printf( "res:\n" ); - test_rl_dump_runlist( res ); + printf("res:\n"); + test_rl_dump_runlist(res); - return res; + return res; } /** @@ -1962,25 +1857,23 @@ static runlist_element * test_rl_runlists_merge( runlist_element *drl, runlist_e * * Returns: */ -static int test_rl_read_buffer( const char *file, u8 *buf, int bufsize ) +static int test_rl_read_buffer(const char *file, u8 *buf, int bufsize) { - FILE *fptr; + FILE *fptr; - fptr = fopen( file, "r" ); - if ( !fptr ) - { - printf( "open %s\n", file ); - return 0; - } + fptr = fopen(file, "r"); + if (!fptr) { + printf("open %s\n", file); + return 0; + } - if ( fread( buf, bufsize, 1, fptr ) == 99 ) - { - printf( "read %s\n", file ); - return 0; - } + if (fread(buf, bufsize, 1, fptr) == 99) { + printf("read %s\n", file); + return 0; + } - fclose( fptr ); - return 1; + fclose(fptr); + return 1; } /** @@ -1994,34 +1887,31 @@ static int test_rl_read_buffer( const char *file, u8 *buf, int bufsize ) * * Returns: */ -static runlist_element * test_rl_pure_src( BOOL contig, BOOL multi, int vcn, int len ) +static runlist_element * test_rl_pure_src(BOOL contig, BOOL multi, int vcn, int len) { - runlist_element *result; - int fudge; + runlist_element *result; + int fudge; - if ( contig ) - fudge = 0; - else - fudge = 999; + if (contig) + fudge = 0; + else + fudge = 999; - result = ntfs_malloc( 4096 ); - if ( !result ) - return NULL; - - if ( multi ) - { - MKRL( result + 0, vcn + ( 0*len / 4 ), fudge + vcn + 1000 + ( 0*len / 4 ), len / 4 ) - MKRL( result + 1, vcn + ( 1*len / 4 ), fudge + vcn + 1000 + ( 1*len / 4 ), len / 4 ) - MKRL( result + 2, vcn + ( 2*len / 4 ), fudge + vcn + 1000 + ( 2*len / 4 ), len / 4 ) - MKRL( result + 3, vcn + ( 3*len / 4 ), fudge + vcn + 1000 + ( 3*len / 4 ), len / 4 ) - MKRL( result + 4, vcn + ( 4*len / 4 ), LCN_RL_NOT_MAPPED, 0 ) - } - else - { - MKRL( result + 0, vcn, fudge + vcn + 1000, len ) - MKRL( result + 1, vcn + len, LCN_RL_NOT_MAPPED, 0 ) - } - return result; + result = ntfs_malloc(4096); + if (!result) + return NULL; + + if (multi) { + MKRL(result+0, vcn + (0*len/4), fudge + vcn + 1000 + (0*len/4), len / 4) + MKRL(result+1, vcn + (1*len/4), fudge + vcn + 1000 + (1*len/4), len / 4) + MKRL(result+2, vcn + (2*len/4), fudge + vcn + 1000 + (2*len/4), len / 4) + MKRL(result+3, vcn + (3*len/4), fudge + vcn + 1000 + (3*len/4), len / 4) + MKRL(result+4, vcn + (4*len/4), LCN_RL_NOT_MAPPED, 0) + } else { + MKRL(result+0, vcn, fudge + vcn + 1000, len) + MKRL(result+1, vcn + len, LCN_RL_NOT_MAPPED, 0) + } + return result; } /** @@ -2038,26 +1928,25 @@ static runlist_element * test_rl_pure_src( BOOL contig, BOOL multi, int vcn, int * * Returns: */ -static void test_rl_pure_test( int test, BOOL contig, BOOL multi, int vcn, int len, runlist_element *file, int size ) +static void test_rl_pure_test(int test, BOOL contig, BOOL multi, int vcn, int len, runlist_element *file, int size) { - runlist_element *src; - runlist_element *dst; - runlist_element *res; + runlist_element *src; + runlist_element *dst; + runlist_element *res; - src = test_rl_pure_src( contig, multi, vcn, len ); - dst = ntfs_malloc( 4096 ); - if ( !src || !dst ) - { - printf( "Test %2d ---------- FAILED! (no free memory?)\n", test ); - return; - } + src = test_rl_pure_src(contig, multi, vcn, len); + dst = ntfs_malloc(4096); + if (!src || !dst) { + printf("Test %2d ---------- FAILED! (no free memory?)\n", test); + return; + } - memcpy( dst, file, size ); + memcpy(dst, file, size); - printf( "Test %2d ----------\n", test ); - res = test_rl_runlists_merge( dst, src ); + printf("Test %2d ----------\n", test); + res = test_rl_runlists_merge(dst, src); - free( res ); + free(res); } /** @@ -2069,103 +1958,95 @@ static void test_rl_pure_test( int test, BOOL contig, BOOL multi, int vcn, int l * * Returns: */ -static void test_rl_pure( char *contig, char *multi ) +static void test_rl_pure(char *contig, char *multi) { - /* VCN, LCN, len */ - static runlist_element file1[] = - { - { 0, -1, 100 }, /* HOLE */ - { 100, 1100, 100 }, /* DATA */ - { 200, -1, 100 }, /* HOLE */ - { 300, 1300, 100 }, /* DATA */ - { 400, -1, 100 }, /* HOLE */ - { 500, -3, 0 } /* NOENT */ - }; - static runlist_element file2[] = - { - { 0, 1000, 100 }, /* DATA */ - { 100, -1, 100 }, /* HOLE */ - { 200, -3, 0 } /* NOENT */ - }; - static runlist_element file3[] = - { - { 0, 1000, 100 }, /* DATA */ - { 100, -3, 0 } /* NOENT */ - }; - static runlist_element file4[] = - { - { 0, -3, 0 } /* NOENT */ - }; - static runlist_element file5[] = - { - { 0, -2, 100 }, /* NOTMAP */ - { 100, 1100, 100 }, /* DATA */ - { 200, -2, 100 }, /* NOTMAP */ - { 300, 1300, 100 }, /* DATA */ - { 400, -2, 100 }, /* NOTMAP */ - { 500, -3, 0 } /* NOENT */ - }; - static runlist_element file6[] = - { - { 0, 1000, 100 }, /* DATA */ - { 100, -2, 100 }, /* NOTMAP */ - { 200, -3, 0 } /* NOENT */ - }; - BOOL c, m; + /* VCN, LCN, len */ + static runlist_element file1[] = { + { 0, -1, 100 }, /* HOLE */ + { 100, 1100, 100 }, /* DATA */ + { 200, -1, 100 }, /* HOLE */ + { 300, 1300, 100 }, /* DATA */ + { 400, -1, 100 }, /* HOLE */ + { 500, -3, 0 } /* NOENT */ + }; + static runlist_element file2[] = { + { 0, 1000, 100 }, /* DATA */ + { 100, -1, 100 }, /* HOLE */ + { 200, -3, 0 } /* NOENT */ + }; + static runlist_element file3[] = { + { 0, 1000, 100 }, /* DATA */ + { 100, -3, 0 } /* NOENT */ + }; + static runlist_element file4[] = { + { 0, -3, 0 } /* NOENT */ + }; + static runlist_element file5[] = { + { 0, -2, 100 }, /* NOTMAP */ + { 100, 1100, 100 }, /* DATA */ + { 200, -2, 100 }, /* NOTMAP */ + { 300, 1300, 100 }, /* DATA */ + { 400, -2, 100 }, /* NOTMAP */ + { 500, -3, 0 } /* NOENT */ + }; + static runlist_element file6[] = { + { 0, 1000, 100 }, /* DATA */ + { 100, -2, 100 }, /* NOTMAP */ + { 200, -3, 0 } /* NOENT */ + }; + BOOL c, m; - if ( strcmp( contig, "contig" ) == 0 ) - c = TRUE; - else if ( strcmp( contig, "noncontig" ) == 0 ) - c = FALSE; - else - { - printf( "rl pure [contig|noncontig] [single|multi]\n" ); - return; - } - if ( strcmp( multi, "multi" ) == 0 ) - m = TRUE; - else if ( strcmp( multi, "single" ) == 0 ) - m = FALSE; - else - { - printf( "rl pure [contig|noncontig] [single|multi]\n" ); - return; - } + if (strcmp(contig, "contig") == 0) + c = TRUE; + else if (strcmp(contig, "noncontig") == 0) + c = FALSE; + else { + printf("rl pure [contig|noncontig] [single|multi]\n"); + return; + } + if (strcmp(multi, "multi") == 0) + m = TRUE; + else if (strcmp(multi, "single") == 0) + m = FALSE; + else { + printf("rl pure [contig|noncontig] [single|multi]\n"); + return; + } - test_rl_pure_test( 1, c, m, 0, 40, file1, sizeof( file1 ) ); - test_rl_pure_test( 2, c, m, 40, 40, file1, sizeof( file1 ) ); - test_rl_pure_test( 3, c, m, 60, 40, file1, sizeof( file1 ) ); - test_rl_pure_test( 4, c, m, 0, 100, file1, sizeof( file1 ) ); - test_rl_pure_test( 5, c, m, 200, 40, file1, sizeof( file1 ) ); - test_rl_pure_test( 6, c, m, 240, 40, file1, sizeof( file1 ) ); - test_rl_pure_test( 7, c, m, 260, 40, file1, sizeof( file1 ) ); - test_rl_pure_test( 8, c, m, 200, 100, file1, sizeof( file1 ) ); - test_rl_pure_test( 9, c, m, 400, 40, file1, sizeof( file1 ) ); - test_rl_pure_test( 10, c, m, 440, 40, file1, sizeof( file1 ) ); - test_rl_pure_test( 11, c, m, 460, 40, file1, sizeof( file1 ) ); - test_rl_pure_test( 12, c, m, 400, 100, file1, sizeof( file1 ) ); - test_rl_pure_test( 13, c, m, 160, 100, file2, sizeof( file2 ) ); - test_rl_pure_test( 14, c, m, 100, 140, file2, sizeof( file2 ) ); - test_rl_pure_test( 15, c, m, 200, 40, file2, sizeof( file2 ) ); - test_rl_pure_test( 16, c, m, 240, 40, file2, sizeof( file2 ) ); - test_rl_pure_test( 17, c, m, 100, 40, file3, sizeof( file3 ) ); - test_rl_pure_test( 18, c, m, 140, 40, file3, sizeof( file3 ) ); - test_rl_pure_test( 19, c, m, 0, 40, file4, sizeof( file4 ) ); - test_rl_pure_test( 20, c, m, 40, 40, file4, sizeof( file4 ) ); - test_rl_pure_test( 21, c, m, 0, 40, file5, sizeof( file5 ) ); - test_rl_pure_test( 22, c, m, 40, 40, file5, sizeof( file5 ) ); - test_rl_pure_test( 23, c, m, 60, 40, file5, sizeof( file5 ) ); - test_rl_pure_test( 24, c, m, 0, 100, file5, sizeof( file5 ) ); - test_rl_pure_test( 25, c, m, 200, 40, file5, sizeof( file5 ) ); - test_rl_pure_test( 26, c, m, 240, 40, file5, sizeof( file5 ) ); - test_rl_pure_test( 27, c, m, 260, 40, file5, sizeof( file5 ) ); - test_rl_pure_test( 28, c, m, 200, 100, file5, sizeof( file5 ) ); - test_rl_pure_test( 29, c, m, 400, 40, file5, sizeof( file5 ) ); - test_rl_pure_test( 30, c, m, 440, 40, file5, sizeof( file5 ) ); - test_rl_pure_test( 31, c, m, 460, 40, file5, sizeof( file5 ) ); - test_rl_pure_test( 32, c, m, 400, 100, file5, sizeof( file5 ) ); - test_rl_pure_test( 33, c, m, 160, 100, file6, sizeof( file6 ) ); - test_rl_pure_test( 34, c, m, 100, 140, file6, sizeof( file6 ) ); + test_rl_pure_test(1, c, m, 0, 40, file1, sizeof(file1)); + test_rl_pure_test(2, c, m, 40, 40, file1, sizeof(file1)); + test_rl_pure_test(3, c, m, 60, 40, file1, sizeof(file1)); + test_rl_pure_test(4, c, m, 0, 100, file1, sizeof(file1)); + test_rl_pure_test(5, c, m, 200, 40, file1, sizeof(file1)); + test_rl_pure_test(6, c, m, 240, 40, file1, sizeof(file1)); + test_rl_pure_test(7, c, m, 260, 40, file1, sizeof(file1)); + test_rl_pure_test(8, c, m, 200, 100, file1, sizeof(file1)); + test_rl_pure_test(9, c, m, 400, 40, file1, sizeof(file1)); + test_rl_pure_test(10, c, m, 440, 40, file1, sizeof(file1)); + test_rl_pure_test(11, c, m, 460, 40, file1, sizeof(file1)); + test_rl_pure_test(12, c, m, 400, 100, file1, sizeof(file1)); + test_rl_pure_test(13, c, m, 160, 100, file2, sizeof(file2)); + test_rl_pure_test(14, c, m, 100, 140, file2, sizeof(file2)); + test_rl_pure_test(15, c, m, 200, 40, file2, sizeof(file2)); + test_rl_pure_test(16, c, m, 240, 40, file2, sizeof(file2)); + test_rl_pure_test(17, c, m, 100, 40, file3, sizeof(file3)); + test_rl_pure_test(18, c, m, 140, 40, file3, sizeof(file3)); + test_rl_pure_test(19, c, m, 0, 40, file4, sizeof(file4)); + test_rl_pure_test(20, c, m, 40, 40, file4, sizeof(file4)); + test_rl_pure_test(21, c, m, 0, 40, file5, sizeof(file5)); + test_rl_pure_test(22, c, m, 40, 40, file5, sizeof(file5)); + test_rl_pure_test(23, c, m, 60, 40, file5, sizeof(file5)); + test_rl_pure_test(24, c, m, 0, 100, file5, sizeof(file5)); + test_rl_pure_test(25, c, m, 200, 40, file5, sizeof(file5)); + test_rl_pure_test(26, c, m, 240, 40, file5, sizeof(file5)); + test_rl_pure_test(27, c, m, 260, 40, file5, sizeof(file5)); + test_rl_pure_test(28, c, m, 200, 100, file5, sizeof(file5)); + test_rl_pure_test(29, c, m, 400, 40, file5, sizeof(file5)); + test_rl_pure_test(30, c, m, 440, 40, file5, sizeof(file5)); + test_rl_pure_test(31, c, m, 460, 40, file5, sizeof(file5)); + test_rl_pure_test(32, c, m, 400, 100, file5, sizeof(file5)); + test_rl_pure_test(33, c, m, 160, 100, file6, sizeof(file6)); + test_rl_pure_test(34, c, m, 100, 140, file6, sizeof(file6)); } /** @@ -2175,23 +2056,23 @@ static void test_rl_pure( char *contig, char *multi ) * * Returns: */ -static void test_rl_zero( void ) +static void test_rl_zero(void) { - runlist_element *jim = NULL; - runlist_element *bob = NULL; + runlist_element *jim = NULL; + runlist_element *bob = NULL; - bob = calloc( 3, sizeof( runlist_element ) ); - if ( !bob ) - return; + bob = calloc(3, sizeof(runlist_element)); + if (!bob) + return; - MKRL( bob + 0, 10, 99, 5 ) - MKRL( bob + 1, 15, LCN_RL_NOT_MAPPED, 0 ) + MKRL(bob+0, 10, 99, 5) + MKRL(bob+1, 15, LCN_RL_NOT_MAPPED, 0) - jim = test_rl_runlists_merge( jim, bob ); - if ( !jim ) - return; + jim = test_rl_runlists_merge(jim, bob); + if (!jim) + return; - free( jim ); + free(jim); } /** @@ -2205,29 +2086,29 @@ static void test_rl_zero( void ) * * Returns: */ -static void test_rl_frag_combine( ntfs_volume *vol, ATTR_RECORD *attr1, ATTR_RECORD *attr2, ATTR_RECORD *attr3 ) +static void test_rl_frag_combine(ntfs_volume *vol, ATTR_RECORD *attr1, ATTR_RECORD *attr2, ATTR_RECORD *attr3) { - runlist_element *run1; - runlist_element *run2; - runlist_element *run3; + runlist_element *run1; + runlist_element *run2; + runlist_element *run3; - run1 = ntfs_mapping_pairs_decompress( vol, attr1, NULL ); - if ( !run1 ) - return; + run1 = ntfs_mapping_pairs_decompress(vol, attr1, NULL); + if (!run1) + return; - run2 = ntfs_mapping_pairs_decompress( vol, attr2, NULL ); - if ( !run2 ) - return; + run2 = ntfs_mapping_pairs_decompress(vol, attr2, NULL); + if (!run2) + return; - run1 = test_rl_runlists_merge( run1, run2 ); + run1 = test_rl_runlists_merge(run1, run2); - run3 = ntfs_mapping_pairs_decompress( vol, attr3, NULL ); - if ( !run3 ) - return; + run3 = ntfs_mapping_pairs_decompress(vol, attr3, NULL); + if (!run3) + return; - run1 = test_rl_runlists_merge( run1, run3 ); + run1 = test_rl_runlists_merge(run1, run3); - free( run1 ); + free(run1); } /** @@ -2238,42 +2119,42 @@ static void test_rl_frag_combine( ntfs_volume *vol, ATTR_RECORD *attr1, ATTR_REC * * Returns: */ -static void test_rl_frag( char *test ) +static void test_rl_frag(char *test) { - ntfs_volume vol; - ATTR_RECORD *attr1 = ntfs_malloc( 1024 ); - ATTR_RECORD *attr2 = ntfs_malloc( 1024 ); - ATTR_RECORD *attr3 = ntfs_malloc( 1024 ); + ntfs_volume vol; + ATTR_RECORD *attr1 = ntfs_malloc(1024); + ATTR_RECORD *attr2 = ntfs_malloc(1024); + ATTR_RECORD *attr3 = ntfs_malloc(1024); - if ( !attr1 || !attr2 || !attr3 ) - goto out; + if (!attr1 || !attr2 || !attr3) + goto out; - vol.sb = NULL; - vol.sector_size_bits = 9; - vol.cluster_size = 2048; - vol.cluster_size_bits = 11; - vol.major_ver = 3; + vol.sb = NULL; + vol.sector_size_bits = 9; + vol.cluster_size = 2048; + vol.cluster_size_bits = 11; + vol.major_ver = 3; - if ( !test_rl_read_buffer( "runlist-data/attr1.bin", ( u8* ) attr1, 1024 ) ) - goto out; - if ( !test_rl_read_buffer( "runlist-data/attr2.bin", ( u8* ) attr2, 1024 ) ) - goto out; - if ( !test_rl_read_buffer( "runlist-data/attr3.bin", ( u8* ) attr3, 1024 ) ) - goto out; + if (!test_rl_read_buffer("runlist-data/attr1.bin", (u8*) attr1, 1024)) + goto out; + if (!test_rl_read_buffer("runlist-data/attr2.bin", (u8*) attr2, 1024)) + goto out; + if (!test_rl_read_buffer("runlist-data/attr3.bin", (u8*) attr3, 1024)) + goto out; - if ( strcmp( test, "123" ) == 0 ) test_rl_frag_combine( &vol, attr1, attr2, attr3 ); - else if ( strcmp( test, "132" ) == 0 ) test_rl_frag_combine( &vol, attr1, attr3, attr2 ); - else if ( strcmp( test, "213" ) == 0 ) test_rl_frag_combine( &vol, attr2, attr1, attr3 ); - else if ( strcmp( test, "231" ) == 0 ) test_rl_frag_combine( &vol, attr2, attr3, attr1 ); - else if ( strcmp( test, "312" ) == 0 ) test_rl_frag_combine( &vol, attr3, attr1, attr2 ); - else if ( strcmp( test, "321" ) == 0 ) test_rl_frag_combine( &vol, attr3, attr2, attr1 ); - else - printf( "Frag: No such test '%s'\n", test ); + if (strcmp(test, "123") == 0) test_rl_frag_combine(&vol, attr1, attr2, attr3); + else if (strcmp(test, "132") == 0) test_rl_frag_combine(&vol, attr1, attr3, attr2); + else if (strcmp(test, "213") == 0) test_rl_frag_combine(&vol, attr2, attr1, attr3); + else if (strcmp(test, "231") == 0) test_rl_frag_combine(&vol, attr2, attr3, attr1); + else if (strcmp(test, "312") == 0) test_rl_frag_combine(&vol, attr3, attr1, attr2); + else if (strcmp(test, "321") == 0) test_rl_frag_combine(&vol, attr3, attr2, attr1); + else + printf("Frag: No such test '%s'\n", test); out: - free( attr1 ); - free( attr2 ); - free( attr3 ); + free(attr1); + free(attr2); + free(attr3); } /** @@ -2285,15 +2166,15 @@ out: * * Returns: */ -int test_rl_main( int argc, char *argv[] ) +int test_rl_main(int argc, char *argv[]) { - if ( ( argc == 2 ) && ( strcmp( argv[1], "zero" ) == 0 ) ) test_rl_zero(); - else if ( ( argc == 3 ) && ( strcmp( argv[1], "frag" ) == 0 ) ) test_rl_frag( argv[2] ); - else if ( ( argc == 4 ) && ( strcmp( argv[1], "pure" ) == 0 ) ) test_rl_pure( argv[2], argv[3] ); - else - printf( "rl [zero|frag|pure] {args}\n" ); + if ((argc == 2) && (strcmp(argv[1], "zero") == 0)) test_rl_zero(); + else if ((argc == 3) && (strcmp(argv[1], "frag") == 0)) test_rl_frag(argv[2]); + else if ((argc == 4) && (strcmp(argv[1], "pure") == 0)) test_rl_pure(argv[2], argv[3]); + else + printf("rl [zero|frag|pure] {args}\n"); - return 0; + return 0; } #endif diff --git a/source/libntfs/runlist.h b/source/libntfs/runlist.h index 697e074f..4b73af9f 100644 --- a/source/libntfs/runlist.h +++ b/source/libntfs/runlist.h @@ -34,57 +34,56 @@ typedef runlist_element runlist; /** * struct _runlist_element - in memory vcn to lcn mapping array element. - * @vcn: starting vcn of the current array element - * @lcn: starting lcn of the current array element - * @length: length in clusters of the current array element + * @vcn: starting vcn of the current array element + * @lcn: starting lcn of the current array element + * @length: length in clusters of the current array element * * The last vcn (in fact the last vcn + 1) is reached when length == 0. * * When lcn == -1 this means that the count vcns starting at vcn are not * physically allocated (i.e. this is a hole / data is sparse). */ -struct _runlist_element /* In memory vcn to lcn mapping structure element. */ -{ - VCN vcn; /* vcn = Starting virtual cluster number. */ - LCN lcn; /* lcn = Starting logical cluster number. */ - s64 length; /* Run length in clusters. */ +struct _runlist_element {/* In memory vcn to lcn mapping structure element. */ + VCN vcn; /* vcn = Starting virtual cluster number. */ + LCN lcn; /* lcn = Starting logical cluster number. */ + s64 length; /* Run length in clusters. */ }; -extern runlist_element *ntfs_rl_extend( ntfs_attr *na, runlist_element *rl, - int more_entries ); +extern runlist_element *ntfs_rl_extend(ntfs_attr *na, runlist_element *rl, + int more_entries); -extern LCN ntfs_rl_vcn_to_lcn( const runlist_element *rl, const VCN vcn ); +extern LCN ntfs_rl_vcn_to_lcn(const runlist_element *rl, const VCN vcn); -extern s64 ntfs_rl_pread( const ntfs_volume *vol, const runlist_element *rl, - const s64 pos, s64 count, void *b ); -extern s64 ntfs_rl_pwrite( const ntfs_volume *vol, const runlist_element *rl, - s64 ofs, const s64 pos, s64 count, void *b ); +extern s64 ntfs_rl_pread(const ntfs_volume *vol, const runlist_element *rl, + const s64 pos, s64 count, void *b); +extern s64 ntfs_rl_pwrite(const ntfs_volume *vol, const runlist_element *rl, + s64 ofs, const s64 pos, s64 count, void *b); -extern runlist_element *ntfs_runlists_merge( runlist_element *drl, - runlist_element *srl ); +extern runlist_element *ntfs_runlists_merge(runlist_element *drl, + runlist_element *srl); -extern runlist_element *ntfs_mapping_pairs_decompress( const ntfs_volume *vol, - const ATTR_RECORD *attr, runlist_element *old_rl ); +extern runlist_element *ntfs_mapping_pairs_decompress(const ntfs_volume *vol, + const ATTR_RECORD *attr, runlist_element *old_rl); -extern int ntfs_get_nr_significant_bytes( const s64 n ); +extern int ntfs_get_nr_significant_bytes(const s64 n); -extern int ntfs_get_size_for_mapping_pairs( const ntfs_volume *vol, - const runlist_element *rl, const VCN start_vcn, int max_size ); +extern int ntfs_get_size_for_mapping_pairs(const ntfs_volume *vol, + const runlist_element *rl, const VCN start_vcn, int max_size); -extern int ntfs_write_significant_bytes( u8 *dst, const u8 *dst_max, - const s64 n ); +extern int ntfs_write_significant_bytes(u8 *dst, const u8 *dst_max, + const s64 n); -extern int ntfs_mapping_pairs_build( const ntfs_volume *vol, u8 *dst, - const int dst_len, const runlist_element *rl, - const VCN start_vcn, runlist_element const **stop_rl ); +extern int ntfs_mapping_pairs_build(const ntfs_volume *vol, u8 *dst, + const int dst_len, const runlist_element *rl, + const VCN start_vcn, runlist_element const **stop_rl); -extern int ntfs_rl_truncate( runlist **arl, const VCN start_vcn ); +extern int ntfs_rl_truncate(runlist **arl, const VCN start_vcn); -extern int ntfs_rl_sparse( runlist *rl ); -extern s64 ntfs_rl_get_compressed_size( ntfs_volume *vol, runlist *rl ); +extern int ntfs_rl_sparse(runlist *rl); +extern s64 ntfs_rl_get_compressed_size(ntfs_volume *vol, runlist *rl); #ifdef NTFS_TEST -int test_rl_main( int argc, char *argv[] ); +int test_rl_main(int argc, char *argv[]); #endif #endif /* defined _NTFS_RUNLIST_H */ diff --git a/source/libntfs/security.c b/source/libntfs/security.c index 20113648..ecf3ca07 100644 --- a/source/libntfs/security.c +++ b/source/libntfs/security.c @@ -65,8 +65,8 @@ #include "misc.h" /* - * JPA NTFS constants or structs - * should be moved to layout.h + * JPA NTFS constants or structs + * should be moved to layout.h */ #define ALIGN_SDS_BLOCK 0x40000 /* Alignment for a $SDS block */ @@ -74,74 +74,70 @@ #define STUFFSZ 0x4000 /* unitary stuffing size for $SDS */ #define FIRST_SECURITY_ID 0x100 /* Lowest security id */ -/* Mask for attributes which can be forced */ -#define FILE_ATTR_SETTABLE ( FILE_ATTR_READONLY \ - | FILE_ATTR_HIDDEN \ - | FILE_ATTR_SYSTEM \ - | FILE_ATTR_ARCHIVE \ - | FILE_ATTR_TEMPORARY \ - | FILE_ATTR_OFFLINE \ - | FILE_ATTR_NOT_CONTENT_INDEXED ) + /* Mask for attributes which can be forced */ +#define FILE_ATTR_SETTABLE ( FILE_ATTR_READONLY \ + | FILE_ATTR_HIDDEN \ + | FILE_ATTR_SYSTEM \ + | FILE_ATTR_ARCHIVE \ + | FILE_ATTR_TEMPORARY \ + | FILE_ATTR_OFFLINE \ + | FILE_ATTR_NOT_CONTENT_INDEXED ) -struct SII /* this is an image of an $SII index entry */ -{ - le16 offs; - le16 size; - le32 fill1; - le16 indexsz; - le16 indexksz; - le16 flags; - le16 fill2; - le32 keysecurid; +struct SII { /* this is an image of an $SII index entry */ + le16 offs; + le16 size; + le32 fill1; + le16 indexsz; + le16 indexksz; + le16 flags; + le16 fill2; + le32 keysecurid; - /* did not find official description for the following */ - le32 hash; - le32 securid; - le32 dataoffsl; /* documented as badly aligned */ - le32 dataoffsh; - le32 datasize; + /* did not find official description for the following */ + le32 hash; + le32 securid; + le32 dataoffsl; /* documented as badly aligned */ + le32 dataoffsh; + le32 datasize; } ; -struct SDH /* this is an image of an $SDH index entry */ -{ - le16 offs; - le16 size; - le32 fill1; - le16 indexsz; - le16 indexksz; - le16 flags; - le16 fill2; - le32 keyhash; - le32 keysecurid; +struct SDH { /* this is an image of an $SDH index entry */ + le16 offs; + le16 size; + le32 fill1; + le16 indexsz; + le16 indexksz; + le16 flags; + le16 fill2; + le32 keyhash; + le32 keysecurid; - /* did not find official description for the following */ - le32 hash; - le32 securid; - le32 dataoffsl; - le32 dataoffsh; - le32 datasize; - le32 fill3; -} ; + /* did not find official description for the following */ + le32 hash; + le32 securid; + le32 dataoffsl; + le32 dataoffsh; + le32 datasize; + le32 fill3; + } ; /* - * A few useful constants + * A few useful constants */ -static ntfschar sii_stream[] = { const_cpu_to_le16( '$' ), - const_cpu_to_le16( 'S' ), - const_cpu_to_le16( 'I' ), - const_cpu_to_le16( 'I' ), - const_cpu_to_le16( 0 ) - }; -static ntfschar sdh_stream[] = { const_cpu_to_le16( '$' ), - const_cpu_to_le16( 'S' ), - const_cpu_to_le16( 'D' ), - const_cpu_to_le16( 'H' ), - const_cpu_to_le16( 0 ) - }; +static ntfschar sii_stream[] = { const_cpu_to_le16('$'), + const_cpu_to_le16('S'), + const_cpu_to_le16('I'), + const_cpu_to_le16('I'), + const_cpu_to_le16(0) }; +static ntfschar sdh_stream[] = { const_cpu_to_le16('$'), + const_cpu_to_le16('S'), + const_cpu_to_le16('D'), + const_cpu_to_le16('H'), + const_cpu_to_le16(0) }; /* - * null SID (S-1-0-0) + * null SID (S-1-0-0) */ extern const SID *nullsid; @@ -150,27 +146,26 @@ extern const SID *nullsid; * The zero GUID. */ -static const GUID __zero_guid = { const_cpu_to_le32( 0 ), const_cpu_to_le16( 0 ), - const_cpu_to_le16( 0 ), { 0, 0, 0, 0, 0, 0, 0, 0 } - }; +static const GUID __zero_guid = { const_cpu_to_le32(0), const_cpu_to_le16(0), + const_cpu_to_le16(0), { 0, 0, 0, 0, 0, 0, 0, 0 } }; static const GUID *const zero_guid = &__zero_guid; /** * ntfs_guid_is_zero - check if a GUID is zero - * @guid: [IN] guid to check + * @guid: [IN] guid to check * * Return TRUE if @guid is a valid pointer to a GUID and it is the zero GUID * and FALSE otherwise. */ -BOOL ntfs_guid_is_zero( const GUID *guid ) +BOOL ntfs_guid_is_zero(const GUID *guid) { - return ( memcmp( guid, zero_guid, sizeof( *zero_guid ) ) ); + return (memcmp(guid, zero_guid, sizeof(*zero_guid))); } /** * ntfs_guid_to_mbs - convert a GUID to a multi byte string - * @guid: [IN] guid to convert - * @guid_str: [OUT] string in which to return the GUID (optional) + * @guid: [IN] guid to convert + * @guid_str: [OUT] string in which to return the GUID (optional) * * Convert the GUID pointed to by @guid to a multi byte string of the form * "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX". Therefore, @guid_str (if not NULL) @@ -183,41 +178,39 @@ BOOL ntfs_guid_is_zero( const GUID *guid ) * On success return the converted string and on failure return NULL with errno * set to the error code. */ -char *ntfs_guid_to_mbs( const GUID *guid, char *guid_str ) +char *ntfs_guid_to_mbs(const GUID *guid, char *guid_str) { - char *_guid_str; - int res; + char *_guid_str; + int res; - if ( !guid ) - { - errno = EINVAL; - return NULL; - } - _guid_str = guid_str; - if ( !_guid_str ) - { - _guid_str = ( char* )ntfs_malloc( 37 ); - if ( !_guid_str ) - return _guid_str; - } - res = snprintf( _guid_str, 37, - "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", - ( unsigned int )le32_to_cpu( guid->data1 ), - le16_to_cpu( guid->data2 ), le16_to_cpu( guid->data3 ), - guid->data4[0], guid->data4[1], - guid->data4[2], guid->data4[3], guid->data4[4], - guid->data4[5], guid->data4[6], guid->data4[7] ); - if ( res == 36 ) - return _guid_str; - if ( !guid_str ) - free( _guid_str ); - errno = EINVAL; - return NULL; + if (!guid) { + errno = EINVAL; + return NULL; + } + _guid_str = guid_str; + if (!_guid_str) { + _guid_str = (char*)ntfs_malloc(37); + if (!_guid_str) + return _guid_str; + } + res = snprintf(_guid_str, 37, + "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + (unsigned int)le32_to_cpu(guid->data1), + le16_to_cpu(guid->data2), le16_to_cpu(guid->data3), + guid->data4[0], guid->data4[1], + guid->data4[2], guid->data4[3], guid->data4[4], + guid->data4[5], guid->data4[6], guid->data4[7]); + if (res == 36) + return _guid_str; + if (!guid_str) + free(_guid_str); + errno = EINVAL; + return NULL; } /** * ntfs_sid_to_mbs_size - determine maximum size for the string of a SID - * @sid: [IN] SID for which to determine the maximum string size + * @sid: [IN] SID for which to determine the maximum string size * * Determine the maximum multi byte string size in bytes which is needed to * store the standard textual representation of the SID pointed to by @sid. @@ -226,49 +219,48 @@ char *ntfs_guid_to_mbs( const GUID *guid, char *guid_str ) * On success return the maximum number of bytes needed to store the multi byte * string and on failure return -1 with errno set to the error code. */ -int ntfs_sid_to_mbs_size( const SID *sid ) +int ntfs_sid_to_mbs_size(const SID *sid) { - int size, i; + int size, i; - if ( !ntfs_sid_is_valid( sid ) ) - { - errno = EINVAL; - return -1; - } - /* Start with "S-". */ - size = 2; - /* - * Add the SID_REVISION. Hopefully the compiler will optimize this - * away as SID_REVISION is a constant. - */ - for ( i = SID_REVISION; i > 0; i /= 10 ) - size++; - /* Add the "-". */ - size++; - /* - * Add the identifier authority. If it needs to be in decimal, the - * maximum is 2^32-1 = 4294967295 = 10 characters. If it needs to be - * in hexadecimal, then maximum is 0x665544332211 = 14 characters. - */ - if ( !sid->identifier_authority.high_part ) - size += 10; - else - size += 14; - /* - * Finally, add the sub authorities. For each we have a "-" followed - * by a decimal which can be up to 2^32-1 = 4294967295 = 10 characters. - */ - size += ( 1 + 10 ) * sid->sub_authority_count; - /* We need the zero byte at the end, too. */ - size++; - return size * sizeof( char ); + if (!ntfs_sid_is_valid(sid)) { + errno = EINVAL; + return -1; + } + /* Start with "S-". */ + size = 2; + /* + * Add the SID_REVISION. Hopefully the compiler will optimize this + * away as SID_REVISION is a constant. + */ + for (i = SID_REVISION; i > 0; i /= 10) + size++; + /* Add the "-". */ + size++; + /* + * Add the identifier authority. If it needs to be in decimal, the + * maximum is 2^32-1 = 4294967295 = 10 characters. If it needs to be + * in hexadecimal, then maximum is 0x665544332211 = 14 characters. + */ + if (!sid->identifier_authority.high_part) + size += 10; + else + size += 14; + /* + * Finally, add the sub authorities. For each we have a "-" followed + * by a decimal which can be up to 2^32-1 = 4294967295 = 10 characters. + */ + size += (1 + 10) * sid->sub_authority_count; + /* We need the zero byte at the end, too. */ + size++; + return size * sizeof(char); } /** * ntfs_sid_to_mbs - convert a SID to a multi byte string - * @sid: [IN] SID to convert - * @sid_str: [OUT] string in which to return the SID (optional) - * @sid_str_size: [IN] size in bytes of @sid_str + * @sid: [IN] SID to convert + * @sid_str: [OUT] string in which to return the SID (optional) + * @sid_str_size: [IN] size in bytes of @sid_str * * Convert the SID pointed to by @sid to its standard textual representation. * @sid_str (if not NULL) needs to be able to store at least @@ -276,16 +268,16 @@ int ntfs_sid_to_mbs_size( const SID *sid ) * @sid_str if @sid_str is not NULL. * * The standard textual representation of the SID is of the form: - * S-R-I-S-S... + * S-R-I-S-S... * Where: * - The first "S" is the literal character 'S' identifying the following - * digits as a SID. + * digits as a SID. * - R is the revision level of the SID expressed as a sequence of digits - * in decimal. + * in decimal. * - I is the 48-bit identifier_authority, expressed as digits in decimal, - * if I < 2^32, or hexadecimal prefixed by "0x", if I >= 2^32. + * if I < 2^32, or hexadecimal prefixed by "0x", if I >= 2^32. * - S... is one or more sub_authority values, expressed as digits in - * decimal. + * decimal. * * If @sid_str is not NULL it will contain the converted SUID on return. If it * is NULL a string will be allocated and this will be returned. The caller is @@ -294,99 +286,93 @@ int ntfs_sid_to_mbs_size( const SID *sid ) * On success return the converted string and on failure return NULL with errno * set to the error code. */ -char *ntfs_sid_to_mbs( const SID *sid, char *sid_str, size_t sid_str_size ) +char *ntfs_sid_to_mbs(const SID *sid, char *sid_str, size_t sid_str_size) { - u64 u; - le32 leauth; - char *s; - int i, j, cnt; + u64 u; + le32 leauth; + char *s; + int i, j, cnt; - /* - * No need to check @sid if !@sid_str since ntfs_sid_to_mbs_size() will - * check @sid, too. 8 is the minimum SID string size. - */ - if ( sid_str && ( sid_str_size < 8 || !ntfs_sid_is_valid( sid ) ) ) - { - errno = EINVAL; - return NULL; - } - /* Allocate string if not provided. */ - if ( !sid_str ) - { - cnt = ntfs_sid_to_mbs_size( sid ); - if ( cnt < 0 ) - return NULL; - s = ( char* )ntfs_malloc( cnt ); - if ( !s ) - return s; - sid_str = s; - /* So we know we allocated it. */ - sid_str_size = 0; - } - else - { - s = sid_str; - cnt = sid_str_size; - } - /* Start with "S-R-". */ - i = snprintf( s, cnt, "S-%hhu-", ( unsigned char )sid->revision ); - if ( i < 0 || i >= cnt ) - goto err_out; - s += i; - cnt -= i; - /* Add the identifier authority. */ - for ( u = i = 0, j = 40; i < 6; i++, j -= 8 ) - u += ( u64 )sid->identifier_authority.value[i] << j; - if ( !sid->identifier_authority.high_part ) - i = snprintf( s, cnt, "%lu", ( unsigned long )u ); - else - i = snprintf( s, cnt, "0x%llx", ( unsigned long long )u ); - if ( i < 0 || i >= cnt ) - goto err_out; - s += i; - cnt -= i; - /* Finally, add the sub authorities. */ - for ( j = 0; j < sid->sub_authority_count; j++ ) - { - leauth = sid->sub_authority[j]; - i = snprintf( s, cnt, "-%u", ( unsigned int ) - le32_to_cpu( leauth ) ); - if ( i < 0 || i >= cnt ) - goto err_out; - s += i; - cnt -= i; - } - return sid_str; + /* + * No need to check @sid if !@sid_str since ntfs_sid_to_mbs_size() will + * check @sid, too. 8 is the minimum SID string size. + */ + if (sid_str && (sid_str_size < 8 || !ntfs_sid_is_valid(sid))) { + errno = EINVAL; + return NULL; + } + /* Allocate string if not provided. */ + if (!sid_str) { + cnt = ntfs_sid_to_mbs_size(sid); + if (cnt < 0) + return NULL; + s = (char*)ntfs_malloc(cnt); + if (!s) + return s; + sid_str = s; + /* So we know we allocated it. */ + sid_str_size = 0; + } else { + s = sid_str; + cnt = sid_str_size; + } + /* Start with "S-R-". */ + i = snprintf(s, cnt, "S-%hhu-", (unsigned char)sid->revision); + if (i < 0 || i >= cnt) + goto err_out; + s += i; + cnt -= i; + /* Add the identifier authority. */ + for (u = i = 0, j = 40; i < 6; i++, j -= 8) + u += (u64)sid->identifier_authority.value[i] << j; + if (!sid->identifier_authority.high_part) + i = snprintf(s, cnt, "%lu", (unsigned long)u); + else + i = snprintf(s, cnt, "0x%llx", (unsigned long long)u); + if (i < 0 || i >= cnt) + goto err_out; + s += i; + cnt -= i; + /* Finally, add the sub authorities. */ + for (j = 0; j < sid->sub_authority_count; j++) { + leauth = sid->sub_authority[j]; + i = snprintf(s, cnt, "-%u", (unsigned int) + le32_to_cpu(leauth)); + if (i < 0 || i >= cnt) + goto err_out; + s += i; + cnt -= i; + } + return sid_str; err_out: - if ( i >= cnt ) - i = EMSGSIZE; - else - i = errno; - if ( !sid_str_size ) - free( sid_str ); - errno = i; - return NULL; + if (i >= cnt) + i = EMSGSIZE; + else + i = errno; + if (!sid_str_size) + free(sid_str); + errno = i; + return NULL; } /** * ntfs_generate_guid - generatates a random current guid. - * @guid: [OUT] pointer to a GUID struct to hold the generated guid. + * @guid: [OUT] pointer to a GUID struct to hold the generated guid. * * perhaps not a very good random number generator though... */ -void ntfs_generate_guid( GUID *guid ) +void ntfs_generate_guid(GUID *guid) { - unsigned int i; - u8 *p = ( u8 * )guid; + unsigned int i; + u8 *p = (u8 *)guid; - for ( i = 0; i < sizeof( GUID ); i++ ) - { - p[i] = ( u8 )( random() & 0xFF ); - if ( i == 7 ) - p[7] = ( p[7] & 0x0F ) | 0x40; - if ( i == 8 ) - p[8] = ( p[8] & 0x3F ) | 0x80; - } + for (i = 0; i < sizeof(GUID); i++) { + p[i] = (u8)(random() & 0xFF); + if (i == 7) + p[7] = (p[7] & 0x0F) | 0x40; + if (i == 8) + p[8] = (p[8] & 0x3F) | 0x80; + } } /** @@ -405,913 +391,834 @@ void ntfs_generate_guid( GUID *guid ) * * Return the calculated security hash in little endian. */ -le32 ntfs_security_hash( const SECURITY_DESCRIPTOR_RELATIVE *sd, const u32 len ) +le32 ntfs_security_hash(const SECURITY_DESCRIPTOR_RELATIVE *sd, const u32 len) { - const le32 *pos = ( const le32* )sd; - const le32 *end = pos + ( len >> 2 ); - u32 hash = 0; + const le32 *pos = (const le32*)sd; + const le32 *end = pos + (len >> 2); + u32 hash = 0; - while ( pos < end ) - { - hash = le32_to_cpup( pos ) + ntfs_rol32( hash, 3 ); - pos++; - } - return cpu_to_le32( hash ); + while (pos < end) { + hash = le32_to_cpup(pos) + ntfs_rol32(hash, 3); + pos++; + } + return cpu_to_le32(hash); } /* - * Internal read - * copied and pasted from ntfs_fuse_read() and made independent - * of fuse context + * Internal read + * copied and pasted from ntfs_fuse_read() and made independent + * of fuse context */ -static int ntfs_local_read( ntfs_inode *ni, - ntfschar *stream_name, int stream_name_len, - char *buf, size_t size, off_t offset ) +static int ntfs_local_read(ntfs_inode *ni, + ntfschar *stream_name, int stream_name_len, + char *buf, size_t size, off_t offset) { - ntfs_attr *na = NULL; - int res, total = 0; + ntfs_attr *na = NULL; + int res, total = 0; - na = ntfs_attr_open( ni, AT_DATA, stream_name, stream_name_len ); - if ( !na ) - { - res = -errno; - goto exit; - } - if ( ( size_t )offset < ( size_t )na->data_size ) - { - if ( offset + size > ( size_t )na->data_size ) - size = na->data_size - offset; - while ( size ) - { - res = ntfs_attr_pread( na, offset, size, buf ); - if ( ( off_t )res < ( off_t )size ) - ntfs_log_perror( "ntfs_attr_pread partial read " - "(%lld : %lld <> %d)", - ( long long )offset, - ( long long )size, res ); - if ( res <= 0 ) - { - res = -errno; - goto exit; - } - size -= res; - offset += res; - total += res; - } - } - res = total; + na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len); + if (!na) { + res = -errno; + goto exit; + } + if ((size_t)offset < (size_t)na->data_size) { + if (offset + size > (size_t)na->data_size) + size = na->data_size - offset; + while (size) { + res = ntfs_attr_pread(na, offset, size, buf); + if ((off_t)res < (off_t)size) + ntfs_log_perror("ntfs_attr_pread partial read " + "(%lld : %lld <> %d)", + (long long)offset, + (long long)size, res); + if (res <= 0) { + res = -errno; + goto exit; + } + size -= res; + offset += res; + total += res; + } + } + res = total; exit: - if ( na ) - ntfs_attr_close( na ); - return res; + if (na) + ntfs_attr_close(na); + return res; } /* - * Internal write - * copied and pasted from ntfs_fuse_write() and made independent - * of fuse context + * Internal write + * copied and pasted from ntfs_fuse_write() and made independent + * of fuse context */ -static int ntfs_local_write( ntfs_inode *ni, - ntfschar *stream_name, int stream_name_len, - char *buf, size_t size, off_t offset ) +static int ntfs_local_write(ntfs_inode *ni, + ntfschar *stream_name, int stream_name_len, + char *buf, size_t size, off_t offset) { - ntfs_attr *na = NULL; - int res, total = 0; + ntfs_attr *na = NULL; + int res, total = 0; - na = ntfs_attr_open( ni, AT_DATA, stream_name, stream_name_len ); - if ( !na ) - { - res = -errno; - goto exit; - } - while ( size ) - { - res = ntfs_attr_pwrite( na, offset, size, buf ); - if ( res < ( s64 )size ) - ntfs_log_perror( "ntfs_attr_pwrite partial write (%lld: " - "%lld <> %d)", ( long long )offset, - ( long long )size, res ); - if ( res <= 0 ) - { - res = -errno; - goto exit; - } - size -= res; - offset += res; - total += res; - } - res = total; + na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len); + if (!na) { + res = -errno; + goto exit; + } + while (size) { + res = ntfs_attr_pwrite(na, offset, size, buf); + if (res < (s64)size) + ntfs_log_perror("ntfs_attr_pwrite partial write (%lld: " + "%lld <> %d)", (long long)offset, + (long long)size, res); + if (res <= 0) { + res = -errno; + goto exit; + } + size -= res; + offset += res; + total += res; + } + res = total; exit: - if ( na ) - ntfs_attr_close( na ); - return res; + if (na) + ntfs_attr_close(na); + return res; } /* - * Get the first entry of current index block - * cut and pasted form ntfs_ie_get_first() in index.c + * Get the first entry of current index block + * cut and pasted form ntfs_ie_get_first() in index.c */ -static INDEX_ENTRY *ntfs_ie_get_first( INDEX_HEADER *ih ) +static INDEX_ENTRY *ntfs_ie_get_first(INDEX_HEADER *ih) { - return ( INDEX_ENTRY* )( ( u8* )ih + le32_to_cpu( ih->entries_offset ) ); + return (INDEX_ENTRY*)((u8*)ih + le32_to_cpu(ih->entries_offset)); } /* - * Stuff a 256KB block into $SDS before writing descriptors - * into the block. + * Stuff a 256KB block into $SDS before writing descriptors + * into the block. * - * This prevents $SDS from being automatically declared as sparse - * when the second copy of the first security descriptor is written - * 256KB further ahead. + * This prevents $SDS from being automatically declared as sparse + * when the second copy of the first security descriptor is written + * 256KB further ahead. * - * Having $SDS declared as a sparse file is not wrong by itself - * and chkdsk leaves it as a sparse file. It does however complain - * and add a sparse flag (0x0200) into field file_attributes of - * STANDARD_INFORMATION of $Secure. This probably means that a - * sparse attribute (ATTR_IS_SPARSE) is only allowed in sparse - * files (FILE_ATTR_SPARSE_FILE). + * Having $SDS declared as a sparse file is not wrong by itself + * and chkdsk leaves it as a sparse file. It does however complain + * and add a sparse flag (0x0200) into field file_attributes of + * STANDARD_INFORMATION of $Secure. This probably means that a + * sparse attribute (ATTR_IS_SPARSE) is only allowed in sparse + * files (FILE_ATTR_SPARSE_FILE). * - * Windows normally does not convert to sparse attribute or sparse - * file. Stuffing is just a way to get to the same result. + * Windows normally does not convert to sparse attribute or sparse + * file. Stuffing is just a way to get to the same result. */ -static int entersecurity_stuff( ntfs_volume *vol, off_t offs ) +static int entersecurity_stuff(ntfs_volume *vol, off_t offs) { - int res; - int written; - unsigned long total; - char *stuff; + int res; + int written; + unsigned long total; + char *stuff; - res = 0; - total = 0; - stuff = ( char* )ntfs_malloc( STUFFSZ ); - if ( stuff ) - { - memset( stuff, 0, STUFFSZ ); - do - { - written = ntfs_local_write( vol->secure_ni, - STREAM_SDS, 4, stuff, STUFFSZ, offs ); - if ( written == STUFFSZ ) - { - total += STUFFSZ; - offs += STUFFSZ; - } - else - { - errno = ENOSPC; - res = -1; - } - } - while ( !res && ( total < ALIGN_SDS_BLOCK ) ); - free( stuff ); - } - else - { - errno = ENOMEM; - res = -1; - } - return ( res ); + res = 0; + total = 0; + stuff = (char*)ntfs_malloc(STUFFSZ); + if (stuff) { + memset(stuff, 0, STUFFSZ); + do { + written = ntfs_local_write(vol->secure_ni, + STREAM_SDS, 4, stuff, STUFFSZ, offs); + if (written == STUFFSZ) { + total += STUFFSZ; + offs += STUFFSZ; + } else { + errno = ENOSPC; + res = -1; + } + } while (!res && (total < ALIGN_SDS_BLOCK)); + free(stuff); + } else { + errno = ENOMEM; + res = -1; + } + return (res); } /* - * Enter a new security descriptor into $Secure (data only) + * Enter a new security descriptor into $Secure (data only) * it has to be written twice with an offset of 256KB * - * Should only be called by entersecurityattr() to ensure consistency + * Should only be called by entersecurityattr() to ensure consistency * - * Returns zero if sucessful + * Returns zero if sucessful */ -static int entersecurity_data( ntfs_volume *vol, - const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz, - le32 hash, le32 keyid, off_t offs, int gap ) +static int entersecurity_data(ntfs_volume *vol, + const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz, + le32 hash, le32 keyid, off_t offs, int gap) { - int res; - int written1; - int written2; - char *fullattr; - int fullsz; - SECURITY_DESCRIPTOR_HEADER *phsds; + int res; + int written1; + int written2; + char *fullattr; + int fullsz; + SECURITY_DESCRIPTOR_HEADER *phsds; - res = -1; - fullsz = attrsz + gap + sizeof( SECURITY_DESCRIPTOR_HEADER ); - fullattr = ( char* )ntfs_malloc( fullsz ); - if ( fullattr ) - { - /* - * Clear the gap from previous descriptor - * this could be useful for appending the second - * copy to the end of file. When creating a new - * 256K block, the gap is cleared while writing - * the first copy - */ - if ( gap ) - memset( fullattr, 0, gap ); - memcpy( &fullattr[gap + sizeof( SECURITY_DESCRIPTOR_HEADER )], - attr, attrsz ); - phsds = ( SECURITY_DESCRIPTOR_HEADER* ) & fullattr[gap]; - phsds->hash = hash; - phsds->security_id = keyid; - phsds->offset = cpu_to_le64( offs ); - phsds->length = cpu_to_le32( fullsz - gap ); - written1 = ntfs_local_write( vol->secure_ni, - STREAM_SDS, 4, fullattr, fullsz, - offs - gap ); - written2 = ntfs_local_write( vol->secure_ni, - STREAM_SDS, 4, fullattr, fullsz, - offs - gap + ALIGN_SDS_BLOCK ); - if ( ( written1 == fullsz ) - && ( written2 == written1 ) ) - res = 0; - else - errno = ENOSPC; - free( fullattr ); - } - else - errno = ENOMEM; - return ( res ); + res = -1; + fullsz = attrsz + gap + sizeof(SECURITY_DESCRIPTOR_HEADER); + fullattr = (char*)ntfs_malloc(fullsz); + if (fullattr) { + /* + * Clear the gap from previous descriptor + * this could be useful for appending the second + * copy to the end of file. When creating a new + * 256K block, the gap is cleared while writing + * the first copy + */ + if (gap) + memset(fullattr,0,gap); + memcpy(&fullattr[gap + sizeof(SECURITY_DESCRIPTOR_HEADER)], + attr,attrsz); + phsds = (SECURITY_DESCRIPTOR_HEADER*)&fullattr[gap]; + phsds->hash = hash; + phsds->security_id = keyid; + phsds->offset = cpu_to_le64(offs); + phsds->length = cpu_to_le32(fullsz - gap); + written1 = ntfs_local_write(vol->secure_ni, + STREAM_SDS, 4, fullattr, fullsz, + offs - gap); + written2 = ntfs_local_write(vol->secure_ni, + STREAM_SDS, 4, fullattr, fullsz, + offs - gap + ALIGN_SDS_BLOCK); + if ((written1 == fullsz) + && (written2 == written1)) + res = 0; + else + errno = ENOSPC; + free(fullattr); + } else + errno = ENOMEM; + return (res); } /* - * Enter a new security descriptor in $Secure (indexes only) + * Enter a new security descriptor in $Secure (indexes only) * - * Should only be called by entersecurityattr() to ensure consistency + * Should only be called by entersecurityattr() to ensure consistency * - * Returns zero if sucessful + * Returns zero if sucessful */ -static int entersecurity_indexes( ntfs_volume *vol, s64 attrsz, - le32 hash, le32 keyid, off_t offs ) +static int entersecurity_indexes(ntfs_volume *vol, s64 attrsz, + le32 hash, le32 keyid, off_t offs) { - union - { - struct - { - le32 dataoffsl; - le32 dataoffsh; - } parts; - le64 all; - } realign; - int res; - ntfs_index_context *xsii; - ntfs_index_context *xsdh; - struct SII newsii; - struct SDH newsdh; + union { + struct { + le32 dataoffsl; + le32 dataoffsh; + } parts; + le64 all; + } realign; + int res; + ntfs_index_context *xsii; + ntfs_index_context *xsdh; + struct SII newsii; + struct SDH newsdh; - res = -1; - /* enter a new $SII record */ + res = -1; + /* enter a new $SII record */ - xsii = vol->secure_xsii; - ntfs_index_ctx_reinit( xsii ); - newsii.offs = const_cpu_to_le16( 20 ); - newsii.size = const_cpu_to_le16( sizeof( struct SII ) - 20 ); - newsii.fill1 = const_cpu_to_le32( 0 ); - newsii.indexsz = const_cpu_to_le16( sizeof( struct SII ) ); - newsii.indexksz = const_cpu_to_le16( sizeof( SII_INDEX_KEY ) ); - newsii.flags = const_cpu_to_le16( 0 ); - newsii.fill2 = const_cpu_to_le16( 0 ); - newsii.keysecurid = keyid; - newsii.hash = hash; - newsii.securid = keyid; - realign.all = cpu_to_le64( offs ); - newsii.dataoffsh = realign.parts.dataoffsh; - newsii.dataoffsl = realign.parts.dataoffsl; - newsii.datasize = cpu_to_le32( attrsz - + sizeof( SECURITY_DESCRIPTOR_HEADER ) ); - if ( !ntfs_ie_add( xsii, ( INDEX_ENTRY* )&newsii ) ) - { + xsii = vol->secure_xsii; + ntfs_index_ctx_reinit(xsii); + newsii.offs = const_cpu_to_le16(20); + newsii.size = const_cpu_to_le16(sizeof(struct SII) - 20); + newsii.fill1 = const_cpu_to_le32(0); + newsii.indexsz = const_cpu_to_le16(sizeof(struct SII)); + newsii.indexksz = const_cpu_to_le16(sizeof(SII_INDEX_KEY)); + newsii.flags = const_cpu_to_le16(0); + newsii.fill2 = const_cpu_to_le16(0); + newsii.keysecurid = keyid; + newsii.hash = hash; + newsii.securid = keyid; + realign.all = cpu_to_le64(offs); + newsii.dataoffsh = realign.parts.dataoffsh; + newsii.dataoffsl = realign.parts.dataoffsl; + newsii.datasize = cpu_to_le32(attrsz + + sizeof(SECURITY_DESCRIPTOR_HEADER)); + if (!ntfs_ie_add(xsii,(INDEX_ENTRY*)&newsii)) { - /* enter a new $SDH record */ + /* enter a new $SDH record */ - xsdh = vol->secure_xsdh; - ntfs_index_ctx_reinit( xsdh ); - newsdh.offs = const_cpu_to_le16( 24 ); - newsdh.size = const_cpu_to_le16( - sizeof( SECURITY_DESCRIPTOR_HEADER ) ); - newsdh.fill1 = const_cpu_to_le32( 0 ); - newsdh.indexsz = const_cpu_to_le16( - sizeof( struct SDH ) ); - newsdh.indexksz = const_cpu_to_le16( - sizeof( SDH_INDEX_KEY ) ); - newsdh.flags = const_cpu_to_le16( 0 ); - newsdh.fill2 = const_cpu_to_le16( 0 ); - newsdh.keyhash = hash; - newsdh.keysecurid = keyid; - newsdh.hash = hash; - newsdh.securid = keyid; - newsdh.dataoffsh = realign.parts.dataoffsh; - newsdh.dataoffsl = realign.parts.dataoffsl; - newsdh.datasize = cpu_to_le32( attrsz - + sizeof( SECURITY_DESCRIPTOR_HEADER ) ); - /* special filler value, Windows generally */ - /* fills with 0x00490049, sometimes with zero */ - newsdh.fill3 = const_cpu_to_le32( 0x00490049 ); - if ( !ntfs_ie_add( xsdh, ( INDEX_ENTRY* )&newsdh ) ) - res = 0; - } - return ( res ); + xsdh = vol->secure_xsdh; + ntfs_index_ctx_reinit(xsdh); + newsdh.offs = const_cpu_to_le16(24); + newsdh.size = const_cpu_to_le16( + sizeof(SECURITY_DESCRIPTOR_HEADER)); + newsdh.fill1 = const_cpu_to_le32(0); + newsdh.indexsz = const_cpu_to_le16( + sizeof(struct SDH)); + newsdh.indexksz = const_cpu_to_le16( + sizeof(SDH_INDEX_KEY)); + newsdh.flags = const_cpu_to_le16(0); + newsdh.fill2 = const_cpu_to_le16(0); + newsdh.keyhash = hash; + newsdh.keysecurid = keyid; + newsdh.hash = hash; + newsdh.securid = keyid; + newsdh.dataoffsh = realign.parts.dataoffsh; + newsdh.dataoffsl = realign.parts.dataoffsl; + newsdh.datasize = cpu_to_le32(attrsz + + sizeof(SECURITY_DESCRIPTOR_HEADER)); + /* special filler value, Windows generally */ + /* fills with 0x00490049, sometimes with zero */ + newsdh.fill3 = const_cpu_to_le32(0x00490049); + if (!ntfs_ie_add(xsdh,(INDEX_ENTRY*)&newsdh)) + res = 0; + } + return (res); } /* - * Enter a new security descriptor in $Secure (data and indexes) - * Returns id of entry, or zero if there is a problem. - * (should not be called for NTFS version < 3.0) + * Enter a new security descriptor in $Secure (data and indexes) + * Returns id of entry, or zero if there is a problem. + * (should not be called for NTFS version < 3.0) * - * important : calls have to be serialized, however no locking is - * needed while fuse is not multithreaded + * important : calls have to be serialized, however no locking is + * needed while fuse is not multithreaded */ -static le32 entersecurityattr( ntfs_volume *vol, - const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz, - le32 hash ) +static le32 entersecurityattr(ntfs_volume *vol, + const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz, + le32 hash) { - union - { - struct - { - le32 dataoffsl; - le32 dataoffsh; - } parts; - le64 all; - } realign; - le32 securid; - le32 keyid; - u32 newkey; - off_t offs; - int gap; - int size; - BOOL found; - struct SII *psii; - INDEX_ENTRY *entry; - INDEX_ENTRY *next; - ntfs_index_context *xsii; - int retries; - ntfs_attr *na; - int olderrno; + union { + struct { + le32 dataoffsl; + le32 dataoffsh; + } parts; + le64 all; + } realign; + le32 securid; + le32 keyid; + u32 newkey; + off_t offs; + int gap; + int size; + BOOL found; + struct SII *psii; + INDEX_ENTRY *entry; + INDEX_ENTRY *next; + ntfs_index_context *xsii; + int retries; + ntfs_attr *na; + int olderrno; - /* find the first available securid beyond the last key */ - /* in $Secure:$SII. This also determines the first */ - /* available location in $Secure:$SDS, as this stream */ - /* is always appended to and the id's are allocated */ - /* in sequence */ + /* find the first available securid beyond the last key */ + /* in $Secure:$SII. This also determines the first */ + /* available location in $Secure:$SDS, as this stream */ + /* is always appended to and the id's are allocated */ + /* in sequence */ - securid = const_cpu_to_le32( 0 ); - xsii = vol->secure_xsii; - ntfs_index_ctx_reinit( xsii ); - offs = size = 0; - keyid = const_cpu_to_le32( -1 ); - olderrno = errno; - found = !ntfs_index_lookup( ( char* ) & keyid, - sizeof( SII_INDEX_KEY ), xsii ); - if ( !found && ( errno != ENOENT ) ) - { - ntfs_log_perror( "Inconsistency in index $SII" ); - psii = ( struct SII* )NULL; - } - else - { - /* restore errno to avoid misinterpretation */ - errno = olderrno; - entry = xsii->entry; - psii = ( struct SII* )xsii->entry; - } - if ( psii ) - { - /* - * Get last entry in block, but must get first one - * one first, as we should already be beyond the - * last one. For some reason the search for the last - * entry sometimes does not return the last block... - * we assume this can only happen in root block - */ - if ( xsii->is_in_root ) - entry = ntfs_ie_get_first - ( ( INDEX_HEADER* ) & xsii->ir->index ); - else - entry = ntfs_ie_get_first - ( ( INDEX_HEADER* ) & xsii->ib->index ); - /* - * All index blocks should be at least half full - * so there always is a last entry but one, - * except when creating the first entry in index root. - * This was however found not to be true : chkdsk - * sometimes deletes all the (unused) keys in the last - * index block without rebalancing the tree. - * When this happens, a new search is restarted from - * the smallest key. - */ - keyid = const_cpu_to_le32( 0 ); - retries = 0; - while ( entry ) - { - next = ntfs_index_next( entry, xsii ); - if ( next ) - { - psii = ( struct SII* )next; - /* save last key and */ - /* available position */ - keyid = psii->keysecurid; - realign.parts.dataoffsh - = psii->dataoffsh; - realign.parts.dataoffsl - = psii->dataoffsl; - offs = le64_to_cpu( realign.all ); - size = le32_to_cpu( psii->datasize ); - } - entry = next; - if ( !entry && !keyid && !retries ) - { - /* search failed, retry from smallest key */ - ntfs_index_ctx_reinit( xsii ); - found = !ntfs_index_lookup( ( char* ) & keyid, - sizeof( SII_INDEX_KEY ), xsii ); - if ( !found && ( errno != ENOENT ) ) - { - ntfs_log_perror( "Index $SII is broken" ); - } - else - { - /* restore errno */ - errno = olderrno; - entry = xsii->entry; - } - retries++; - } - } - } - if ( !keyid ) - { - /* - * could not find any entry, before creating the first - * entry, make a double check by making sure size of $SII - * is less than needed for one entry - */ - securid = const_cpu_to_le32( 0 ); - na = ntfs_attr_open( vol->secure_ni, AT_INDEX_ROOT, sii_stream, 4 ); - if ( na ) - { - if ( ( size_t )na->data_size < sizeof( struct SII ) ) - { - ntfs_log_error( "Creating the first security_id\n" ); - securid = const_cpu_to_le32( FIRST_SECURITY_ID ); - } - ntfs_attr_close( na ); - } - if ( !securid ) - { - ntfs_log_error( "Error creating a security_id\n" ); - errno = EIO; - } - } - else - { - newkey = le32_to_cpu( keyid ) + 1; - securid = cpu_to_le32( newkey ); - } - /* - * The security attr has to be written twice 256KB - * apart. This implies that offsets like - * 0x40000*odd_integer must be left available for - * the second copy. So align to next block when - * the last byte overflows on a wrong block. - */ + securid = const_cpu_to_le32(0); + xsii = vol->secure_xsii; + ntfs_index_ctx_reinit(xsii); + offs = size = 0; + keyid = const_cpu_to_le32(-1); + olderrno = errno; + found = !ntfs_index_lookup((char*)&keyid, + sizeof(SII_INDEX_KEY), xsii); + if (!found && (errno != ENOENT)) { + ntfs_log_perror("Inconsistency in index $SII"); + psii = (struct SII*)NULL; + } else { + /* restore errno to avoid misinterpretation */ + errno = olderrno; + entry = xsii->entry; + psii = (struct SII*)xsii->entry; + } + if (psii) { + /* + * Get last entry in block, but must get first one + * one first, as we should already be beyond the + * last one. For some reason the search for the last + * entry sometimes does not return the last block... + * we assume this can only happen in root block + */ + if (xsii->is_in_root) + entry = ntfs_ie_get_first + ((INDEX_HEADER*)&xsii->ir->index); + else + entry = ntfs_ie_get_first + ((INDEX_HEADER*)&xsii->ib->index); + /* + * All index blocks should be at least half full + * so there always is a last entry but one, + * except when creating the first entry in index root. + * This was however found not to be true : chkdsk + * sometimes deletes all the (unused) keys in the last + * index block without rebalancing the tree. + * When this happens, a new search is restarted from + * the smallest key. + */ + keyid = const_cpu_to_le32(0); + retries = 0; + while (entry) { + next = ntfs_index_next(entry,xsii); + if (next) { + psii = (struct SII*)next; + /* save last key and */ + /* available position */ + keyid = psii->keysecurid; + realign.parts.dataoffsh + = psii->dataoffsh; + realign.parts.dataoffsl + = psii->dataoffsl; + offs = le64_to_cpu(realign.all); + size = le32_to_cpu(psii->datasize); + } + entry = next; + if (!entry && !keyid && !retries) { + /* search failed, retry from smallest key */ + ntfs_index_ctx_reinit(xsii); + found = !ntfs_index_lookup((char*)&keyid, + sizeof(SII_INDEX_KEY), xsii); + if (!found && (errno != ENOENT)) { + ntfs_log_perror("Index $SII is broken"); + } else { + /* restore errno */ + errno = olderrno; + entry = xsii->entry; + } + retries++; + } + } + } + if (!keyid) { + /* + * could not find any entry, before creating the first + * entry, make a double check by making sure size of $SII + * is less than needed for one entry + */ + securid = const_cpu_to_le32(0); + na = ntfs_attr_open(vol->secure_ni,AT_INDEX_ROOT,sii_stream,4); + if (na) { + if ((size_t)na->data_size < sizeof(struct SII)) { + ntfs_log_error("Creating the first security_id\n"); + securid = const_cpu_to_le32(FIRST_SECURITY_ID); + } + ntfs_attr_close(na); + } + if (!securid) { + ntfs_log_error("Error creating a security_id\n"); + errno = EIO; + } + } else { + newkey = le32_to_cpu(keyid) + 1; + securid = cpu_to_le32(newkey); + } + /* + * The security attr has to be written twice 256KB + * apart. This implies that offsets like + * 0x40000*odd_integer must be left available for + * the second copy. So align to next block when + * the last byte overflows on a wrong block. + */ - if ( securid ) - { - gap = ( -size ) & ( ALIGN_SDS_ENTRY - 1 ); - offs += gap + size; - if ( ( offs + attrsz + sizeof( SECURITY_DESCRIPTOR_HEADER ) - 1 ) - & ALIGN_SDS_BLOCK ) - { - offs = ( ( offs + attrsz - + sizeof( SECURITY_DESCRIPTOR_HEADER ) - 1 ) - | ( ALIGN_SDS_BLOCK - 1 ) ) + 1; - } - if ( !( offs & ( ALIGN_SDS_BLOCK - 1 ) ) ) - entersecurity_stuff( vol, offs ); - /* - * now write the security attr to storage : - * first data, then SII, then SDH - * If failure occurs while writing SDS, data will never - * be accessed through indexes, and will be overwritten - * by the next allocated descriptor - * If failure occurs while writing SII, the id has not - * recorded and will be reallocated later - * If failure occurs while writing SDH, the space allocated - * in SDS or SII will not be reused, an inconsistency - * will persist with no significant consequence - */ - if ( entersecurity_data( vol, attr, attrsz, hash, securid, offs, gap ) - || entersecurity_indexes( vol, attrsz, hash, securid, offs ) ) - securid = const_cpu_to_le32( 0 ); - } - /* inode now is dirty, synchronize it all */ - ntfs_index_entry_mark_dirty( vol->secure_xsii ); - ntfs_index_ctx_reinit( vol->secure_xsii ); - ntfs_index_entry_mark_dirty( vol->secure_xsdh ); - ntfs_index_ctx_reinit( vol->secure_xsdh ); - NInoSetDirty( vol->secure_ni ); - if ( ntfs_inode_sync( vol->secure_ni ) ) - ntfs_log_perror( "Could not sync $Secure\n" ); - return ( securid ); + if (securid) { + gap = (-size) & (ALIGN_SDS_ENTRY - 1); + offs += gap + size; + if ((offs + attrsz + sizeof(SECURITY_DESCRIPTOR_HEADER) - 1) + & ALIGN_SDS_BLOCK) { + offs = ((offs + attrsz + + sizeof(SECURITY_DESCRIPTOR_HEADER) - 1) + | (ALIGN_SDS_BLOCK - 1)) + 1; + } + if (!(offs & (ALIGN_SDS_BLOCK - 1))) + entersecurity_stuff(vol, offs); + /* + * now write the security attr to storage : + * first data, then SII, then SDH + * If failure occurs while writing SDS, data will never + * be accessed through indexes, and will be overwritten + * by the next allocated descriptor + * If failure occurs while writing SII, the id has not + * recorded and will be reallocated later + * If failure occurs while writing SDH, the space allocated + * in SDS or SII will not be reused, an inconsistency + * will persist with no significant consequence + */ + if (entersecurity_data(vol, attr, attrsz, hash, securid, offs, gap) + || entersecurity_indexes(vol, attrsz, hash, securid, offs)) + securid = const_cpu_to_le32(0); + } + /* inode now is dirty, synchronize it all */ + ntfs_index_entry_mark_dirty(vol->secure_xsii); + ntfs_index_ctx_reinit(vol->secure_xsii); + ntfs_index_entry_mark_dirty(vol->secure_xsdh); + ntfs_index_ctx_reinit(vol->secure_xsdh); + NInoSetDirty(vol->secure_ni); + if (ntfs_inode_sync(vol->secure_ni)) + ntfs_log_perror("Could not sync $Secure\n"); + return (securid); } /* - * Find a matching security descriptor in $Secure, - * if none, allocate a new id and write the descriptor to storage - * Returns id of entry, or zero if there is a problem. + * Find a matching security descriptor in $Secure, + * if none, allocate a new id and write the descriptor to storage + * Returns id of entry, or zero if there is a problem. * - * important : calls have to be serialized, however no locking is - * needed while fuse is not multithreaded + * important : calls have to be serialized, however no locking is + * needed while fuse is not multithreaded */ -static le32 setsecurityattr( ntfs_volume *vol, - const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz ) +static le32 setsecurityattr(ntfs_volume *vol, + const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz) { - struct SDH *psdh; /* this is an image of index (le) */ - union - { - struct - { - le32 dataoffsl; - le32 dataoffsh; - } parts; - le64 all; - } realign; - BOOL found; - BOOL collision; - size_t size; - size_t rdsize; - s64 offs; - int res; - ntfs_index_context *xsdh; - char *oldattr; - SDH_INDEX_KEY key; - INDEX_ENTRY *entry; - le32 securid; - le32 hash; - int olderrno; + struct SDH *psdh; /* this is an image of index (le) */ + union { + struct { + le32 dataoffsl; + le32 dataoffsh; + } parts; + le64 all; + } realign; + BOOL found; + BOOL collision; + size_t size; + size_t rdsize; + s64 offs; + int res; + ntfs_index_context *xsdh; + char *oldattr; + SDH_INDEX_KEY key; + INDEX_ENTRY *entry; + le32 securid; + le32 hash; + int olderrno; - hash = ntfs_security_hash( attr, attrsz ); - oldattr = ( char* )NULL; - securid = const_cpu_to_le32( 0 ); - res = 0; - xsdh = vol->secure_xsdh; - if ( vol->secure_ni && xsdh && !vol->secure_reentry++ ) - { - ntfs_index_ctx_reinit( xsdh ); - /* - * find the nearest key as (hash,0) - * (do not search for partial key : in case of collision, - * it could return a key which is not the first one which - * collides) - */ - key.hash = hash; - key.security_id = const_cpu_to_le32( 0 ); - olderrno = errno; - found = !ntfs_index_lookup( ( char* ) & key, - sizeof( SDH_INDEX_KEY ), xsdh ); - if ( !found && ( errno != ENOENT ) ) - ntfs_log_perror( "Inconsistency in index $SDH" ); - else - { - /* restore errno to avoid misinterpretation */ - errno = olderrno; - entry = xsdh->entry; - found = FALSE; - /* - * lookup() may return a node with no data, - * if so get next - */ - if ( entry->ie_flags & INDEX_ENTRY_END ) - entry = ntfs_index_next( entry, xsdh ); - do - { - collision = FALSE; - psdh = ( struct SDH* )entry; - if ( psdh ) - size = ( size_t ) le32_to_cpu( psdh->datasize ) - - sizeof( SECURITY_DESCRIPTOR_HEADER ); - else size = 0; - /* if hash is not the same, the key is not present */ - if ( psdh && ( size > 0 ) - && ( psdh->keyhash == hash ) ) - { - /* if hash is the same */ - /* check the whole record */ - realign.parts.dataoffsh = psdh->dataoffsh; - realign.parts.dataoffsl = psdh->dataoffsl; - offs = le64_to_cpu( realign.all ) - + sizeof( SECURITY_DESCRIPTOR_HEADER ); - oldattr = ( char* )ntfs_malloc( size ); - if ( oldattr ) - { - rdsize = ntfs_local_read( - vol->secure_ni, - STREAM_SDS, 4, - oldattr, size, offs ); - found = ( rdsize == size ) - && !memcmp( oldattr, attr, size ); - free( oldattr ); - /* if the records do not compare */ - /* (hash collision), try next one */ - if ( !found ) - { - entry = ntfs_index_next( - entry, xsdh ); - collision = TRUE; - } - } - else - res = ENOMEM; - } - } - while ( collision && entry ); - if ( found ) - securid = psdh->keysecurid; - else - { - if ( res ) - { - errno = res; - securid = const_cpu_to_le32( 0 ); - } - else - { - /* - * no matching key : - * have to build a new one - */ - securid = entersecurityattr( vol, - attr, attrsz, hash ); - } - } - } - } - if ( --vol->secure_reentry ) - ntfs_log_perror( "Reentry error, check no multithreading\n" ); - return ( securid ); + hash = ntfs_security_hash(attr,attrsz); + oldattr = (char*)NULL; + securid = const_cpu_to_le32(0); + res = 0; + xsdh = vol->secure_xsdh; + if (vol->secure_ni && xsdh && !vol->secure_reentry++) { + ntfs_index_ctx_reinit(xsdh); + /* + * find the nearest key as (hash,0) + * (do not search for partial key : in case of collision, + * it could return a key which is not the first one which + * collides) + */ + key.hash = hash; + key.security_id = const_cpu_to_le32(0); + olderrno = errno; + found = !ntfs_index_lookup((char*)&key, + sizeof(SDH_INDEX_KEY), xsdh); + if (!found && (errno != ENOENT)) + ntfs_log_perror("Inconsistency in index $SDH"); + else { + /* restore errno to avoid misinterpretation */ + errno = olderrno; + entry = xsdh->entry; + found = FALSE; + /* + * lookup() may return a node with no data, + * if so get next + */ + if (entry->ie_flags & INDEX_ENTRY_END) + entry = ntfs_index_next(entry,xsdh); + do { + collision = FALSE; + psdh = (struct SDH*)entry; + if (psdh) + size = (size_t) le32_to_cpu(psdh->datasize) + - sizeof(SECURITY_DESCRIPTOR_HEADER); + else size = 0; + /* if hash is not the same, the key is not present */ + if (psdh && (size > 0) + && (psdh->keyhash == hash)) { + /* if hash is the same */ + /* check the whole record */ + realign.parts.dataoffsh = psdh->dataoffsh; + realign.parts.dataoffsl = psdh->dataoffsl; + offs = le64_to_cpu(realign.all) + + sizeof(SECURITY_DESCRIPTOR_HEADER); + oldattr = (char*)ntfs_malloc(size); + if (oldattr) { + rdsize = ntfs_local_read( + vol->secure_ni, + STREAM_SDS, 4, + oldattr, size, offs); + found = (rdsize == size) + && !memcmp(oldattr,attr,size); + free(oldattr); + /* if the records do not compare */ + /* (hash collision), try next one */ + if (!found) { + entry = ntfs_index_next( + entry,xsdh); + collision = TRUE; + } + } else + res = ENOMEM; + } + } while (collision && entry); + if (found) + securid = psdh->keysecurid; + else { + if (res) { + errno = res; + securid = const_cpu_to_le32(0); + } else { + /* + * no matching key : + * have to build a new one + */ + securid = entersecurityattr(vol, + attr, attrsz, hash); + } + } + } + } + if (--vol->secure_reentry) + ntfs_log_perror("Reentry error, check no multithreading\n"); + return (securid); } /* - * Update the security descriptor of a file - * Either as an attribute (complying with pre v3.x NTFS version) - * or, when possible, as an entry in $Secure (for NTFS v3.x) + * Update the security descriptor of a file + * Either as an attribute (complying with pre v3.x NTFS version) + * or, when possible, as an entry in $Secure (for NTFS v3.x) * - * returns 0 if success + * returns 0 if success */ -static int update_secur_descr( ntfs_volume *vol, - char *newattr, ntfs_inode *ni ) +static int update_secur_descr(ntfs_volume *vol, + char *newattr, ntfs_inode *ni) { - int newattrsz; - int written; - int res; - ntfs_attr *na; + int newattrsz; + int written; + int res; + ntfs_attr *na; - newattrsz = ntfs_attr_size( newattr ); + newattrsz = ntfs_attr_size(newattr); #if !FORCE_FORMAT_v1x - if ( ( vol->major_ver < 3 ) || !vol->secure_ni ) - { + if ((vol->major_ver < 3) || !vol->secure_ni) { #endif - /* update for NTFS format v1.x */ + /* update for NTFS format v1.x */ - /* update the old security attribute */ - na = ntfs_attr_open( ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0 ); - if ( na ) - { - /* resize attribute */ - res = ntfs_attr_truncate( na, ( s64 ) newattrsz ); - /* overwrite value */ - if ( !res ) - { - written = ( int )ntfs_attr_pwrite( na, ( s64 ) 0, - ( s64 ) newattrsz, newattr ); - if ( written != newattrsz ) - { - ntfs_log_error( "Failed to update " - "a v1.x security descriptor\n" ); - errno = EIO; - res = -1; - } - } + /* update the old security attribute */ + na = ntfs_attr_open(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0); + if (na) { + /* resize attribute */ + res = ntfs_attr_truncate(na, (s64) newattrsz); + /* overwrite value */ + if (!res) { + written = (int)ntfs_attr_pwrite(na, (s64) 0, + (s64) newattrsz, newattr); + if (written != newattrsz) { + ntfs_log_error("Failed to update " + "a v1.x security descriptor\n"); + errno = EIO; + res = -1; + } + } - ntfs_attr_close( na ); - /* if old security attribute was found, also */ - /* truncate standard information attribute to v1.x */ - /* this is needed when security data is wanted */ - /* as v1.x though volume is formatted for v3.x */ - na = ntfs_attr_open( ni, AT_STANDARD_INFORMATION, - AT_UNNAMED, 0 ); - if ( na ) - { - clear_nino_flag( ni, v3_Extensions ); - /* - * Truncating the record does not sweep extensions - * from copy in memory. Clear security_id to be safe - */ - ni->security_id = const_cpu_to_le32( 0 ); - res = ntfs_attr_truncate( na, ( s64 )48 ); - ntfs_attr_close( na ); - clear_nino_flag( ni, v3_Extensions ); - } - } - else - { - /* - * insert the new security attribute if there - * were none - */ - res = ntfs_attr_add( ni, AT_SECURITY_DESCRIPTOR, - AT_UNNAMED, 0, ( u8* )newattr, - ( s64 ) newattrsz ); - } + ntfs_attr_close(na); + /* if old security attribute was found, also */ + /* truncate standard information attribute to v1.x */ + /* this is needed when security data is wanted */ + /* as v1.x though volume is formatted for v3.x */ + na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION, + AT_UNNAMED, 0); + if (na) { + clear_nino_flag(ni, v3_Extensions); + /* + * Truncating the record does not sweep extensions + * from copy in memory. Clear security_id to be safe + */ + ni->security_id = const_cpu_to_le32(0); + res = ntfs_attr_truncate(na, (s64)48); + ntfs_attr_close(na); + clear_nino_flag(ni, v3_Extensions); + } + } else { + /* + * insert the new security attribute if there + * were none + */ + res = ntfs_attr_add(ni, AT_SECURITY_DESCRIPTOR, + AT_UNNAMED, 0, (u8*)newattr, + (s64) newattrsz); + } #if !FORCE_FORMAT_v1x - } - else - { + } else { - /* update for NTFS format v3.x */ + /* update for NTFS format v3.x */ - le32 securid; + le32 securid; - securid = setsecurityattr( vol, - ( const SECURITY_DESCRIPTOR_RELATIVE* )newattr, - ( s64 )newattrsz ); - if ( securid ) - { - na = ntfs_attr_open( ni, AT_STANDARD_INFORMATION, - AT_UNNAMED, 0 ); - if ( na ) - { - res = 0; - if ( !test_nino_flag( ni, v3_Extensions ) ) - { - /* expand standard information attribute to v3.x */ - res = ntfs_attr_truncate( na, - ( s64 )sizeof( STANDARD_INFORMATION ) ); - ni->owner_id = const_cpu_to_le32( 0 ); - ni->quota_charged = const_cpu_to_le64( 0 ); - ni->usn = const_cpu_to_le64( 0 ); - ntfs_attr_remove( ni, - AT_SECURITY_DESCRIPTOR, - AT_UNNAMED, 0 ); - } - set_nino_flag( ni, v3_Extensions ); - ni->security_id = securid; - ntfs_attr_close( na ); - } - else - { - ntfs_log_error( "Failed to update " - "standard informations\n" ); - errno = EIO; - res = -1; - } - } - else - res = -1; - } + securid = setsecurityattr(vol, + (const SECURITY_DESCRIPTOR_RELATIVE*)newattr, + (s64)newattrsz); + if (securid) { + na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION, + AT_UNNAMED, 0); + if (na) { + res = 0; + if (!test_nino_flag(ni, v3_Extensions)) { + /* expand standard information attribute to v3.x */ + res = ntfs_attr_truncate(na, + (s64)sizeof(STANDARD_INFORMATION)); + ni->owner_id = const_cpu_to_le32(0); + ni->quota_charged = const_cpu_to_le64(0); + ni->usn = const_cpu_to_le64(0); + ntfs_attr_remove(ni, + AT_SECURITY_DESCRIPTOR, + AT_UNNAMED, 0); + } + set_nino_flag(ni, v3_Extensions); + ni->security_id = securid; + ntfs_attr_close(na); + } else { + ntfs_log_error("Failed to update " + "standard informations\n"); + errno = EIO; + res = -1; + } + } else + res = -1; + } #endif - /* mark node as dirty */ - NInoSetDirty( ni ); - return ( res ); + /* mark node as dirty */ + NInoSetDirty(ni); + return (res); } /* - * Upgrade the security descriptor of a file - * This is intended to allow graceful upgrades for files which - * were created in previous versions, with a security attributes - * and no security id. - * + * Upgrade the security descriptor of a file + * This is intended to allow graceful upgrades for files which + * were created in previous versions, with a security attributes + * and no security id. + * * It will allocate a security id and replace the individual - * security attribute by a reference to the global one + * security attribute by a reference to the global one * - * Special files are not upgraded (currently / and files in - * directories /$*) + * Special files are not upgraded (currently / and files in + * directories /$*) * - * Though most code is similar to update_secur_desc() it has - * been kept apart to facilitate the further processing of - * special cases or even to remove it if found dangerous. + * Though most code is similar to update_secur_desc() it has + * been kept apart to facilitate the further processing of + * special cases or even to remove it if found dangerous. * - * returns 0 if success, - * 1 if not upgradable. This is not an error. - * -1 if there is a problem + * returns 0 if success, + * 1 if not upgradable. This is not an error. + * -1 if there is a problem */ -static int upgrade_secur_desc( ntfs_volume *vol, - const char *attr, ntfs_inode *ni ) +static int upgrade_secur_desc(ntfs_volume *vol, + const char *attr, ntfs_inode *ni) { - int attrsz; - int res; - le32 securid; - ntfs_attr *na; + int attrsz; + int res; + le32 securid; + ntfs_attr *na; - /* - * upgrade requires NTFS format v3.x - * also refuse upgrading for special files - * whose number is less than FILE_first_user - */ + /* + * upgrade requires NTFS format v3.x + * also refuse upgrading for special files + * whose number is less than FILE_first_user + */ - if ( ( vol->major_ver >= 3 ) - && ( ni->mft_no >= FILE_first_user ) ) - { - attrsz = ntfs_attr_size( attr ); - securid = setsecurityattr( vol, - ( const SECURITY_DESCRIPTOR_RELATIVE* )attr, - ( s64 )attrsz ); - if ( securid ) - { - na = ntfs_attr_open( ni, AT_STANDARD_INFORMATION, - AT_UNNAMED, 0 ); - if ( na ) - { - res = 0; - /* expand standard information attribute to v3.x */ - res = ntfs_attr_truncate( na, - ( s64 )sizeof( STANDARD_INFORMATION ) ); - ni->owner_id = const_cpu_to_le32( 0 ); - ni->quota_charged = const_cpu_to_le64( 0 ); - ni->usn = const_cpu_to_le64( 0 ); - ntfs_attr_remove( ni, AT_SECURITY_DESCRIPTOR, - AT_UNNAMED, 0 ); - set_nino_flag( ni, v3_Extensions ); - ni->security_id = securid; - ntfs_attr_close( na ); - } - else - { - ntfs_log_error( "Failed to upgrade " - "standard informations\n" ); - errno = EIO; - res = -1; - } - } - else - res = -1; - /* mark node as dirty */ - NInoSetDirty( ni ); - } - else - res = 1; + if ((vol->major_ver >= 3) + && (ni->mft_no >= FILE_first_user)) { + attrsz = ntfs_attr_size(attr); + securid = setsecurityattr(vol, + (const SECURITY_DESCRIPTOR_RELATIVE*)attr, + (s64)attrsz); + if (securid) { + na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION, + AT_UNNAMED, 0); + if (na) { + res = 0; + /* expand standard information attribute to v3.x */ + res = ntfs_attr_truncate(na, + (s64)sizeof(STANDARD_INFORMATION)); + ni->owner_id = const_cpu_to_le32(0); + ni->quota_charged = const_cpu_to_le64(0); + ni->usn = const_cpu_to_le64(0); + ntfs_attr_remove(ni, AT_SECURITY_DESCRIPTOR, + AT_UNNAMED, 0); + set_nino_flag(ni, v3_Extensions); + ni->security_id = securid; + ntfs_attr_close(na); + } else { + ntfs_log_error("Failed to upgrade " + "standard informations\n"); + errno = EIO; + res = -1; + } + } else + res = -1; + /* mark node as dirty */ + NInoSetDirty(ni); + } else + res = 1; - return ( res ); + return (res); } /* - * Optional simplified checking of group membership + * Optional simplified checking of group membership * - * This only takes into account the groups defined in - * /etc/group at initialization time. - * It does not take into account the groups dynamically set by - * setgroups() nor the changes in /etc/group since initialization + * This only takes into account the groups defined in + * /etc/group at initialization time. + * It does not take into account the groups dynamically set by + * setgroups() nor the changes in /etc/group since initialization * - * This optional method could be useful if standard checking - * leads to a performance concern. + * This optional method could be useful if standard checking + * leads to a performance concern. * - * Should not be called for user root, however the group may be root + * Should not be called for user root, however the group may be root * */ -static BOOL staticgroupmember( struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid ) +static BOOL staticgroupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid) { - BOOL ingroup; - int grcnt; - gid_t *groups; - struct MAPPING *user; + BOOL ingroup; + int grcnt; + gid_t *groups; + struct MAPPING *user; - ingroup = FALSE; - if ( uid ) - { - user = scx->mapping[MAPUSERS]; - while ( user && ( ( uid_t )user->xid != uid ) ) - user = user->next; - if ( user ) - { - groups = user->groups; - grcnt = user->grcnt; - while ( ( --grcnt >= 0 ) && ( groups[grcnt] != gid ) ) { } - ingroup = ( grcnt >= 0 ); - } - } - return ( ingroup ); + ingroup = FALSE; + if (uid) { + user = scx->mapping[MAPUSERS]; + while (user && ((uid_t)user->xid != uid)) + user = user->next; + if (user) { + groups = user->groups; + grcnt = user->grcnt; + while ((--grcnt >= 0) && (groups[grcnt] != gid)) { } + ingroup = (grcnt >= 0); + } + } + return (ingroup); } /* - * Check whether current thread owner is member of file group + * Check whether current thread owner is member of file group * - * Should not be called for user root, however the group may be root + * Should not be called for user root, however the group may be root * * As indicated by Miklos Szeredi : * @@ -1329,1896 +1236,1721 @@ static BOOL staticgroupmember( struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gi * contains the same data. */ -static BOOL groupmember( struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid ) +static BOOL groupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid) { - static char key[] = "\nGroups:"; - char buf[BUFSZ+1]; - char filename[64]; - enum { INKEY, INSEP, INNUM, INEND } state; - int fd; - char c; - int matched; - BOOL ismember; - int got; - char *p; - gid_t grp; - pid_t tid; + static char key[] = "\nGroups:"; + char buf[BUFSZ+1]; + char filename[64]; + enum { INKEY, INSEP, INNUM, INEND } state; + int fd; + char c; + int matched; + BOOL ismember; + int got; + char *p; + gid_t grp; + pid_t tid; - if ( scx->vol->secure_flags & ( 1 << SECURITY_STATICGRPS ) ) - ismember = staticgroupmember( scx, uid, gid ); - else - { - ismember = FALSE; /* default return */ - tid = scx->tid; - sprintf( filename, "/proc/%u/task/%u/status", tid, tid ); - fd = open( filename, O_RDONLY ); - if ( fd >= 0 ) - { - got = read( fd, buf, BUFSZ ); - buf[got] = 0; - state = INKEY; - matched = 0; - p = buf; - grp = 0; - /* - * A simple automaton to process lines like - * Groups: 14 500 513 - */ - do - { - c = *p++; - if ( !c ) - { - /* refill buffer */ - got = read( fd, buf, BUFSZ ); - buf[got] = 0; - p = buf; - c = *p++; /* 0 at end of file */ - } - switch ( state ) - { - case INKEY : - if ( key[matched] == c ) - { - if ( !key[++matched] ) - state = INSEP; - } - else if ( key[0] == c ) - matched = 1; - else - matched = 0; - break; - case INSEP : - if ( ( c >= '0' ) && ( c <= '9' ) ) - { - grp = c - '0'; - state = INNUM; - } - else if ( ( c != ' ' ) && ( c != '\t' ) ) - state = INEND; - break; - case INNUM : - if ( ( c >= '0' ) && ( c <= '9' ) ) - grp = grp * 10 + c - '0'; - else - { - ismember = ( grp == gid ); - if ( ( c != ' ' ) && ( c != '\t' ) ) - state = INEND; - else - state = INSEP; - } - default : - break; - } - } - while ( !ismember && c && ( state != INEND ) ); - close( fd ); - if ( !c ) - ntfs_log_error( "No group record found in %s\n", filename ); - } - else - ntfs_log_error( "Could not open %s\n", filename ); - } - return ( ismember ); + if (scx->vol->secure_flags & (1 << SECURITY_STATICGRPS)) + ismember = staticgroupmember(scx, uid, gid); + else { + ismember = FALSE; /* default return */ + tid = scx->tid; + sprintf(filename,"/proc/%u/task/%u/status",tid,tid); + fd = open(filename,O_RDONLY); + if (fd >= 0) { + got = read(fd, buf, BUFSZ); + buf[got] = 0; + state = INKEY; + matched = 0; + p = buf; + grp = 0; + /* + * A simple automaton to process lines like + * Groups: 14 500 513 + */ + do { + c = *p++; + if (!c) { + /* refill buffer */ + got = read(fd, buf, BUFSZ); + buf[got] = 0; + p = buf; + c = *p++; /* 0 at end of file */ + } + switch (state) { + case INKEY : + if (key[matched] == c) { + if (!key[++matched]) + state = INSEP; + } else + if (key[0] == c) + matched = 1; + else + matched = 0; + break; + case INSEP : + if ((c >= '0') && (c <= '9')) { + grp = c - '0'; + state = INNUM; + } else + if ((c != ' ') && (c != '\t')) + state = INEND; + break; + case INNUM : + if ((c >= '0') && (c <= '9')) + grp = grp*10 + c - '0'; + else { + ismember = (grp == gid); + if ((c != ' ') && (c != '\t')) + state = INEND; + else + state = INSEP; + } + default : + break; + } + } while (!ismember && c && (state != INEND)); + close(fd); + if (!c) + ntfs_log_error("No group record found in %s\n",filename); + } else + ntfs_log_error("Could not open %s\n",filename); + } + return (ismember); } /* - * Cacheing is done two-way : - * - from uid, gid and perm to securid (CACHED_SECURID) - * - from a securid to uid, gid and perm (CACHED_PERMISSIONS) + * Cacheing is done two-way : + * - from uid, gid and perm to securid (CACHED_SECURID) + * - from a securid to uid, gid and perm (CACHED_PERMISSIONS) * - * CACHED_SECURID data is kept in a most-recent-first list - * which should not be too long to be efficient. Its optimal - * size is depends on usage and is hard to determine. + * CACHED_SECURID data is kept in a most-recent-first list + * which should not be too long to be efficient. Its optimal + * size is depends on usage and is hard to determine. * - * CACHED_PERMISSIONS data is kept in a two-level indexed array. It - * is optimal at the expense of storage. Use of a most-recent-first - * list would save memory and provide similar performances for - * standard usage, but not for file servers with too many file - * owners + * CACHED_PERMISSIONS data is kept in a two-level indexed array. It + * is optimal at the expense of storage. Use of a most-recent-first + * list would save memory and provide similar performances for + * standard usage, but not for file servers with too many file + * owners * - * CACHED_PERMISSIONS_LEGACY is a special case for CACHED_PERMISSIONS - * for legacy directories which were not allocated a security_id - * it is organized in a most-recent-first list. + * CACHED_PERMISSIONS_LEGACY is a special case for CACHED_PERMISSIONS + * for legacy directories which were not allocated a security_id + * it is organized in a most-recent-first list. * - * In main caches, data is never invalidated, as the meaning of - * a security_id only changes when user mapping is changed, which - * current implies remounting. However returned entries may be - * overwritten at next update, so data has to be copied elsewhere - * before another cache update is made. - * In legacy cache, data has to be invalidated when protection is - * changed. + * In main caches, data is never invalidated, as the meaning of + * a security_id only changes when user mapping is changed, which + * current implies remounting. However returned entries may be + * overwritten at next update, so data has to be copied elsewhere + * before another cache update is made. + * In legacy cache, data has to be invalidated when protection is + * changed. * - * Though the same data may be found in both list, they - * must be kept separately : the interpretation of ACL - * in both direction are approximations which could be non - * reciprocal for some configuration of the user mapping data + * Though the same data may be found in both list, they + * must be kept separately : the interpretation of ACL + * in both direction are approximations which could be non + * reciprocal for some configuration of the user mapping data * - * During the process of recompiling ntfs-3g from a tgz archive, - * security processing added 7.6% to the cpu time used by ntfs-3g - * and 30% if the cache is disabled. + * During the process of recompiling ntfs-3g from a tgz archive, + * security processing added 7.6% to the cpu time used by ntfs-3g + * and 30% if the cache is disabled. */ -static struct PERMISSIONS_CACHE *create_caches( struct SECURITY_CONTEXT *scx, - u32 securindex ) +static struct PERMISSIONS_CACHE *create_caches(struct SECURITY_CONTEXT *scx, + u32 securindex) { - struct PERMISSIONS_CACHE *cache; - unsigned int index1; - unsigned int i; + struct PERMISSIONS_CACHE *cache; + unsigned int index1; + unsigned int i; - cache = ( struct PERMISSIONS_CACHE* )NULL; - /* create the first permissions blocks */ - index1 = securindex >> CACHE_PERMISSIONS_BITS; - cache = ( struct PERMISSIONS_CACHE* ) - ntfs_malloc( sizeof( struct PERMISSIONS_CACHE ) - + index1*sizeof( struct CACHED_PERMISSIONS* ) ); - if ( cache ) - { - cache->head.last = index1; - cache->head.p_reads = 0; - cache->head.p_hits = 0; - cache->head.p_writes = 0; - *scx->pseccache = cache; - for ( i = 0; i <= index1; i++ ) - cache->cachetable[i] - = ( struct CACHED_PERMISSIONS* )NULL; - } - return ( cache ); + cache = (struct PERMISSIONS_CACHE*)NULL; + /* create the first permissions blocks */ + index1 = securindex >> CACHE_PERMISSIONS_BITS; + cache = (struct PERMISSIONS_CACHE*) + ntfs_malloc(sizeof(struct PERMISSIONS_CACHE) + + index1*sizeof(struct CACHED_PERMISSIONS*)); + if (cache) { + cache->head.last = index1; + cache->head.p_reads = 0; + cache->head.p_hits = 0; + cache->head.p_writes = 0; + *scx->pseccache = cache; + for (i=0; i<=index1; i++) + cache->cachetable[i] + = (struct CACHED_PERMISSIONS*)NULL; + } + return (cache); } /* - * Free memory used by caches - * The only purpose is to facilitate the detection of memory leaks + * Free memory used by caches + * The only purpose is to facilitate the detection of memory leaks */ -static void free_caches( struct SECURITY_CONTEXT *scx ) +static void free_caches(struct SECURITY_CONTEXT *scx) { - unsigned int index1; - struct PERMISSIONS_CACHE *pseccache; + unsigned int index1; + struct PERMISSIONS_CACHE *pseccache; - pseccache = *scx->pseccache; - if ( pseccache ) - { - for ( index1 = 0; index1 <= pseccache->head.last; index1++ ) - if ( pseccache->cachetable[index1] ) - { + pseccache = *scx->pseccache; + if (pseccache) { + for (index1=0; index1<=pseccache->head.last; index1++) + if (pseccache->cachetable[index1]) { #if POSIXACLS - struct CACHED_PERMISSIONS *cacheentry; - unsigned int index2; + struct CACHED_PERMISSIONS *cacheentry; + unsigned int index2; - for ( index2 = 0; index2 < ( 1 << CACHE_PERMISSIONS_BITS ); index2++ ) - { - cacheentry = &pseccache->cachetable[index1][index2]; - if ( cacheentry->valid - && cacheentry->pxdesc ) - free( cacheentry->pxdesc ); - } + for (index2=0; index2<(1<< CACHE_PERMISSIONS_BITS); index2++) { + cacheentry = &pseccache->cachetable[index1][index2]; + if (cacheentry->valid + && cacheentry->pxdesc) + free(cacheentry->pxdesc); + } #endif - free( pseccache->cachetable[index1] ); - } - free( pseccache ); - } + free(pseccache->cachetable[index1]); + } + free(pseccache); + } } -static int compare( const struct CACHED_SECURID *cached, - const struct CACHED_SECURID *item ) +static int compare(const struct CACHED_SECURID *cached, + const struct CACHED_SECURID *item) { #if POSIXACLS - size_t csize; - size_t isize; + size_t csize; + size_t isize; - /* only compare data and sizes */ - csize = ( cached->variable ? - sizeof( struct POSIX_ACL ) - + ( ( ( struct POSIX_SECURITY* )cached->variable )->acccnt - + ( ( struct POSIX_SECURITY* )cached->variable )->defcnt ) - * sizeof( struct POSIX_ACE ) : - 0 ); - isize = ( item->variable ? - sizeof( struct POSIX_ACL ) - + ( ( ( struct POSIX_SECURITY* )item->variable )->acccnt - + ( ( struct POSIX_SECURITY* )item->variable )->defcnt ) - * sizeof( struct POSIX_ACE ) : - 0 ); - return ( ( cached->uid != item->uid ) - || ( cached->gid != item->gid ) - || ( cached->dmode != item->dmode ) - || ( csize != isize ) - || ( csize - && isize - && memcmp( &( ( struct POSIX_SECURITY* )cached->variable )->acl, - &( ( struct POSIX_SECURITY* )item->variable )->acl, csize ) ) ); + /* only compare data and sizes */ + csize = (cached->variable ? + sizeof(struct POSIX_ACL) + + (((struct POSIX_SECURITY*)cached->variable)->acccnt + + ((struct POSIX_SECURITY*)cached->variable)->defcnt) + *sizeof(struct POSIX_ACE) : + 0); + isize = (item->variable ? + sizeof(struct POSIX_ACL) + + (((struct POSIX_SECURITY*)item->variable)->acccnt + + ((struct POSIX_SECURITY*)item->variable)->defcnt) + *sizeof(struct POSIX_ACE) : + 0); + return ((cached->uid != item->uid) + || (cached->gid != item->gid) + || (cached->dmode != item->dmode) + || (csize != isize) + || (csize + && isize + && memcmp(&((struct POSIX_SECURITY*)cached->variable)->acl, + &((struct POSIX_SECURITY*)item->variable)->acl, csize))); #else - return ( ( cached->uid != item->uid ) - || ( cached->gid != item->gid ) - || ( cached->dmode != item->dmode ) ); + return ((cached->uid != item->uid) + || (cached->gid != item->gid) + || (cached->dmode != item->dmode)); #endif } -static int leg_compare( const struct CACHED_PERMISSIONS_LEGACY *cached, - const struct CACHED_PERMISSIONS_LEGACY *item ) +static int leg_compare(const struct CACHED_PERMISSIONS_LEGACY *cached, + const struct CACHED_PERMISSIONS_LEGACY *item) { - return ( cached->mft_no != item->mft_no ); + return (cached->mft_no != item->mft_no); } /* - * Resize permission cache table - * do not call unless resizing is needed - * - * If allocation fails, the cache size is not updated - * Lack of memory is not considered as an error, the cache is left - * consistent and errno is not set. + * Resize permission cache table + * do not call unless resizing is needed + * + * If allocation fails, the cache size is not updated + * Lack of memory is not considered as an error, the cache is left + * consistent and errno is not set. */ -static void resize_cache( struct SECURITY_CONTEXT *scx, - u32 securindex ) +static void resize_cache(struct SECURITY_CONTEXT *scx, + u32 securindex) { - struct PERMISSIONS_CACHE *oldcache; - struct PERMISSIONS_CACHE *newcache; - int newcnt; - int oldcnt; - unsigned int index1; - unsigned int i; + struct PERMISSIONS_CACHE *oldcache; + struct PERMISSIONS_CACHE *newcache; + int newcnt; + int oldcnt; + unsigned int index1; + unsigned int i; - oldcache = *scx->pseccache; - index1 = securindex >> CACHE_PERMISSIONS_BITS; - newcnt = index1 + 1; - if ( newcnt <= ( ( CACHE_PERMISSIONS_SIZE - + ( 1 << CACHE_PERMISSIONS_BITS ) - - 1 ) >> CACHE_PERMISSIONS_BITS ) ) - { - /* expand cache beyond current end, do not use realloc() */ - /* to avoid losing data when there is no more memory */ - oldcnt = oldcache->head.last + 1; - newcache = ( struct PERMISSIONS_CACHE* ) - ntfs_malloc( - sizeof( struct PERMISSIONS_CACHE ) - + ( newcnt - 1 ) * sizeof( struct CACHED_PERMISSIONS* ) ); - if ( newcache ) - { - memcpy( newcache, oldcache, - sizeof( struct PERMISSIONS_CACHE ) - + ( oldcnt - 1 )*sizeof( struct CACHED_PERMISSIONS* ) ); - free( oldcache ); - /* mark new entries as not valid */ - for ( i = newcache->head.last + 1; i <= index1; i++ ) - newcache->cachetable[i] - = ( struct CACHED_PERMISSIONS* )NULL; - newcache->head.last = index1; - *scx->pseccache = newcache; - } - } + oldcache = *scx->pseccache; + index1 = securindex >> CACHE_PERMISSIONS_BITS; + newcnt = index1 + 1; + if (newcnt <= ((CACHE_PERMISSIONS_SIZE + + (1 << CACHE_PERMISSIONS_BITS) + - 1) >> CACHE_PERMISSIONS_BITS)) { + /* expand cache beyond current end, do not use realloc() */ + /* to avoid losing data when there is no more memory */ + oldcnt = oldcache->head.last + 1; + newcache = (struct PERMISSIONS_CACHE*) + ntfs_malloc( + sizeof(struct PERMISSIONS_CACHE) + + (newcnt - 1)*sizeof(struct CACHED_PERMISSIONS*)); + if (newcache) { + memcpy(newcache,oldcache, + sizeof(struct PERMISSIONS_CACHE) + + (oldcnt - 1)*sizeof(struct CACHED_PERMISSIONS*)); + free(oldcache); + /* mark new entries as not valid */ + for (i=newcache->head.last+1; i<=index1; i++) + newcache->cachetable[i] + = (struct CACHED_PERMISSIONS*)NULL; + newcache->head.last = index1; + *scx->pseccache = newcache; + } + } } /* - * Enter uid, gid and mode into cache, if possible + * Enter uid, gid and mode into cache, if possible * - * returns the updated or created cache entry, - * or NULL if not possible (typically if there is no - * security id associated) + * returns the updated or created cache entry, + * or NULL if not possible (typically if there is no + * security id associated) */ #if POSIXACLS -static struct CACHED_PERMISSIONS *enter_cache( struct SECURITY_CONTEXT *scx, - ntfs_inode *ni, uid_t uid, gid_t gid, - struct POSIX_SECURITY *pxdesc ) +static struct CACHED_PERMISSIONS *enter_cache(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, uid_t uid, gid_t gid, + struct POSIX_SECURITY *pxdesc) #else -static struct CACHED_PERMISSIONS *enter_cache( struct SECURITY_CONTEXT *scx, - ntfs_inode *ni, uid_t uid, gid_t gid, mode_t mode ) +static struct CACHED_PERMISSIONS *enter_cache(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, uid_t uid, gid_t gid, mode_t mode) #endif { - struct CACHED_PERMISSIONS *cacheentry; - struct CACHED_PERMISSIONS *cacheblock; - struct PERMISSIONS_CACHE *pcache; - u32 securindex; + struct CACHED_PERMISSIONS *cacheentry; + struct CACHED_PERMISSIONS *cacheblock; + struct PERMISSIONS_CACHE *pcache; + u32 securindex; #if POSIXACLS - int pxsize; - struct POSIX_SECURITY *pxcached; + int pxsize; + struct POSIX_SECURITY *pxcached; #endif - unsigned int index1; - unsigned int index2; - int i; + unsigned int index1; + unsigned int index2; + int i; - /* cacheing is only possible if a security_id has been defined */ - if ( test_nino_flag( ni, v3_Extensions ) - && ni->security_id ) - { - /* - * Immediately test the most frequent situation - * where the entry exists - */ - securindex = le32_to_cpu( ni->security_id ); - index1 = securindex >> CACHE_PERMISSIONS_BITS; - index2 = securindex & ( ( 1 << CACHE_PERMISSIONS_BITS ) - 1 ); - pcache = *scx->pseccache; - if ( pcache - && ( pcache->head.last >= index1 ) - && pcache->cachetable[index1] ) - { - cacheentry = &pcache->cachetable[index1][index2]; - cacheentry->uid = uid; - cacheentry->gid = gid; + /* cacheing is only possible if a security_id has been defined */ + if (test_nino_flag(ni, v3_Extensions) + && ni->security_id) { + /* + * Immediately test the most frequent situation + * where the entry exists + */ + securindex = le32_to_cpu(ni->security_id); + index1 = securindex >> CACHE_PERMISSIONS_BITS; + index2 = securindex & ((1 << CACHE_PERMISSIONS_BITS) - 1); + pcache = *scx->pseccache; + if (pcache + && (pcache->head.last >= index1) + && pcache->cachetable[index1]) { + cacheentry = &pcache->cachetable[index1][index2]; + cacheentry->uid = uid; + cacheentry->gid = gid; #if POSIXACLS - if ( cacheentry->valid && cacheentry->pxdesc ) - free( cacheentry->pxdesc ); - if ( pxdesc ) - { - pxsize = sizeof( struct POSIX_SECURITY ) - + ( pxdesc->acccnt + pxdesc->defcnt ) * sizeof( struct POSIX_ACE ); - pxcached = ( struct POSIX_SECURITY* )malloc( pxsize ); - if ( pxcached ) - { - memcpy( pxcached, pxdesc, pxsize ); - cacheentry->pxdesc = pxcached; - } - else - { - cacheentry->valid = 0; - cacheentry = ( struct CACHED_PERMISSIONS* )NULL; - } - cacheentry->mode = pxdesc->mode & 07777; - } - else - cacheentry->pxdesc = ( struct POSIX_SECURITY* )NULL; + if (cacheentry->valid && cacheentry->pxdesc) + free(cacheentry->pxdesc); + if (pxdesc) { + pxsize = sizeof(struct POSIX_SECURITY) + + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE); + pxcached = (struct POSIX_SECURITY*)malloc(pxsize); + if (pxcached) { + memcpy(pxcached, pxdesc, pxsize); + cacheentry->pxdesc = pxcached; + } else { + cacheentry->valid = 0; + cacheentry = (struct CACHED_PERMISSIONS*)NULL; + } + cacheentry->mode = pxdesc->mode & 07777; + } else + cacheentry->pxdesc = (struct POSIX_SECURITY*)NULL; #else - cacheentry->mode = mode & 07777; + cacheentry->mode = mode & 07777; #endif - cacheentry->inh_fileid = const_cpu_to_le32( 0 ); - cacheentry->inh_dirid = const_cpu_to_le32( 0 ); - cacheentry->valid = 1; - pcache->head.p_writes++; - } - else - { - if ( !pcache ) - { - /* create the first cache block */ - pcache = create_caches( scx, securindex ); - } - else - { - if ( index1 > pcache->head.last ) - { - resize_cache( scx, securindex ); - pcache = *scx->pseccache; - } - } - /* allocate block, if cache table was allocated */ - if ( pcache && ( index1 <= pcache->head.last ) ) - { - cacheblock = ( struct CACHED_PERMISSIONS* ) - malloc( sizeof( struct CACHED_PERMISSIONS ) - << CACHE_PERMISSIONS_BITS ); - pcache->cachetable[index1] = cacheblock; - for ( i = 0; i < ( 1 << CACHE_PERMISSIONS_BITS ); i++ ) - cacheblock[i].valid = 0; - cacheentry = &cacheblock[index2]; - if ( cacheentry ) - { - cacheentry->uid = uid; - cacheentry->gid = gid; + cacheentry->inh_fileid = const_cpu_to_le32(0); + cacheentry->inh_dirid = const_cpu_to_le32(0); + cacheentry->valid = 1; + pcache->head.p_writes++; + } else { + if (!pcache) { + /* create the first cache block */ + pcache = create_caches(scx, securindex); + } else { + if (index1 > pcache->head.last) { + resize_cache(scx, securindex); + pcache = *scx->pseccache; + } + } + /* allocate block, if cache table was allocated */ + if (pcache && (index1 <= pcache->head.last)) { + cacheblock = (struct CACHED_PERMISSIONS*) + malloc(sizeof(struct CACHED_PERMISSIONS) + << CACHE_PERMISSIONS_BITS); + pcache->cachetable[index1] = cacheblock; + for (i=0; i<(1 << CACHE_PERMISSIONS_BITS); i++) + cacheblock[i].valid = 0; + cacheentry = &cacheblock[index2]; + if (cacheentry) { + cacheentry->uid = uid; + cacheentry->gid = gid; #if POSIXACLS - if ( pxdesc ) - { - pxsize = sizeof( struct POSIX_SECURITY ) - + ( pxdesc->acccnt + pxdesc->defcnt ) * sizeof( struct POSIX_ACE ); - pxcached = ( struct POSIX_SECURITY* )malloc( pxsize ); - if ( pxcached ) - { - memcpy( pxcached, pxdesc, pxsize ); - cacheentry->pxdesc = pxcached; - } - else - { - cacheentry->valid = 0; - cacheentry = ( struct CACHED_PERMISSIONS* )NULL; - } - cacheentry->mode = pxdesc->mode & 07777; - } - else - cacheentry->pxdesc = ( struct POSIX_SECURITY* )NULL; + if (pxdesc) { + pxsize = sizeof(struct POSIX_SECURITY) + + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE); + pxcached = (struct POSIX_SECURITY*)malloc(pxsize); + if (pxcached) { + memcpy(pxcached, pxdesc, pxsize); + cacheentry->pxdesc = pxcached; + } else { + cacheentry->valid = 0; + cacheentry = (struct CACHED_PERMISSIONS*)NULL; + } + cacheentry->mode = pxdesc->mode & 07777; + } else + cacheentry->pxdesc = (struct POSIX_SECURITY*)NULL; #else - cacheentry->mode = mode & 07777; + cacheentry->mode = mode & 07777; #endif - cacheentry->inh_fileid = const_cpu_to_le32( 0 ); - cacheentry->inh_dirid = const_cpu_to_le32( 0 ); - cacheentry->valid = 1; - pcache->head.p_writes++; - } - } - else - cacheentry = ( struct CACHED_PERMISSIONS* )NULL; - } - } - else - { - cacheentry = ( struct CACHED_PERMISSIONS* )NULL; + cacheentry->inh_fileid = const_cpu_to_le32(0); + cacheentry->inh_dirid = const_cpu_to_le32(0); + cacheentry->valid = 1; + pcache->head.p_writes++; + } + } else + cacheentry = (struct CACHED_PERMISSIONS*)NULL; + } + } else { + cacheentry = (struct CACHED_PERMISSIONS*)NULL; #if CACHE_LEGACY_SIZE - if ( ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ) - { - struct CACHED_PERMISSIONS_LEGACY wanted; - struct CACHED_PERMISSIONS_LEGACY *legacy; + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + struct CACHED_PERMISSIONS_LEGACY wanted; + struct CACHED_PERMISSIONS_LEGACY *legacy; - wanted.perm.uid = uid; - wanted.perm.gid = gid; + wanted.perm.uid = uid; + wanted.perm.gid = gid; #if POSIXACLS - wanted.perm.mode = pxdesc->mode & 07777; - wanted.perm.inh_fileid = const_cpu_to_le32( 0 ); - wanted.perm.inh_dirid = const_cpu_to_le32( 0 ); - wanted.mft_no = ni->mft_no; - wanted.variable = ( void* )pxdesc; - wanted.varsize = sizeof( struct POSIX_SECURITY ) - + ( pxdesc->acccnt + pxdesc->defcnt ) * sizeof( struct POSIX_ACE ); + wanted.perm.mode = pxdesc->mode & 07777; + wanted.perm.inh_fileid = const_cpu_to_le32(0); + wanted.perm.inh_dirid = const_cpu_to_le32(0); + wanted.mft_no = ni->mft_no; + wanted.variable = (void*)pxdesc; + wanted.varsize = sizeof(struct POSIX_SECURITY) + + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE); #else - wanted.perm.mode = mode & 07777; - wanted.perm.inh_fileid = const_cpu_to_le32( 0 ); - wanted.perm.inh_dirid = const_cpu_to_le32( 0 ); - wanted.mft_no = ni->mft_no; - wanted.variable = ( void* )NULL; - wanted.varsize = 0; + wanted.perm.mode = mode & 07777; + wanted.perm.inh_fileid = const_cpu_to_le32(0); + wanted.perm.inh_dirid = const_cpu_to_le32(0); + wanted.mft_no = ni->mft_no; + wanted.variable = (void*)NULL; + wanted.varsize = 0; #endif - legacy = ( struct CACHED_PERMISSIONS_LEGACY* )ntfs_enter_cache( - scx->vol->legacy_cache, GENERIC( &wanted ), - ( cache_compare )leg_compare ); - if ( legacy ) - { - cacheentry = &legacy->perm; + legacy = (struct CACHED_PERMISSIONS_LEGACY*)ntfs_enter_cache( + scx->vol->legacy_cache, GENERIC(&wanted), + (cache_compare)leg_compare); + if (legacy) { + cacheentry = &legacy->perm; #if POSIXACLS - /* - * give direct access to the cached pxdesc - * in the permissions structure - */ - cacheentry->pxdesc = legacy->variable; + /* + * give direct access to the cached pxdesc + * in the permissions structure + */ + cacheentry->pxdesc = legacy->variable; #endif - } - } + } + } #endif - } - return ( cacheentry ); + } + return (cacheentry); } /* - * Fetch owner, group and permission of a file, if cached + * Fetch owner, group and permission of a file, if cached * - * Beware : do not use the returned entry after a cache update : - * the cache may be relocated making the returned entry meaningless + * Beware : do not use the returned entry after a cache update : + * the cache may be relocated making the returned entry meaningless * - * returns the cache entry, or NULL if not available + * returns the cache entry, or NULL if not available */ -static struct CACHED_PERMISSIONS *fetch_cache( struct SECURITY_CONTEXT *scx, - ntfs_inode *ni ) +static struct CACHED_PERMISSIONS *fetch_cache(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni) { - struct CACHED_PERMISSIONS *cacheentry; - struct PERMISSIONS_CACHE *pcache; - u32 securindex; - unsigned int index1; - unsigned int index2; + struct CACHED_PERMISSIONS *cacheentry; + struct PERMISSIONS_CACHE *pcache; + u32 securindex; + unsigned int index1; + unsigned int index2; - /* cacheing is only possible if a security_id has been defined */ - cacheentry = ( struct CACHED_PERMISSIONS* )NULL; - if ( test_nino_flag( ni, v3_Extensions ) - && ( ni->security_id ) ) - { - securindex = le32_to_cpu( ni->security_id ); - index1 = securindex >> CACHE_PERMISSIONS_BITS; - index2 = securindex & ( ( 1 << CACHE_PERMISSIONS_BITS ) - 1 ); - pcache = *scx->pseccache; - if ( pcache - && ( pcache->head.last >= index1 ) - && pcache->cachetable[index1] ) - { - cacheentry = &pcache->cachetable[index1][index2]; - /* reject if entry is not valid */ - if ( !cacheentry->valid ) - cacheentry = ( struct CACHED_PERMISSIONS* )NULL; - else - pcache->head.p_hits++; - if ( pcache ) - pcache->head.p_reads++; - } - } + /* cacheing is only possible if a security_id has been defined */ + cacheentry = (struct CACHED_PERMISSIONS*)NULL; + if (test_nino_flag(ni, v3_Extensions) + && (ni->security_id)) { + securindex = le32_to_cpu(ni->security_id); + index1 = securindex >> CACHE_PERMISSIONS_BITS; + index2 = securindex & ((1 << CACHE_PERMISSIONS_BITS) - 1); + pcache = *scx->pseccache; + if (pcache + && (pcache->head.last >= index1) + && pcache->cachetable[index1]) { + cacheentry = &pcache->cachetable[index1][index2]; + /* reject if entry is not valid */ + if (!cacheentry->valid) + cacheentry = (struct CACHED_PERMISSIONS*)NULL; + else + pcache->head.p_hits++; + if (pcache) + pcache->head.p_reads++; + } + } #if CACHE_LEGACY_SIZE - else - { - cacheentry = ( struct CACHED_PERMISSIONS* )NULL; - if ( ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ) - { - struct CACHED_PERMISSIONS_LEGACY wanted; - struct CACHED_PERMISSIONS_LEGACY *legacy; + else { + cacheentry = (struct CACHED_PERMISSIONS*)NULL; + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + struct CACHED_PERMISSIONS_LEGACY wanted; + struct CACHED_PERMISSIONS_LEGACY *legacy; - wanted.mft_no = ni->mft_no; - wanted.variable = ( void* )NULL; - wanted.varsize = 0; - legacy = ( struct CACHED_PERMISSIONS_LEGACY* )ntfs_fetch_cache( - scx->vol->legacy_cache, GENERIC( &wanted ), - ( cache_compare )leg_compare ); - if ( legacy ) cacheentry = &legacy->perm; - } - } + wanted.mft_no = ni->mft_no; + wanted.variable = (void*)NULL; + wanted.varsize = 0; + legacy = (struct CACHED_PERMISSIONS_LEGACY*)ntfs_fetch_cache( + scx->vol->legacy_cache, GENERIC(&wanted), + (cache_compare)leg_compare); + if (legacy) cacheentry = &legacy->perm; + } + } #endif #if POSIXACLS - if ( cacheentry && !cacheentry->pxdesc ) - { - ntfs_log_error( "No Posix descriptor in cache\n" ); - cacheentry = ( struct CACHED_PERMISSIONS* )NULL; - } + if (cacheentry && !cacheentry->pxdesc) { + ntfs_log_error("No Posix descriptor in cache\n"); + cacheentry = (struct CACHED_PERMISSIONS*)NULL; + } #endif - return ( cacheentry ); + return (cacheentry); } /* - * Retrieve a security attribute from $Secure + * Retrieve a security attribute from $Secure */ -static char *retrievesecurityattr( ntfs_volume *vol, SII_INDEX_KEY id ) +static char *retrievesecurityattr(ntfs_volume *vol, SII_INDEX_KEY id) { - struct SII *psii; - union - { - struct - { - le32 dataoffsl; - le32 dataoffsh; - } parts; - le64 all; - } realign; - int found; - size_t size; - size_t rdsize; - s64 offs; - ntfs_inode *ni; - ntfs_index_context *xsii; - char *securattr; + struct SII *psii; + union { + struct { + le32 dataoffsl; + le32 dataoffsh; + } parts; + le64 all; + } realign; + int found; + size_t size; + size_t rdsize; + s64 offs; + ntfs_inode *ni; + ntfs_index_context *xsii; + char *securattr; - securattr = ( char* )NULL; - ni = vol->secure_ni; - xsii = vol->secure_xsii; - if ( ni && xsii ) - { - ntfs_index_ctx_reinit( xsii ); - found = - !ntfs_index_lookup( ( char* ) & id, - sizeof( SII_INDEX_KEY ), xsii ); - if ( found ) - { - psii = ( struct SII* )xsii->entry; - size = - ( size_t ) le32_to_cpu( psii->datasize ) - - sizeof( SECURITY_DESCRIPTOR_HEADER ); - /* work around bad alignment problem */ - realign.parts.dataoffsh = psii->dataoffsh; - realign.parts.dataoffsl = psii->dataoffsl; - offs = le64_to_cpu( realign.all ) - + sizeof( SECURITY_DESCRIPTOR_HEADER ); + securattr = (char*)NULL; + ni = vol->secure_ni; + xsii = vol->secure_xsii; + if (ni && xsii) { + ntfs_index_ctx_reinit(xsii); + found = + !ntfs_index_lookup((char*)&id, + sizeof(SII_INDEX_KEY), xsii); + if (found) { + psii = (struct SII*)xsii->entry; + size = + (size_t) le32_to_cpu(psii->datasize) + - sizeof(SECURITY_DESCRIPTOR_HEADER); + /* work around bad alignment problem */ + realign.parts.dataoffsh = psii->dataoffsh; + realign.parts.dataoffsl = psii->dataoffsl; + offs = le64_to_cpu(realign.all) + + sizeof(SECURITY_DESCRIPTOR_HEADER); - securattr = ( char* )ntfs_malloc( size ); - if ( securattr ) - { - rdsize = ntfs_local_read( - ni, STREAM_SDS, 4, - securattr, size, offs ); - if ( ( rdsize != size ) - || !ntfs_valid_descr( securattr, - rdsize ) ) - { - /* error to be logged by caller */ - free( securattr ); - securattr = ( char* )NULL; - } - } - } - else if ( errno != ENOENT ) - ntfs_log_perror( "Inconsistency in index $SII" ); - } - if ( !securattr ) - { - ntfs_log_error( "Failed to retrieve a security descriptor\n" ); - errno = EIO; - } - return ( securattr ); + securattr = (char*)ntfs_malloc(size); + if (securattr) { + rdsize = ntfs_local_read( + ni, STREAM_SDS, 4, + securattr, size, offs); + if ((rdsize != size) + || !ntfs_valid_descr(securattr, + rdsize)) { + /* error to be logged by caller */ + free(securattr); + securattr = (char*)NULL; + } + } + } else + if (errno != ENOENT) + ntfs_log_perror("Inconsistency in index $SII"); + } + if (!securattr) { + ntfs_log_error("Failed to retrieve a security descriptor\n"); + errno = EIO; + } + return (securattr); } /* - * Get the security descriptor associated to a file + * Get the security descriptor associated to a file * - * Either : - * - read the security descriptor attribute (v1.x format) - * - or find the descriptor in $Secure:$SDS (v3.x format) + * Either : + * - read the security descriptor attribute (v1.x format) + * - or find the descriptor in $Secure:$SDS (v3.x format) * - * in both case, sanity checks are done on the attribute and - * the descriptor can be assumed safe + * in both case, sanity checks are done on the attribute and + * the descriptor can be assumed safe * - * The returned descriptor is dynamically allocated and has to be freed + * The returned descriptor is dynamically allocated and has to be freed */ -static char *getsecurityattr( ntfs_volume *vol, ntfs_inode *ni ) +static char *getsecurityattr(ntfs_volume *vol, ntfs_inode *ni) { - SII_INDEX_KEY securid; - char *securattr; - s64 readallsz; + SII_INDEX_KEY securid; + char *securattr; + s64 readallsz; - /* - * Warning : in some situations, after fixing by chkdsk, - * v3_Extensions are marked present (long standard informations) - * with a default security descriptor inserted in an - * attribute - */ - if ( test_nino_flag( ni, v3_Extensions ) - && vol->secure_ni && ni->security_id ) - { - /* get v3.x descriptor in $Secure */ - securid.security_id = ni->security_id; - securattr = retrievesecurityattr( vol, securid ); - if ( !securattr ) - ntfs_log_error( "Bad security descriptor for 0x%lx\n", - ( long )le32_to_cpu( ni->security_id ) ); - } - else - { - /* get v1.x security attribute */ - readallsz = 0; - securattr = ntfs_attr_readall( ni, AT_SECURITY_DESCRIPTOR, - AT_UNNAMED, 0, &readallsz ); - if ( securattr && !ntfs_valid_descr( securattr, readallsz ) ) - { - ntfs_log_error( "Bad security descriptor for inode %lld\n", - ( long long )ni->mft_no ); - free( securattr ); - securattr = ( char* )NULL; - } - } - if ( !securattr ) - { - /* - * in some situations, there is no security - * descriptor, and chkdsk does not detect or fix - * anything. This could be a normal situation. - * When this happens, simulate a descriptor with - * minimum rights, so that a real descriptor can - * be created by chown or chmod - */ - ntfs_log_error( "No security descriptor found for inode %lld\n", - ( long long )ni->mft_no ); - securattr = ntfs_build_descr( 0, 0, adminsid, adminsid ); - } - return ( securattr ); + /* + * Warning : in some situations, after fixing by chkdsk, + * v3_Extensions are marked present (long standard informations) + * with a default security descriptor inserted in an + * attribute + */ + if (test_nino_flag(ni, v3_Extensions) + && vol->secure_ni && ni->security_id) { + /* get v3.x descriptor in $Secure */ + securid.security_id = ni->security_id; + securattr = retrievesecurityattr(vol,securid); + if (!securattr) + ntfs_log_error("Bad security descriptor for 0x%lx\n", + (long)le32_to_cpu(ni->security_id)); + } else { + /* get v1.x security attribute */ + readallsz = 0; + securattr = ntfs_attr_readall(ni, AT_SECURITY_DESCRIPTOR, + AT_UNNAMED, 0, &readallsz); + if (securattr && !ntfs_valid_descr(securattr, readallsz)) { + ntfs_log_error("Bad security descriptor for inode %lld\n", + (long long)ni->mft_no); + free(securattr); + securattr = (char*)NULL; + } + } + if (!securattr) { + /* + * in some situations, there is no security + * descriptor, and chkdsk does not detect or fix + * anything. This could be a normal situation. + * When this happens, simulate a descriptor with + * minimum rights, so that a real descriptor can + * be created by chown or chmod + */ + ntfs_log_error("No security descriptor found for inode %lld\n", + (long long)ni->mft_no); + securattr = ntfs_build_descr(0, 0, adminsid, adminsid); + } + return (securattr); } #if POSIXACLS /* - * Determine which access types to a file are allowed - * according to the relation of current process to the file + * Determine which access types to a file are allowed + * according to the relation of current process to the file * - * Do not call if default_permissions is set + * Do not call if default_permissions is set */ -static int access_check_posix( struct SECURITY_CONTEXT *scx, - struct POSIX_SECURITY *pxdesc, mode_t request, - uid_t uid, gid_t gid ) +static int access_check_posix(struct SECURITY_CONTEXT *scx, + struct POSIX_SECURITY *pxdesc, mode_t request, + uid_t uid, gid_t gid) { - struct POSIX_ACE *pxace; - int userperms; - int groupperms; - int mask; - BOOL somegroup; - BOOL needgroups; - mode_t perms; - int i; + struct POSIX_ACE *pxace; + int userperms; + int groupperms; + int mask; + BOOL somegroup; + BOOL needgroups; + mode_t perms; + int i; - perms = pxdesc->mode; - /* owner and root access */ - if ( !scx->uid || ( uid == scx->uid ) ) - { - if ( !scx->uid ) - { - /* root access if owner or other execution */ - if ( perms & 0101 ) - perms = 07777; - else - { - /* root access if some group execution */ - groupperms = 0; - mask = 7; - for ( i = pxdesc->acccnt - 1; i >= 0 ; i-- ) - { - pxace = &pxdesc->acl.ace[i]; - switch ( pxace->tag ) - { - case POSIX_ACL_USER_OBJ : - case POSIX_ACL_GROUP_OBJ : - case POSIX_ACL_GROUP : - groupperms |= pxace->perms; - break; - case POSIX_ACL_MASK : - mask = pxace->perms & 7; - break; - default : - break; - } - } - perms = ( groupperms & mask & 1 ) | 6; - } - } - else - perms &= 07700; - } - else - { - /* - * analyze designated users, get mask - * and identify whether we need to check - * the group memberships. The groups are - * not needed when all groups have the - * same permissions as other for the - * requested modes. - */ - userperms = -1; - groupperms = -1; - needgroups = FALSE; - mask = 7; - for ( i = pxdesc->acccnt - 1; i >= 0 ; i-- ) - { - pxace = &pxdesc->acl.ace[i]; - switch ( pxace->tag ) - { - case POSIX_ACL_USER : - if ( ( uid_t )pxace->id == scx->uid ) - userperms = pxace->perms; - break; - case POSIX_ACL_MASK : - mask = pxace->perms & 7; - break; - case POSIX_ACL_GROUP_OBJ : - case POSIX_ACL_GROUP : - if ( ( ( pxace->perms & mask ) ^ perms ) - & ( request >> 6 ) & 7 ) - needgroups = TRUE; - break; - default : - break; - } - } - /* designated users */ - if ( userperms >= 0 ) - perms = ( perms & 07000 ) + ( userperms & mask ); - else if ( !needgroups ) - perms &= 07007; - else - { - /* owning group */ - if ( !( ~( perms >> 3 ) & request & mask ) - && ( ( gid == scx->gid ) - || groupmember( scx, scx->uid, gid ) ) ) - perms &= 07070; - else - { - /* other groups */ - groupperms = -1; - somegroup = FALSE; - for ( i = pxdesc->acccnt - 1; i >= 0 ; i-- ) - { - pxace = &pxdesc->acl.ace[i]; - if ( ( pxace->tag == POSIX_ACL_GROUP ) - && groupmember( scx, uid, pxace->id ) ) - { - if ( !( ~pxace->perms & request & mask ) ) - groupperms = pxace->perms; - somegroup = TRUE; - } - } - if ( groupperms >= 0 ) - perms = ( perms & 07000 ) + ( groupperms & mask ); - else if ( somegroup ) - perms = 0; - else - perms &= 07007; - } - } - } - return ( perms ); + perms = pxdesc->mode; + /* owner and root access */ + if (!scx->uid || (uid == scx->uid)) { + if (!scx->uid) { + /* root access if owner or other execution */ + if (perms & 0101) + perms = 07777; + else { + /* root access if some group execution */ + groupperms = 0; + mask = 7; + for (i=pxdesc->acccnt-1; i>=0 ; i--) { + pxace = &pxdesc->acl.ace[i]; + switch (pxace->tag) { + case POSIX_ACL_USER_OBJ : + case POSIX_ACL_GROUP_OBJ : + case POSIX_ACL_GROUP : + groupperms |= pxace->perms; + break; + case POSIX_ACL_MASK : + mask = pxace->perms & 7; + break; + default : + break; + } + } + perms = (groupperms & mask & 1) | 6; + } + } else + perms &= 07700; + } else { + /* + * analyze designated users, get mask + * and identify whether we need to check + * the group memberships. The groups are + * not needed when all groups have the + * same permissions as other for the + * requested modes. + */ + userperms = -1; + groupperms = -1; + needgroups = FALSE; + mask = 7; + for (i=pxdesc->acccnt-1; i>=0 ; i--) { + pxace = &pxdesc->acl.ace[i]; + switch (pxace->tag) { + case POSIX_ACL_USER : + if ((uid_t)pxace->id == scx->uid) + userperms = pxace->perms; + break; + case POSIX_ACL_MASK : + mask = pxace->perms & 7; + break; + case POSIX_ACL_GROUP_OBJ : + case POSIX_ACL_GROUP : + if (((pxace->perms & mask) ^ perms) + & (request >> 6) & 7) + needgroups = TRUE; + break; + default : + break; + } + } + /* designated users */ + if (userperms >= 0) + perms = (perms & 07000) + (userperms & mask); + else if (!needgroups) + perms &= 07007; + else { + /* owning group */ + if (!(~(perms >> 3) & request & mask) + && ((gid == scx->gid) + || groupmember(scx, scx->uid, gid))) + perms &= 07070; + else { + /* other groups */ + groupperms = -1; + somegroup = FALSE; + for (i=pxdesc->acccnt-1; i>=0 ; i--) { + pxace = &pxdesc->acl.ace[i]; + if ((pxace->tag == POSIX_ACL_GROUP) + && groupmember(scx, uid, pxace->id)) { + if (!(~pxace->perms & request & mask)) + groupperms = pxace->perms; + somegroup = TRUE; + } + } + if (groupperms >= 0) + perms = (perms & 07000) + (groupperms & mask); + else + if (somegroup) + perms = 0; + else + perms &= 07007; + } + } + } + return (perms); } /* - * Get permissions to access a file - * Takes into account the relation of user to file (owner, group, ...) - * Do no use as mode of the file - * Do no call if default_permissions is set + * Get permissions to access a file + * Takes into account the relation of user to file (owner, group, ...) + * Do no use as mode of the file + * Do no call if default_permissions is set * - * returns -1 if there is a problem + * returns -1 if there is a problem */ -static int ntfs_get_perm( struct SECURITY_CONTEXT *scx, - ntfs_inode * ni, mode_t request ) +static int ntfs_get_perm(struct SECURITY_CONTEXT *scx, + ntfs_inode * ni, mode_t request) { - const SECURITY_DESCRIPTOR_RELATIVE *phead; - const struct CACHED_PERMISSIONS *cached; - char *securattr; - const SID *usid; /* owner of file/directory */ - const SID *gsid; /* group of file/directory */ - uid_t uid; - gid_t gid; - int perm; - BOOL isdir; - struct POSIX_SECURITY *pxdesc; + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const struct CACHED_PERMISSIONS *cached; + char *securattr; + const SID *usid; /* owner of file/directory */ + const SID *gsid; /* group of file/directory */ + uid_t uid; + gid_t gid; + int perm; + BOOL isdir; + struct POSIX_SECURITY *pxdesc; - if ( !scx->mapping[MAPUSERS] ) - perm = 07777; - else - { - /* check whether available in cache */ - cached = fetch_cache( scx, ni ); - if ( cached ) - { - uid = cached->uid; - gid = cached->gid; - perm = access_check_posix( scx, cached->pxdesc, request, uid, gid ); - } - else - { - perm = 0; /* default to no permission */ - isdir = ( ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ) - != const_cpu_to_le16( 0 ); - securattr = getsecurityattr( scx->vol, ni ); - if ( securattr ) - { - phead = ( const SECURITY_DESCRIPTOR_RELATIVE* ) - securattr; - gsid = ( const SID* ) & - securattr[le32_to_cpu( phead->group )]; - gid = ntfs_find_group( scx->mapping[MAPGROUPS], gsid ); + if (!scx->mapping[MAPUSERS]) + perm = 07777; + else { + /* check whether available in cache */ + cached = fetch_cache(scx,ni); + if (cached) { + uid = cached->uid; + gid = cached->gid; + perm = access_check_posix(scx,cached->pxdesc,request,uid,gid); + } else { + perm = 0; /* default to no permission */ + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + != const_cpu_to_le16(0); + securattr = getsecurityattr(scx->vol, ni); + if (securattr) { + phead = (const SECURITY_DESCRIPTOR_RELATIVE*) + securattr; + gsid = (const SID*)& + securattr[le32_to_cpu(phead->group)]; + gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); #if OWNERFROMACL - usid = ntfs_acl_owner( securattr ); - pxdesc = ntfs_build_permissions_posix( scx->mapping, securattr, - usid, gsid, isdir ); - if ( pxdesc ) - perm = pxdesc->mode & 07777; - else - perm = -1; - uid = ntfs_find_user( scx->mapping[MAPUSERS], usid ); + usid = ntfs_acl_owner(securattr); + pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr, + usid, gsid, isdir); + if (pxdesc) + perm = pxdesc->mode & 07777; + else + perm = -1; + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); #else - usid = ( const SID* ) & - securattr[le32_to_cpu( phead->owner )]; - pxdesc = ntfs_build_permissions_posix( scx, securattr, - usid, gsid, isdir ); - if ( pxdesc ) - perm = pxdesc->mode & 07777; - else - perm = -1; - if ( !perm && ntfs_same_sid( usid, adminsid ) ) - { - uid = find_tenant( scx, securattr ); - if ( uid ) - perm = 0700; - } - else - uid = ntfs_find_user( scx->mapping[MAPUSERS], usid ); + usid = (const SID*)& + securattr[le32_to_cpu(phead->owner)]; + pxdesc = ntfs_build_permissions_posix(scx,securattr, + usid, gsid, isdir); + if (pxdesc) + perm = pxdesc->mode & 07777; + else + perm = -1; + if (!perm && ntfs_same_sid(usid, adminsid)) { + uid = find_tenant(scx, securattr); + if (uid) + perm = 0700; + } else + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); #endif - /* - * Create a security id if there were none - * and upgrade option is selected - */ - if ( !test_nino_flag( ni, v3_Extensions ) - && ( perm >= 0 ) - && ( scx->vol->secure_flags - & ( 1 << SECURITY_ADDSECURIDS ) ) ) - { - upgrade_secur_desc( scx->vol, - securattr, ni ); - /* - * fetch owner and group for cacheing - * if there is a securid - */ - } - if ( test_nino_flag( ni, v3_Extensions ) - && ( perm >= 0 ) ) - { - enter_cache( scx, ni, uid, - gid, pxdesc ); - } - if ( pxdesc ) - { - perm = access_check_posix( scx, pxdesc, request, uid, gid ); - free( pxdesc ); - } - free( securattr ); - } - else - { - perm = -1; - uid = gid = 0; - } - } - } - return ( perm ); + /* + * Create a security id if there were none + * and upgrade option is selected + */ + if (!test_nino_flag(ni, v3_Extensions) + && (perm >= 0) + && (scx->vol->secure_flags + & (1 << SECURITY_ADDSECURIDS))) { + upgrade_secur_desc(scx->vol, + securattr, ni); + /* + * fetch owner and group for cacheing + * if there is a securid + */ + } + if (test_nino_flag(ni, v3_Extensions) + && (perm >= 0)) { + enter_cache(scx, ni, uid, + gid, pxdesc); + } + if (pxdesc) { + perm = access_check_posix(scx,pxdesc,request,uid,gid); + free(pxdesc); + } + free(securattr); + } else { + perm = -1; + uid = gid = 0; + } + } + } + return (perm); } /* - * Get a Posix ACL + * Get a Posix ACL * - * returns size or -errno if there is a problem - * if size was too small, no copy is done and errno is not set, - * the caller is expected to issue a new call + * returns size or -errno if there is a problem + * if size was too small, no copy is done and errno is not set, + * the caller is expected to issue a new call */ -int ntfs_get_posix_acl( struct SECURITY_CONTEXT *scx, ntfs_inode *ni, - const char *name, char *value, size_t size ) +int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *name, char *value, size_t size) { - const SECURITY_DESCRIPTOR_RELATIVE *phead; - struct POSIX_SECURITY *pxdesc; - const struct CACHED_PERMISSIONS *cached; - char *securattr; - const SID *usid; /* owner of file/directory */ - const SID *gsid; /* group of file/directory */ - uid_t uid; - gid_t gid; - int perm; - BOOL isdir; - size_t outsize; + const SECURITY_DESCRIPTOR_RELATIVE *phead; + struct POSIX_SECURITY *pxdesc; + const struct CACHED_PERMISSIONS *cached; + char *securattr; + const SID *usid; /* owner of file/directory */ + const SID *gsid; /* group of file/directory */ + uid_t uid; + gid_t gid; + int perm; + BOOL isdir; + size_t outsize; - outsize = 0; /* default to error */ - if ( !scx->mapping[MAPUSERS] ) - errno = ENOTSUP; - else - { - /* check whether available in cache */ - cached = fetch_cache( scx, ni ); - if ( cached ) - pxdesc = cached->pxdesc; - else - { - securattr = getsecurityattr( scx->vol, ni ); - isdir = ( ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ) - != const_cpu_to_le16( 0 ); - if ( securattr ) - { - phead = - ( const SECURITY_DESCRIPTOR_RELATIVE* ) - securattr; - gsid = ( const SID* ) & - securattr[le32_to_cpu( phead->group )]; + outsize = 0; /* default to error */ + if (!scx->mapping[MAPUSERS]) + errno = ENOTSUP; + else { + /* check whether available in cache */ + cached = fetch_cache(scx,ni); + if (cached) + pxdesc = cached->pxdesc; + else { + securattr = getsecurityattr(scx->vol, ni); + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + != const_cpu_to_le16(0); + if (securattr) { + phead = + (const SECURITY_DESCRIPTOR_RELATIVE*) + securattr; + gsid = (const SID*)& + securattr[le32_to_cpu(phead->group)]; #if OWNERFROMACL - usid = ntfs_acl_owner( securattr ); + usid = ntfs_acl_owner(securattr); #else - usid = ( const SID* ) & - securattr[le32_to_cpu( phead->owner )]; + usid = (const SID*)& + securattr[le32_to_cpu(phead->owner)]; #endif - pxdesc = ntfs_build_permissions_posix( scx->mapping, securattr, - usid, gsid, isdir ); + pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr, + usid, gsid, isdir); - /* - * fetch owner and group for cacheing - */ - if ( pxdesc ) - { - perm = pxdesc->mode & 07777; - /* - * Create a security id if there were none - * and upgrade option is selected - */ - if ( !test_nino_flag( ni, v3_Extensions ) - && ( scx->vol->secure_flags - & ( 1 << SECURITY_ADDSECURIDS ) ) ) - { - upgrade_secur_desc( scx->vol, - securattr, ni ); - } + /* + * fetch owner and group for cacheing + */ + if (pxdesc) { + perm = pxdesc->mode & 07777; + /* + * Create a security id if there were none + * and upgrade option is selected + */ + if (!test_nino_flag(ni, v3_Extensions) + && (scx->vol->secure_flags + & (1 << SECURITY_ADDSECURIDS))) { + upgrade_secur_desc(scx->vol, + securattr, ni); + } #if OWNERFROMACL - uid = ntfs_find_user( scx->mapping[MAPUSERS], usid ); + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); #else - if ( !perm && ntfs_same_sid( usid, adminsid ) ) - { - uid = find_tenant( scx, - securattr ); - if ( uid ) - perm = 0700; - } - else - uid = ntfs_find_user( scx->mapping[MAPUSERS], usid ); + if (!perm && ntfs_same_sid(usid, adminsid)) { + uid = find_tenant(scx, + securattr); + if (uid) + perm = 0700; + } else + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); #endif - gid = ntfs_find_group( scx->mapping[MAPGROUPS], gsid ); - if ( pxdesc->tagsset & POSIX_ACL_EXTENSIONS ) - enter_cache( scx, ni, uid, - gid, pxdesc ); - } - free( securattr ); - } - else - pxdesc = ( struct POSIX_SECURITY* )NULL; - } + gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); + if (pxdesc->tagsset & POSIX_ACL_EXTENSIONS) + enter_cache(scx, ni, uid, + gid, pxdesc); + } + free(securattr); + } else + pxdesc = (struct POSIX_SECURITY*)NULL; + } - if ( pxdesc ) - { - if ( ntfs_valid_posix( pxdesc ) ) - { - if ( !strcmp( name, "system.posix_acl_default" ) ) - { - if ( ni->mrec->flags - & MFT_RECORD_IS_DIRECTORY ) - outsize = sizeof( struct POSIX_ACL ) - + pxdesc->defcnt * sizeof( struct POSIX_ACE ); - else - { - /* - * getting default ACL from plain file : - * return EACCES if size > 0 as - * indicated in the man, but return ok - * if size == 0, so that ls does not - * display an error - */ - if ( size > 0 ) - { - outsize = 0; - errno = EACCES; - } - else - outsize = sizeof( struct POSIX_ACL ); - } - if ( outsize && ( outsize <= size ) ) - { - memcpy( value, &pxdesc->acl, sizeof( struct POSIX_ACL ) ); - memcpy( &value[sizeof( struct POSIX_ACL )], - &pxdesc->acl.ace[pxdesc->firstdef], - outsize - sizeof( struct POSIX_ACL ) ); - } - } - else - { - outsize = sizeof( struct POSIX_ACL ) - + pxdesc->acccnt * sizeof( struct POSIX_ACE ); - if ( outsize <= size ) - memcpy( value, &pxdesc->acl, outsize ); - } - } - else - { - outsize = 0; - errno = EIO; - ntfs_log_error( "Invalid Posix ACL built\n" ); - } - if ( !cached ) - free( pxdesc ); - } - else - outsize = 0; - } - return ( outsize ? ( int )outsize : -errno ); + if (pxdesc) { + if (ntfs_valid_posix(pxdesc)) { + if (!strcmp(name,"system.posix_acl_default")) { + if (ni->mrec->flags + & MFT_RECORD_IS_DIRECTORY) + outsize = sizeof(struct POSIX_ACL) + + pxdesc->defcnt*sizeof(struct POSIX_ACE); + else { + /* + * getting default ACL from plain file : + * return EACCES if size > 0 as + * indicated in the man, but return ok + * if size == 0, so that ls does not + * display an error + */ + if (size > 0) { + outsize = 0; + errno = EACCES; + } else + outsize = sizeof(struct POSIX_ACL); + } + if (outsize && (outsize <= size)) { + memcpy(value,&pxdesc->acl,sizeof(struct POSIX_ACL)); + memcpy(&value[sizeof(struct POSIX_ACL)], + &pxdesc->acl.ace[pxdesc->firstdef], + outsize-sizeof(struct POSIX_ACL)); + } + } else { + outsize = sizeof(struct POSIX_ACL) + + pxdesc->acccnt*sizeof(struct POSIX_ACE); + if (outsize <= size) + memcpy(value,&pxdesc->acl,outsize); + } + } else { + outsize = 0; + errno = EIO; + ntfs_log_error("Invalid Posix ACL built\n"); + } + if (!cached) + free(pxdesc); + } else + outsize = 0; + } + return (outsize ? (int)outsize : -errno); } #else /* POSIXACLS */ /* - * Get permissions to access a file - * Takes into account the relation of user to file (owner, group, ...) - * Do no use as mode of the file + * Get permissions to access a file + * Takes into account the relation of user to file (owner, group, ...) + * Do no use as mode of the file * - * returns -1 if there is a problem + * returns -1 if there is a problem */ -static int ntfs_get_perm( struct SECURITY_CONTEXT *scx, - ntfs_inode *ni, mode_t request ) +static int ntfs_get_perm(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, mode_t request) { - const SECURITY_DESCRIPTOR_RELATIVE *phead; - const struct CACHED_PERMISSIONS *cached; - char *securattr; - const SID *usid; /* owner of file/directory */ - const SID *gsid; /* group of file/directory */ - BOOL isdir; - uid_t uid; - gid_t gid; - int perm; + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const struct CACHED_PERMISSIONS *cached; + char *securattr; + const SID *usid; /* owner of file/directory */ + const SID *gsid; /* group of file/directory */ + BOOL isdir; + uid_t uid; + gid_t gid; + int perm; - if ( !scx->mapping[MAPUSERS] || ( !scx->uid && !( request & S_IEXEC ) ) ) - perm = 07777; - else - { - /* check whether available in cache */ - cached = fetch_cache( scx, ni ); - if ( cached ) - { - perm = cached->mode; - uid = cached->uid; - gid = cached->gid; - } - else - { - perm = 0; /* default to no permission */ - isdir = ( ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ) - != const_cpu_to_le16( 0 ); - securattr = getsecurityattr( scx->vol, ni ); - if ( securattr ) - { - phead = ( const SECURITY_DESCRIPTOR_RELATIVE* ) - securattr; - gsid = ( const SID* ) & - securattr[le32_to_cpu( phead->group )]; - gid = ntfs_find_group( scx->mapping[MAPGROUPS], gsid ); + if (!scx->mapping[MAPUSERS] || (!scx->uid && !(request & S_IEXEC))) + perm = 07777; + else { + /* check whether available in cache */ + cached = fetch_cache(scx,ni); + if (cached) { + perm = cached->mode; + uid = cached->uid; + gid = cached->gid; + } else { + perm = 0; /* default to no permission */ + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + != const_cpu_to_le16(0); + securattr = getsecurityattr(scx->vol, ni); + if (securattr) { + phead = (const SECURITY_DESCRIPTOR_RELATIVE*) + securattr; + gsid = (const SID*)& + securattr[le32_to_cpu(phead->group)]; + gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); #if OWNERFROMACL - usid = ntfs_acl_owner( securattr ); - perm = ntfs_build_permissions( securattr, - usid, gsid, isdir ); - uid = ntfs_find_user( scx->mapping[MAPUSERS], usid ); + usid = ntfs_acl_owner(securattr); + perm = ntfs_build_permissions(securattr, + usid, gsid, isdir); + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); #else -usid = ( const SID* ) & - securattr[le32_to_cpu( phead->owner )]; -perm = ntfs_build_permissions( securattr, - usid, gsid, isdir ); -if ( !perm && ntfs_same_sid( usid, adminsid ) ) -{ - uid = find_tenant( scx, securattr ); - if ( uid ) - perm = 0700; -} -else - uid = ntfs_find_user( scx->mapping[MAPUSERS], usid ); + usid = (const SID*)& + securattr[le32_to_cpu(phead->owner)]; + perm = ntfs_build_permissions(securattr, + usid, gsid, isdir); + if (!perm && ntfs_same_sid(usid, adminsid)) { + uid = find_tenant(scx, securattr); + if (uid) + perm = 0700; + } else + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); #endif - /* - * Create a security id if there were none - * and upgrade option is selected - */ - if ( !test_nino_flag( ni, v3_Extensions ) - && ( perm >= 0 ) - && ( scx->vol->secure_flags - & ( 1 << SECURITY_ADDSECURIDS ) ) ) - { - upgrade_secur_desc( scx->vol, - securattr, ni ); - /* - * fetch owner and group for cacheing - * if there is a securid - */ - } - if ( test_nino_flag( ni, v3_Extensions ) - && ( perm >= 0 ) ) - { - enter_cache( scx, ni, uid, - gid, perm ); - } - free( securattr ); - } - else - { - perm = -1; - uid = gid = 0; - } - } - if ( perm >= 0 ) - { - if ( !scx->uid ) - { - /* root access and execution */ - if ( perm & 0111 ) - perm = 07777; - else - perm = 0; - } - else if ( uid == scx->uid ) - perm &= 07700; - else - /* - * avoid checking group membership - * when the requested perms for group - * are the same as perms for other - */ - if ( ( gid == scx->gid ) - || ( ( ( ( perm >> 3 ) ^ perm ) - & ( request >> 6 ) & 7 ) - && groupmember( scx, scx->uid, gid ) ) ) - perm &= 07070; - else - perm &= 07007; - } - } - return ( perm ); + /* + * Create a security id if there were none + * and upgrade option is selected + */ + if (!test_nino_flag(ni, v3_Extensions) + && (perm >= 0) + && (scx->vol->secure_flags + & (1 << SECURITY_ADDSECURIDS))) { + upgrade_secur_desc(scx->vol, + securattr, ni); + /* + * fetch owner and group for cacheing + * if there is a securid + */ + } + if (test_nino_flag(ni, v3_Extensions) + && (perm >= 0)) { + enter_cache(scx, ni, uid, + gid, perm); + } + free(securattr); + } else { + perm = -1; + uid = gid = 0; + } + } + if (perm >= 0) { + if (!scx->uid) { + /* root access and execution */ + if (perm & 0111) + perm = 07777; + else + perm = 0; + } else + if (uid == scx->uid) + perm &= 07700; + else + /* + * avoid checking group membership + * when the requested perms for group + * are the same as perms for other + */ + if ((gid == scx->gid) + || ((((perm >> 3) ^ perm) + & (request >> 6) & 7) + && groupmember(scx, scx->uid, gid))) + perm &= 07070; + else + perm &= 07007; + } + } + return (perm); } #endif /* POSIXACLS */ /* - * Get an NTFS ACL + * Get an NTFS ACL * - * Returns size or -errno if there is a problem - * if size was too small, no copy is done and errno is not set, - * the caller is expected to issue a new call + * Returns size or -errno if there is a problem + * if size was too small, no copy is done and errno is not set, + * the caller is expected to issue a new call */ -int ntfs_get_ntfs_acl( struct SECURITY_CONTEXT *scx, ntfs_inode *ni, - char *value, size_t size ) +int ntfs_get_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + char *value, size_t size) { - char *securattr; - size_t outsize; + char *securattr; + size_t outsize; - outsize = 0; /* default to no data and no error */ - securattr = getsecurityattr( scx->vol, ni ); - if ( securattr ) - { - outsize = ntfs_attr_size( securattr ); - if ( outsize <= size ) - { - memcpy( value, securattr, outsize ); - } - free( securattr ); - } - return ( outsize ? ( int )outsize : -errno ); + outsize = 0; /* default to no data and no error */ + securattr = getsecurityattr(scx->vol, ni); + if (securattr) { + outsize = ntfs_attr_size(securattr); + if (outsize <= size) { + memcpy(value,securattr,outsize); + } + free(securattr); + } + return (outsize ? (int)outsize : -errno); } /* - * Get owner, group and permissions in an stat structure - * returns permissions, or -1 if there is a problem + * Get owner, group and permissions in an stat structure + * returns permissions, or -1 if there is a problem */ -int ntfs_get_owner_mode( struct SECURITY_CONTEXT *scx, - ntfs_inode * ni, struct stat *stbuf ) +int ntfs_get_owner_mode(struct SECURITY_CONTEXT *scx, + ntfs_inode * ni, struct stat *stbuf) { - const SECURITY_DESCRIPTOR_RELATIVE *phead; - char *securattr; - const SID *usid; /* owner of file/directory */ - const SID *gsid; /* group of file/directory */ - const struct CACHED_PERMISSIONS *cached; - int perm; - BOOL isdir; + const SECURITY_DESCRIPTOR_RELATIVE *phead; + char *securattr; + const SID *usid; /* owner of file/directory */ + const SID *gsid; /* group of file/directory */ + const struct CACHED_PERMISSIONS *cached; + int perm; + BOOL isdir; #if POSIXACLS - struct POSIX_SECURITY *pxdesc; + struct POSIX_SECURITY *pxdesc; #endif - if ( !scx->mapping[MAPUSERS] ) - perm = 07777; - else - { - /* check whether available in cache */ - cached = fetch_cache( scx, ni ); - if ( cached ) - { - perm = cached->mode; - stbuf->st_uid = cached->uid; - stbuf->st_gid = cached->gid; - stbuf->st_mode = ( stbuf->st_mode & ~07777 ) + perm; - } - else - { - perm = -1; /* default to error */ - isdir = ( ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ) - != const_cpu_to_le16( 0 ); - securattr = getsecurityattr( scx->vol, ni ); - if ( securattr ) - { - phead = - ( const SECURITY_DESCRIPTOR_RELATIVE* ) - securattr; - gsid = ( const SID* ) & - securattr[le32_to_cpu( phead->group )]; + if (!scx->mapping[MAPUSERS]) + perm = 07777; + else { + /* check whether available in cache */ + cached = fetch_cache(scx,ni); + if (cached) { + perm = cached->mode; + stbuf->st_uid = cached->uid; + stbuf->st_gid = cached->gid; + stbuf->st_mode = (stbuf->st_mode & ~07777) + perm; + } else { + perm = -1; /* default to error */ + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + != const_cpu_to_le16(0); + securattr = getsecurityattr(scx->vol, ni); + if (securattr) { + phead = + (const SECURITY_DESCRIPTOR_RELATIVE*) + securattr; + gsid = (const SID*)& + securattr[le32_to_cpu(phead->group)]; #if OWNERFROMACL - usid = ntfs_acl_owner( securattr ); + usid = ntfs_acl_owner(securattr); #else - usid = ( const SID* ) & - securattr[le32_to_cpu( phead->owner )]; + usid = (const SID*)& + securattr[le32_to_cpu(phead->owner)]; #endif #if POSIXACLS - pxdesc = ntfs_build_permissions_posix( scx->mapping, securattr, - usid, gsid, isdir ); - if ( pxdesc ) - perm = pxdesc->mode & 07777; - else - perm = -1; + pxdesc = ntfs_build_permissions_posix(scx->mapping, securattr, + usid, gsid, isdir); + if (pxdesc) + perm = pxdesc->mode & 07777; + else + perm = -1; #else - perm = ntfs_build_permissions( securattr, - usid, gsid, isdir ); + perm = ntfs_build_permissions(securattr, + usid, gsid, isdir); #endif - /* - * fetch owner and group for cacheing - */ - if ( perm >= 0 ) - { - /* - * Create a security id if there were none - * and upgrade option is selected - */ - if ( !test_nino_flag( ni, v3_Extensions ) - && ( scx->vol->secure_flags - & ( 1 << SECURITY_ADDSECURIDS ) ) ) - { - upgrade_secur_desc( scx->vol, - securattr, ni ); - } + /* + * fetch owner and group for cacheing + */ + if (perm >= 0) { + /* + * Create a security id if there were none + * and upgrade option is selected + */ + if (!test_nino_flag(ni, v3_Extensions) + && (scx->vol->secure_flags + & (1 << SECURITY_ADDSECURIDS))) { + upgrade_secur_desc(scx->vol, + securattr, ni); + } #if OWNERFROMACL - stbuf->st_uid = ntfs_find_user( scx->mapping[MAPUSERS], usid ); + stbuf->st_uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); #else - if ( !perm && ntfs_same_sid( usid, adminsid ) ) - { - stbuf->st_uid = - find_tenant( scx, - securattr ); - if ( stbuf->st_uid ) - perm = 0700; - } - else - stbuf->st_uid = ntfs_find_user( scx->mapping[MAPUSERS], usid ); + if (!perm && ntfs_same_sid(usid, adminsid)) { + stbuf->st_uid = + find_tenant(scx, + securattr); + if (stbuf->st_uid) + perm = 0700; + } else + stbuf->st_uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); #endif - stbuf->st_gid = ntfs_find_group( scx->mapping[MAPGROUPS], gsid ); - stbuf->st_mode = - ( stbuf->st_mode & ~07777 ) + perm; + stbuf->st_gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); + stbuf->st_mode = + (stbuf->st_mode & ~07777) + perm; #if POSIXACLS - enter_cache( scx, ni, stbuf->st_uid, - stbuf->st_gid, pxdesc ); - free( pxdesc ); + enter_cache(scx, ni, stbuf->st_uid, + stbuf->st_gid, pxdesc); + free(pxdesc); #else - enter_cache( scx, ni, stbuf->st_uid, - stbuf->st_gid, perm ); + enter_cache(scx, ni, stbuf->st_uid, + stbuf->st_gid, perm); #endif - } - free( securattr ); - } - } - } - return ( perm ); + } + free(securattr); + } + } + } + return (perm); } #if POSIXACLS /* - * Get the base for a Posix inheritance and - * build an inherited Posix descriptor + * Get the base for a Posix inheritance and + * build an inherited Posix descriptor */ -static struct POSIX_SECURITY *inherit_posix( struct SECURITY_CONTEXT *scx, - ntfs_inode *dir_ni, mode_t mode, BOOL isdir ) +static struct POSIX_SECURITY *inherit_posix(struct SECURITY_CONTEXT *scx, + ntfs_inode *dir_ni, mode_t mode, BOOL isdir) { - const struct CACHED_PERMISSIONS *cached; - const SECURITY_DESCRIPTOR_RELATIVE *phead; - struct POSIX_SECURITY *pxdesc; - struct POSIX_SECURITY *pydesc; - char *securattr; - const SID *usid; - const SID *gsid; - uid_t uid; - gid_t gid; + const struct CACHED_PERMISSIONS *cached; + const SECURITY_DESCRIPTOR_RELATIVE *phead; + struct POSIX_SECURITY *pxdesc; + struct POSIX_SECURITY *pydesc; + char *securattr; + const SID *usid; + const SID *gsid; + uid_t uid; + gid_t gid; - pydesc = ( struct POSIX_SECURITY* )NULL; - /* check whether parent directory is available in cache */ - cached = fetch_cache( scx, dir_ni ); - if ( cached ) - { - uid = cached->uid; - gid = cached->gid; - pxdesc = cached->pxdesc; - if ( pxdesc ) - { - pydesc = ntfs_build_inherited_posix( pxdesc, mode, - scx->umask, isdir ); - } - } - else - { - securattr = getsecurityattr( scx->vol, dir_ni ); - if ( securattr ) - { - phead = ( const SECURITY_DESCRIPTOR_RELATIVE* ) - securattr; - gsid = ( const SID* ) & - securattr[le32_to_cpu( phead->group )]; - gid = ntfs_find_group( scx->mapping[MAPGROUPS], gsid ); + pydesc = (struct POSIX_SECURITY*)NULL; + /* check whether parent directory is available in cache */ + cached = fetch_cache(scx,dir_ni); + if (cached) { + uid = cached->uid; + gid = cached->gid; + pxdesc = cached->pxdesc; + if (pxdesc) { + pydesc = ntfs_build_inherited_posix(pxdesc,mode, + scx->umask,isdir); + } + } else { + securattr = getsecurityattr(scx->vol, dir_ni); + if (securattr) { + phead = (const SECURITY_DESCRIPTOR_RELATIVE*) + securattr; + gsid = (const SID*)& + securattr[le32_to_cpu(phead->group)]; + gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); #if OWNERFROMACL - usid = ntfs_acl_owner( securattr ); - pxdesc = ntfs_build_permissions_posix( scx->mapping, securattr, - usid, gsid, TRUE ); - uid = ntfs_find_user( scx->mapping[MAPUSERS], usid ); + usid = ntfs_acl_owner(securattr); + pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr, + usid, gsid, TRUE); + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); #else - usid = ( const SID* ) & - securattr[le32_to_cpu( phead->owner )]; - pxdesc = ntfs_build_permissions_posix( scx->mapping, securattr, - usid, gsid, TRUE ); - if ( pxdesc && ntfs_same_sid( usid, adminsid ) ) - { - uid = find_tenant( scx, securattr ); - } - else - uid = ntfs_find_user( scx->mapping[MAPUSERS], usid ); + usid = (const SID*)& + securattr[le32_to_cpu(phead->owner)]; + pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr, + usid, gsid, TRUE); + if (pxdesc && ntfs_same_sid(usid, adminsid)) { + uid = find_tenant(scx, securattr); + } else + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); #endif - if ( pxdesc ) - { - /* - * Create a security id if there were none - * and upgrade option is selected - */ - if ( !test_nino_flag( dir_ni, v3_Extensions ) - && ( scx->vol->secure_flags - & ( 1 << SECURITY_ADDSECURIDS ) ) ) - { - upgrade_secur_desc( scx->vol, - securattr, dir_ni ); - /* - * fetch owner and group for cacheing - * if there is a securid - */ - } - if ( test_nino_flag( dir_ni, v3_Extensions ) ) - { - enter_cache( scx, dir_ni, uid, - gid, pxdesc ); - } - pydesc = ntfs_build_inherited_posix( pxdesc, - mode, scx->umask, isdir ); - free( pxdesc ); - } - free( securattr ); - } - } - return ( pydesc ); + if (pxdesc) { + /* + * Create a security id if there were none + * and upgrade option is selected + */ + if (!test_nino_flag(dir_ni, v3_Extensions) + && (scx->vol->secure_flags + & (1 << SECURITY_ADDSECURIDS))) { + upgrade_secur_desc(scx->vol, + securattr, dir_ni); + /* + * fetch owner and group for cacheing + * if there is a securid + */ + } + if (test_nino_flag(dir_ni, v3_Extensions)) { + enter_cache(scx, dir_ni, uid, + gid, pxdesc); + } + pydesc = ntfs_build_inherited_posix(pxdesc, + mode, scx->umask, isdir); + free(pxdesc); + } + free(securattr); + } + } + return (pydesc); } /* - * Allocate a security_id for a file being created - * - * Returns zero if not possible (NTFS v3.x required) + * Allocate a security_id for a file being created + * + * Returns zero if not possible (NTFS v3.x required) */ -le32 ntfs_alloc_securid( struct SECURITY_CONTEXT *scx, - uid_t uid, gid_t gid, ntfs_inode *dir_ni, - mode_t mode, BOOL isdir ) +le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx, + uid_t uid, gid_t gid, ntfs_inode *dir_ni, + mode_t mode, BOOL isdir) { #if !FORCE_FORMAT_v1x - const struct CACHED_SECURID *cached; - struct CACHED_SECURID wanted; - struct POSIX_SECURITY *pxdesc; - char *newattr; - int newattrsz; - const SID *usid; - const SID *gsid; - BIGSID defusid; - BIGSID defgsid; - le32 securid; + const struct CACHED_SECURID *cached; + struct CACHED_SECURID wanted; + struct POSIX_SECURITY *pxdesc; + char *newattr; + int newattrsz; + const SID *usid; + const SID *gsid; + BIGSID defusid; + BIGSID defgsid; + le32 securid; #endif - securid = const_cpu_to_le32( 0 ); + securid = const_cpu_to_le32(0); #if !FORCE_FORMAT_v1x - pxdesc = inherit_posix( scx, dir_ni, mode, isdir ); - if ( pxdesc ) - { - /* check whether target securid is known in cache */ + pxdesc = inherit_posix(scx, dir_ni, mode, isdir); + if (pxdesc) { + /* check whether target securid is known in cache */ - wanted.uid = uid; - wanted.gid = gid; - wanted.dmode = pxdesc->mode & mode & 07777; - if ( isdir ) wanted.dmode |= 0x10000; - wanted.variable = ( void* )pxdesc; - wanted.varsize = sizeof( struct POSIX_SECURITY ) - + ( pxdesc->acccnt + pxdesc->defcnt ) * sizeof( struct POSIX_ACE ); - cached = ( const struct CACHED_SECURID* )ntfs_fetch_cache( - scx->vol->securid_cache, GENERIC( &wanted ), - ( cache_compare )compare ); - /* quite simple, if we are lucky */ - if ( cached ) - securid = cached->securid; + wanted.uid = uid; + wanted.gid = gid; + wanted.dmode = pxdesc->mode & mode & 07777; + if (isdir) wanted.dmode |= 0x10000; + wanted.variable = (void*)pxdesc; + wanted.varsize = sizeof(struct POSIX_SECURITY) + + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE); + cached = (const struct CACHED_SECURID*)ntfs_fetch_cache( + scx->vol->securid_cache, GENERIC(&wanted), + (cache_compare)compare); + /* quite simple, if we are lucky */ + if (cached) + securid = cached->securid; - /* not in cache : make sure we can create ids */ + /* not in cache : make sure we can create ids */ - if ( !cached && ( scx->vol->major_ver >= 3 ) ) - { - usid = ntfs_find_usid( scx->mapping[MAPUSERS], uid, ( SID* ) & defusid ); - gsid = ntfs_find_gsid( scx->mapping[MAPGROUPS], gid, ( SID* ) & defgsid ); - if ( !usid || !gsid ) - { - ntfs_log_error( "File created by an unmapped user/group %d/%d\n", - ( int )uid, ( int )gid ); - usid = gsid = adminsid; - } - newattr = ntfs_build_descr_posix( scx->mapping, pxdesc, - isdir, usid, gsid ); - if ( newattr ) - { - newattrsz = ntfs_attr_size( newattr ); - securid = setsecurityattr( scx->vol, - ( const SECURITY_DESCRIPTOR_RELATIVE* )newattr, - newattrsz ); - if ( securid ) - { - /* update cache, for subsequent use */ - wanted.securid = securid; - ntfs_enter_cache( scx->vol->securid_cache, - GENERIC( &wanted ), - ( cache_compare )compare ); - } - free( newattr ); - } - else - { - /* - * could not build new security attribute - * errno set by ntfs_build_descr() - */ - } - } - free( pxdesc ); - } + if (!cached && (scx->vol->major_ver >= 3)) { + usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid); + gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid); + if (!usid || !gsid) { + ntfs_log_error("File created by an unmapped user/group %d/%d\n", + (int)uid, (int)gid); + usid = gsid = adminsid; + } + newattr = ntfs_build_descr_posix(scx->mapping, pxdesc, + isdir, usid, gsid); + if (newattr) { + newattrsz = ntfs_attr_size(newattr); + securid = setsecurityattr(scx->vol, + (const SECURITY_DESCRIPTOR_RELATIVE*)newattr, + newattrsz); + if (securid) { + /* update cache, for subsequent use */ + wanted.securid = securid; + ntfs_enter_cache(scx->vol->securid_cache, + GENERIC(&wanted), + (cache_compare)compare); + } + free(newattr); + } else { + /* + * could not build new security attribute + * errno set by ntfs_build_descr() + */ + } + } + free(pxdesc); + } #endif - return ( securid ); + return (securid); } /* - * Apply Posix inheritance to a newly created file - * (for NTFS 1.x only : no securid) + * Apply Posix inheritance to a newly created file + * (for NTFS 1.x only : no securid) */ -int ntfs_set_inherited_posix( struct SECURITY_CONTEXT *scx, - ntfs_inode *ni, uid_t uid, gid_t gid, - ntfs_inode *dir_ni, mode_t mode ) +int ntfs_set_inherited_posix(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, uid_t uid, gid_t gid, + ntfs_inode *dir_ni, mode_t mode) { - struct POSIX_SECURITY *pxdesc; - char *newattr; - const SID *usid; - const SID *gsid; - BIGSID defusid; - BIGSID defgsid; - BOOL isdir; - int res; + struct POSIX_SECURITY *pxdesc; + char *newattr; + const SID *usid; + const SID *gsid; + BIGSID defusid; + BIGSID defgsid; + BOOL isdir; + int res; - res = -1; - isdir = ( ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ) != const_cpu_to_le16( 0 ); - pxdesc = inherit_posix( scx, dir_ni, mode, isdir ); - if ( pxdesc ) - { - usid = ntfs_find_usid( scx->mapping[MAPUSERS], uid, ( SID* ) & defusid ); - gsid = ntfs_find_gsid( scx->mapping[MAPGROUPS], gid, ( SID* ) & defgsid ); - if ( !usid || !gsid ) - { - ntfs_log_error( "File created by an unmapped user/group %d/%d\n", - ( int )uid, ( int )gid ); - usid = gsid = adminsid; - } - newattr = ntfs_build_descr_posix( scx->mapping, pxdesc, - isdir, usid, gsid ); - if ( newattr ) - { - /* Adjust Windows read-only flag */ - res = update_secur_descr( scx->vol, newattr, ni ); - if ( !res && !isdir ) - { - if ( mode & S_IWUSR ) - ni->flags &= ~FILE_ATTR_READONLY; - else - ni->flags |= FILE_ATTR_READONLY; - } + res = -1; + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0); + pxdesc = inherit_posix(scx, dir_ni, mode, isdir); + if (pxdesc) { + usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid); + gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid); + if (!usid || !gsid) { + ntfs_log_error("File created by an unmapped user/group %d/%d\n", + (int)uid, (int)gid); + usid = gsid = adminsid; + } + newattr = ntfs_build_descr_posix(scx->mapping, pxdesc, + isdir, usid, gsid); + if (newattr) { + /* Adjust Windows read-only flag */ + res = update_secur_descr(scx->vol, newattr, ni); + if (!res && !isdir) { + if (mode & S_IWUSR) + ni->flags &= ~FILE_ATTR_READONLY; + else + ni->flags |= FILE_ATTR_READONLY; + } #if CACHE_LEGACY_SIZE - /* also invalidate legacy cache */ - if ( isdir && !ni->security_id ) - { - struct CACHED_PERMISSIONS_LEGACY legacy; + /* also invalidate legacy cache */ + if (isdir && !ni->security_id) { + struct CACHED_PERMISSIONS_LEGACY legacy; - legacy.mft_no = ni->mft_no; - legacy.variable = pxdesc; - legacy.varsize = sizeof( struct POSIX_SECURITY ) - + ( pxdesc->acccnt + pxdesc->defcnt ) * sizeof( struct POSIX_ACE ); - ntfs_invalidate_cache( scx->vol->legacy_cache, - GENERIC( &legacy ), - ( cache_compare )leg_compare, 0 ); - } + legacy.mft_no = ni->mft_no; + legacy.variable = pxdesc; + legacy.varsize = sizeof(struct POSIX_SECURITY) + + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE); + ntfs_invalidate_cache(scx->vol->legacy_cache, + GENERIC(&legacy), + (cache_compare)leg_compare,0); + } #endif - free( newattr ); + free(newattr); - } - else - { - /* - * could not build new security attribute - * errno set by ntfs_build_descr() - */ - } - } - return ( res ); + } else { + /* + * could not build new security attribute + * errno set by ntfs_build_descr() + */ + } + } + return (res); } #else -le32 ntfs_alloc_securid( struct SECURITY_CONTEXT *scx, - uid_t uid, gid_t gid, mode_t mode, BOOL isdir ) +le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx, + uid_t uid, gid_t gid, mode_t mode, BOOL isdir) { #if !FORCE_FORMAT_v1x - const struct CACHED_SECURID *cached; - struct CACHED_SECURID wanted; - char *newattr; - int newattrsz; - const SID *usid; - const SID *gsid; - BIGSID defusid; - BIGSID defgsid; - le32 securid; + const struct CACHED_SECURID *cached; + struct CACHED_SECURID wanted; + char *newattr; + int newattrsz; + const SID *usid; + const SID *gsid; + BIGSID defusid; + BIGSID defgsid; + le32 securid; #endif - securid = const_cpu_to_le32( 0 ); + securid = const_cpu_to_le32(0); #if !FORCE_FORMAT_v1x - /* check whether target securid is known in cache */ + /* check whether target securid is known in cache */ - wanted.uid = uid; - wanted.gid = gid; - wanted.dmode = mode & 07777; - if ( isdir ) wanted.dmode |= 0x10000; - wanted.variable = ( void* )NULL; - wanted.varsize = 0; - cached = ( const struct CACHED_SECURID* )ntfs_fetch_cache( - scx->vol->securid_cache, GENERIC( &wanted ), - ( cache_compare )compare ); - /* quite simple, if we are lucky */ - if ( cached ) - securid = cached->securid; + wanted.uid = uid; + wanted.gid = gid; + wanted.dmode = mode & 07777; + if (isdir) wanted.dmode |= 0x10000; + wanted.variable = (void*)NULL; + wanted.varsize = 0; + cached = (const struct CACHED_SECURID*)ntfs_fetch_cache( + scx->vol->securid_cache, GENERIC(&wanted), + (cache_compare)compare); + /* quite simple, if we are lucky */ + if (cached) + securid = cached->securid; - /* not in cache : make sure we can create ids */ + /* not in cache : make sure we can create ids */ - if ( !cached && ( scx->vol->major_ver >= 3 ) ) - { - usid = ntfs_find_usid( scx->mapping[MAPUSERS], uid, ( SID* ) & defusid ); - gsid = ntfs_find_gsid( scx->mapping[MAPGROUPS], gid, ( SID* ) & defgsid ); - if ( !usid || !gsid ) - { - ntfs_log_error( "File created by an unmapped user/group %d/%d\n", - ( int )uid, ( int )gid ); - usid = gsid = adminsid; - } - newattr = ntfs_build_descr( mode, isdir, usid, gsid ); - if ( newattr ) - { - newattrsz = ntfs_attr_size( newattr ); - securid = setsecurityattr( scx->vol, - ( const SECURITY_DESCRIPTOR_RELATIVE* )newattr, - newattrsz ); - if ( securid ) - { - /* update cache, for subsequent use */ - wanted.securid = securid; - ntfs_enter_cache( scx->vol->securid_cache, - GENERIC( &wanted ), - ( cache_compare )compare ); - } - free( newattr ); - } - else - { - /* - * could not build new security attribute - * errno set by ntfs_build_descr() - */ - } - } + if (!cached && (scx->vol->major_ver >= 3)) { + usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid); + gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid); + if (!usid || !gsid) { + ntfs_log_error("File created by an unmapped user/group %d/%d\n", + (int)uid, (int)gid); + usid = gsid = adminsid; + } + newattr = ntfs_build_descr(mode, isdir, usid, gsid); + if (newattr) { + newattrsz = ntfs_attr_size(newattr); + securid = setsecurityattr(scx->vol, + (const SECURITY_DESCRIPTOR_RELATIVE*)newattr, + newattrsz); + if (securid) { + /* update cache, for subsequent use */ + wanted.securid = securid; + ntfs_enter_cache(scx->vol->securid_cache, + GENERIC(&wanted), + (cache_compare)compare); + } + free(newattr); + } else { + /* + * could not build new security attribute + * errno set by ntfs_build_descr() + */ + } + } #endif - return ( securid ); + return (securid); } #endif /* - * Update ownership and mode of a file, reusing an existing - * security descriptor when possible - * - * Returns zero if successful + * Update ownership and mode of a file, reusing an existing + * security descriptor when possible + * + * Returns zero if successful */ #if POSIXACLS -int ntfs_set_owner_mode( struct SECURITY_CONTEXT *scx, ntfs_inode *ni, - uid_t uid, gid_t gid, mode_t mode, - struct POSIX_SECURITY *pxdesc ) +int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + uid_t uid, gid_t gid, mode_t mode, + struct POSIX_SECURITY *pxdesc) #else -int ntfs_set_owner_mode( struct SECURITY_CONTEXT *scx, ntfs_inode *ni, - uid_t uid, gid_t gid, mode_t mode ) +int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + uid_t uid, gid_t gid, mode_t mode) #endif { - int res; - const struct CACHED_SECURID *cached; - struct CACHED_SECURID wanted; - char *newattr; - const SID *usid; - const SID *gsid; - BIGSID defusid; - BIGSID defgsid; - BOOL isdir; + int res; + const struct CACHED_SECURID *cached; + struct CACHED_SECURID wanted; + char *newattr; + const SID *usid; + const SID *gsid; + BIGSID defusid; + BIGSID defgsid; + BOOL isdir; - res = 0; + res = 0; - /* check whether target securid is known in cache */ + /* check whether target securid is known in cache */ - isdir = ( ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ) != const_cpu_to_le16( 0 ); - wanted.uid = uid; - wanted.gid = gid; - wanted.dmode = mode & 07777; - if ( isdir ) wanted.dmode |= 0x10000; + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0); + wanted.uid = uid; + wanted.gid = gid; + wanted.dmode = mode & 07777; + if (isdir) wanted.dmode |= 0x10000; #if POSIXACLS - wanted.variable = ( void* )pxdesc; - if ( pxdesc ) - wanted.varsize = sizeof( struct POSIX_SECURITY ) - + ( pxdesc->acccnt + pxdesc->defcnt ) * sizeof( struct POSIX_ACE ); - else - wanted.varsize = 0; + wanted.variable = (void*)pxdesc; + if (pxdesc) + wanted.varsize = sizeof(struct POSIX_SECURITY) + + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE); + else + wanted.varsize = 0; #else - wanted.variable = ( void* )NULL; - wanted.varsize = 0; + wanted.variable = (void*)NULL; + wanted.varsize = 0; #endif - if ( test_nino_flag( ni, v3_Extensions ) ) - { - cached = ( const struct CACHED_SECURID* )ntfs_fetch_cache( - scx->vol->securid_cache, GENERIC( &wanted ), - ( cache_compare )compare ); - /* quite simple, if we are lucky */ - if ( cached ) - { - ni->security_id = cached->securid; - NInoSetDirty( ni ); - } - } - else cached = ( struct CACHED_SECURID* )NULL; + if (test_nino_flag(ni, v3_Extensions)) { + cached = (const struct CACHED_SECURID*)ntfs_fetch_cache( + scx->vol->securid_cache, GENERIC(&wanted), + (cache_compare)compare); + /* quite simple, if we are lucky */ + if (cached) { + ni->security_id = cached->securid; + NInoSetDirty(ni); + } + } else cached = (struct CACHED_SECURID*)NULL; - if ( !cached ) - { - /* - * Do not use usid and gsid from former attributes, - * but recompute them to get repeatable results - * which can be kept in cache. - */ - usid = ntfs_find_usid( scx->mapping[MAPUSERS], uid, ( SID* ) & defusid ); - gsid = ntfs_find_gsid( scx->mapping[MAPGROUPS], gid, ( SID* ) & defgsid ); - if ( !usid || !gsid ) - { - ntfs_log_error( "File made owned by an unmapped user/group %d/%d\n", - uid, gid ); - usid = gsid = adminsid; - } + if (!cached) { + /* + * Do not use usid and gsid from former attributes, + * but recompute them to get repeatable results + * which can be kept in cache. + */ + usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid); + gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid); + if (!usid || !gsid) { + ntfs_log_error("File made owned by an unmapped user/group %d/%d\n", + uid, gid); + usid = gsid = adminsid; + } #if POSIXACLS - if ( pxdesc ) - newattr = ntfs_build_descr_posix( scx->mapping, pxdesc, - isdir, usid, gsid ); - else - newattr = ntfs_build_descr( mode, - isdir, usid, gsid ); + if (pxdesc) + newattr = ntfs_build_descr_posix(scx->mapping, pxdesc, + isdir, usid, gsid); + else + newattr = ntfs_build_descr(mode, + isdir, usid, gsid); #else - newattr = ntfs_build_descr( mode, - isdir, usid, gsid ); + newattr = ntfs_build_descr(mode, + isdir, usid, gsid); #endif - if ( newattr ) - { - res = update_secur_descr( scx->vol, newattr, ni ); - if ( !res ) - { - /* adjust Windows read-only flag */ - if ( !isdir ) - { - if ( mode & S_IWUSR ) - ni->flags &= ~FILE_ATTR_READONLY; - else - ni->flags |= FILE_ATTR_READONLY; - NInoFileNameSetDirty( ni ); - } - /* update cache, for subsequent use */ - if ( test_nino_flag( ni, v3_Extensions ) ) - { - wanted.securid = ni->security_id; - ntfs_enter_cache( scx->vol->securid_cache, - GENERIC( &wanted ), - ( cache_compare )compare ); - } + if (newattr) { + res = update_secur_descr(scx->vol, newattr, ni); + if (!res) { + /* adjust Windows read-only flag */ + if (!isdir) { + if (mode & S_IWUSR) + ni->flags &= ~FILE_ATTR_READONLY; + else + ni->flags |= FILE_ATTR_READONLY; + NInoFileNameSetDirty(ni); + } + /* update cache, for subsequent use */ + if (test_nino_flag(ni, v3_Extensions)) { + wanted.securid = ni->security_id; + ntfs_enter_cache(scx->vol->securid_cache, + GENERIC(&wanted), + (cache_compare)compare); + } #if CACHE_LEGACY_SIZE - /* also invalidate legacy cache */ - if ( isdir && !ni->security_id ) - { - struct CACHED_PERMISSIONS_LEGACY legacy; + /* also invalidate legacy cache */ + if (isdir && !ni->security_id) { + struct CACHED_PERMISSIONS_LEGACY legacy; - legacy.mft_no = ni->mft_no; + legacy.mft_no = ni->mft_no; #if POSIXACLS - legacy.variable = wanted.variable; - legacy.varsize = wanted.varsize; + legacy.variable = wanted.variable; + legacy.varsize = wanted.varsize; #else - legacy.variable = ( void* )NULL; - legacy.varsize = 0; + legacy.variable = (void*)NULL; + legacy.varsize = 0; #endif - ntfs_invalidate_cache( scx->vol->legacy_cache, - GENERIC( &legacy ), - ( cache_compare )leg_compare, 0 ); - } + ntfs_invalidate_cache(scx->vol->legacy_cache, + GENERIC(&legacy), + (cache_compare)leg_compare,0); + } #endif - } - free( newattr ); - } - else - { - /* - * could not build new security attribute - * errno set by ntfs_build_descr() - */ - res = -1; - } - } - return ( res ); + } + free(newattr); + } else { + /* + * could not build new security attribute + * errno set by ntfs_build_descr() + */ + res = -1; + } + } + return (res); } /* - * Check whether user has ownership rights on a file + * Check whether user has ownership rights on a file * - * Returns TRUE if allowed - * if not, errno tells why + * Returns TRUE if allowed + * if not, errno tells why */ -BOOL ntfs_allowed_as_owner( struct SECURITY_CONTEXT *scx, ntfs_inode *ni ) +BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni) { - const struct CACHED_PERMISSIONS *cached; - char *oldattr; - const SID *usid; - uid_t processuid; - uid_t uid; - BOOL gotowner; - int allowed; + const struct CACHED_PERMISSIONS *cached; + char *oldattr; + const SID *usid; + uid_t processuid; + uid_t uid; + BOOL gotowner; + int allowed; - processuid = scx->uid; - /* TODO : use CAP_FOWNER process capability */ - /* - * Always allow for root - * Also always allow if no mapping has been defined - */ - if ( !scx->mapping[MAPUSERS] || !processuid ) - allowed = TRUE; - else - { - gotowner = FALSE; /* default */ - /* get the owner, either from cache or from old attribute */ - cached = fetch_cache( scx, ni ); - if ( cached ) - { - uid = cached->uid; - gotowner = TRUE; - } - else - { - oldattr = getsecurityattr( scx->vol, ni ); - if ( oldattr ) - { + processuid = scx->uid; +/* TODO : use CAP_FOWNER process capability */ + /* + * Always allow for root + * Also always allow if no mapping has been defined + */ + if (!scx->mapping[MAPUSERS] || !processuid) + allowed = TRUE; + else { + gotowner = FALSE; /* default */ + /* get the owner, either from cache or from old attribute */ + cached = fetch_cache(scx, ni); + if (cached) { + uid = cached->uid; + gotowner = TRUE; + } else { + oldattr = getsecurityattr(scx->vol, ni); + if (oldattr) { #if OWNERFROMACL - usid = ntfs_acl_owner( oldattr ); + usid = ntfs_acl_owner(oldattr); #else - const SECURITY_DESCRIPTOR_RELATIVE *phead; + const SECURITY_DESCRIPTOR_RELATIVE *phead; - phead = ( const SECURITY_DESCRIPTOR_RELATIVE* ) - oldattr; - usid = ( const SID* ) & oldattr - [le32_to_cpu( phead->owner )]; + phead = (const SECURITY_DESCRIPTOR_RELATIVE*) + oldattr; + usid = (const SID*)&oldattr + [le32_to_cpu(phead->owner)]; #endif - uid = ntfs_find_user( scx->mapping[MAPUSERS], - usid ); - gotowner = TRUE; - free( oldattr ); - } - } - allowed = FALSE; - if ( gotowner ) - { - /* TODO : use CAP_FOWNER process capability */ - if ( !processuid || ( processuid == uid ) ) - allowed = TRUE; - else - errno = EPERM; - } - } - return ( allowed ); + uid = ntfs_find_user(scx->mapping[MAPUSERS], + usid); + gotowner = TRUE; + free(oldattr); + } + } + allowed = FALSE; + if (gotowner) { +/* TODO : use CAP_FOWNER process capability */ + if (!processuid || (processuid == uid)) + allowed = TRUE; + else + errno = EPERM; + } + } + return (allowed); } #ifdef HAVE_SETXATTR /* extended attributes interface required */ @@ -3226,1195 +2958,1089 @@ BOOL ntfs_allowed_as_owner( struct SECURITY_CONTEXT *scx, ntfs_inode *ni ) #if POSIXACLS /* - * Set a new access or default Posix ACL to a file - * (or remove ACL if no input data) - * Validity of input data is checked after merging + * Set a new access or default Posix ACL to a file + * (or remove ACL if no input data) + * Validity of input data is checked after merging * - * Returns 0, or -1 if there is a problem which errno describes + * Returns 0, or -1 if there is a problem which errno describes */ -int ntfs_set_posix_acl( struct SECURITY_CONTEXT *scx, ntfs_inode *ni, - const char *name, const char *value, size_t size, - int flags ) +int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *name, const char *value, size_t size, + int flags) { - const SECURITY_DESCRIPTOR_RELATIVE *phead; - const struct CACHED_PERMISSIONS *cached; - char *oldattr; - uid_t processuid; - const SID *usid; - const SID *gsid; - uid_t uid; - uid_t gid; - int res; - mode_t mode; - BOOL isdir; - BOOL deflt; - BOOL exist; - int count; - struct POSIX_SECURITY *oldpxdesc; - struct POSIX_SECURITY *newpxdesc; + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const struct CACHED_PERMISSIONS *cached; + char *oldattr; + uid_t processuid; + const SID *usid; + const SID *gsid; + uid_t uid; + uid_t gid; + int res; + mode_t mode; + BOOL isdir; + BOOL deflt; + BOOL exist; + int count; + struct POSIX_SECURITY *oldpxdesc; + struct POSIX_SECURITY *newpxdesc; - /* get the current pxsec, either from cache or from old attribute */ - res = -1; - deflt = !strcmp( name, "system.posix_acl_default" ); - if ( size ) - count = ( size - sizeof( struct POSIX_ACL ) ) / sizeof( struct POSIX_ACE ); - else - count = 0; - isdir = ( ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ) != const_cpu_to_le16( 0 ); - newpxdesc = ( struct POSIX_SECURITY* )NULL; - if ( !deflt || isdir || !size ) - { - cached = fetch_cache( scx, ni ); - if ( cached ) - { - uid = cached->uid; - gid = cached->gid; - oldpxdesc = cached->pxdesc; - if ( oldpxdesc ) - { - mode = oldpxdesc->mode; - newpxdesc = ntfs_replace_acl( oldpxdesc, - ( const struct POSIX_ACL* )value, count, deflt ); - } - } - else - { - oldattr = getsecurityattr( scx->vol, ni ); - if ( oldattr ) - { - phead = ( const SECURITY_DESCRIPTOR_RELATIVE* )oldattr; + /* get the current pxsec, either from cache or from old attribute */ + res = -1; + deflt = !strcmp(name,"system.posix_acl_default"); + if (size) + count = (size - sizeof(struct POSIX_ACL)) / sizeof(struct POSIX_ACE); + else + count = 0; + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0); + newpxdesc = (struct POSIX_SECURITY*)NULL; + if (!deflt || isdir || !size) { + cached = fetch_cache(scx, ni); + if (cached) { + uid = cached->uid; + gid = cached->gid; + oldpxdesc = cached->pxdesc; + if (oldpxdesc) { + mode = oldpxdesc->mode; + newpxdesc = ntfs_replace_acl(oldpxdesc, + (const struct POSIX_ACL*)value,count,deflt); + } + } else { + oldattr = getsecurityattr(scx->vol, ni); + if (oldattr) { + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr; #if OWNERFROMACL - usid = ntfs_acl_owner( oldattr ); + usid = ntfs_acl_owner(oldattr); #else - usid = ( const SID* ) & oldattr[le32_to_cpu( phead->owner )]; + usid = (const SID*)&oldattr[le32_to_cpu(phead->owner)]; #endif - gsid = ( const SID* ) & oldattr[le32_to_cpu( phead->group )]; - uid = ntfs_find_user( scx->mapping[MAPUSERS], usid ); - gid = ntfs_find_group( scx->mapping[MAPGROUPS], gsid ); - oldpxdesc = ntfs_build_permissions_posix( scx->mapping, - oldattr, usid, gsid, isdir ); - if ( oldpxdesc ) - { - if ( deflt ) - exist = oldpxdesc->defcnt > 0; - else - exist = oldpxdesc->acccnt > 3; - if ( ( exist && ( flags & XATTR_CREATE ) ) - || ( !exist && ( flags & XATTR_REPLACE ) ) ) - { - errno = ( exist ? EEXIST : ENODATA ); - } - else - { - mode = oldpxdesc->mode; - newpxdesc = ntfs_replace_acl( oldpxdesc, - ( const struct POSIX_ACL* )value, count, deflt ); - } - free( oldpxdesc ); - } - free( oldattr ); - } - } - } - else - errno = EINVAL; + gsid = (const SID*)&oldattr[le32_to_cpu(phead->group)]; + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); + gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); + oldpxdesc = ntfs_build_permissions_posix(scx->mapping, + oldattr, usid, gsid, isdir); + if (oldpxdesc) { + if (deflt) + exist = oldpxdesc->defcnt > 0; + else + exist = oldpxdesc->acccnt > 3; + if ((exist && (flags & XATTR_CREATE)) + || (!exist && (flags & XATTR_REPLACE))) { + errno = (exist ? EEXIST : ENODATA); + } else { + mode = oldpxdesc->mode; + newpxdesc = ntfs_replace_acl(oldpxdesc, + (const struct POSIX_ACL*)value,count,deflt); + } + free(oldpxdesc); + } + free(oldattr); + } + } + } else + errno = EINVAL; - if ( newpxdesc ) - { - processuid = scx->uid; - /* TODO : use CAP_FOWNER process capability */ - if ( !processuid || ( uid == processuid ) ) - { - /* - * clear setgid if file group does - * not match process group - */ - if ( processuid && ( gid != scx->gid ) - && !groupmember( scx, scx->uid, gid ) ) - { - newpxdesc->mode &= ~S_ISGID; - } - res = ntfs_set_owner_mode( scx, ni, uid, gid, - newpxdesc->mode, newpxdesc ); - } - else - errno = EPERM; - free( newpxdesc ); - } - return ( res ? -1 : 0 ); + if (newpxdesc) { + processuid = scx->uid; +/* TODO : use CAP_FOWNER process capability */ + if (!processuid || (uid == processuid)) { + /* + * clear setgid if file group does + * not match process group + */ + if (processuid && (gid != scx->gid) + && !groupmember(scx, scx->uid, gid)) { + newpxdesc->mode &= ~S_ISGID; + } + res = ntfs_set_owner_mode(scx, ni, uid, gid, + newpxdesc->mode, newpxdesc); + } else + errno = EPERM; + free(newpxdesc); + } + return (res ? -1 : 0); } /* - * Remove a default Posix ACL from a file + * Remove a default Posix ACL from a file * - * Returns 0, or -1 if there is a problem which errno describes + * Returns 0, or -1 if there is a problem which errno describes */ -int ntfs_remove_posix_acl( struct SECURITY_CONTEXT *scx, ntfs_inode *ni, - const char *name ) +int ntfs_remove_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *name) { - return ( ntfs_set_posix_acl( scx, ni, name, - ( const char* )NULL, 0, 0 ) ); + return (ntfs_set_posix_acl(scx, ni, name, + (const char*)NULL, 0, 0)); } #endif /* - * Set a new NTFS ACL to a file + * Set a new NTFS ACL to a file * - * Returns 0, or -1 if there is a problem + * Returns 0, or -1 if there is a problem */ -int ntfs_set_ntfs_acl( struct SECURITY_CONTEXT *scx, ntfs_inode *ni, - const char *value, size_t size, int flags ) +int ntfs_set_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *value, size_t size, int flags) { - char *attr; - int res; + char *attr; + int res; - res = -1; - if ( ( size > 0 ) - && !( flags & XATTR_CREATE ) - && ntfs_valid_descr( value, size ) - && ( ntfs_attr_size( value ) == size ) ) - { - /* need copying in order to write */ - attr = ( char* )ntfs_malloc( size ); - if ( attr ) - { - memcpy( attr, value, size ); - res = update_secur_descr( scx->vol, attr, ni ); - /* - * No need to invalidate standard caches : - * the relation between a securid and - * the associated protection is unchanged, - * only the relation between a file and - * its securid and protection is changed. - */ + res = -1; + if ((size > 0) + && !(flags & XATTR_CREATE) + && ntfs_valid_descr(value,size) + && (ntfs_attr_size(value) == size)) { + /* need copying in order to write */ + attr = (char*)ntfs_malloc(size); + if (attr) { + memcpy(attr,value,size); + res = update_secur_descr(scx->vol, attr, ni); + /* + * No need to invalidate standard caches : + * the relation between a securid and + * the associated protection is unchanged, + * only the relation between a file and + * its securid and protection is changed. + */ #if CACHE_LEGACY_SIZE - /* - * we must however invalidate the legacy - * cache, which is based on inode numbers. - * For safety, invalidate even if updating - * failed. - */ - if ( ( ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ) - && !ni->security_id ) - { - struct CACHED_PERMISSIONS_LEGACY legacy; + /* + * we must however invalidate the legacy + * cache, which is based on inode numbers. + * For safety, invalidate even if updating + * failed. + */ + if ((ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + && !ni->security_id) { + struct CACHED_PERMISSIONS_LEGACY legacy; - legacy.mft_no = ni->mft_no; - legacy.variable = ( char* )NULL; - legacy.varsize = 0; - ntfs_invalidate_cache( scx->vol->legacy_cache, - GENERIC( &legacy ), - ( cache_compare )leg_compare, 0 ); - } + legacy.mft_no = ni->mft_no; + legacy.variable = (char*)NULL; + legacy.varsize = 0; + ntfs_invalidate_cache(scx->vol->legacy_cache, + GENERIC(&legacy), + (cache_compare)leg_compare,0); + } #endif - free( attr ); - } - else - errno = ENOMEM; - } - else - errno = EINVAL; - return ( res ? -1 : 0 ); + free(attr); + } else + errno = ENOMEM; + } else + errno = EINVAL; + return (res ? -1 : 0); } #endif /* HAVE_SETXATTR */ /* - * Set new permissions to a file - * Checks user mapping has been defined before request for setting + * Set new permissions to a file + * Checks user mapping has been defined before request for setting * - * rejected if request is not originated by owner or root + * rejected if request is not originated by owner or root * - * returns 0 on success - * -1 on failure, with errno = EIO + * returns 0 on success + * -1 on failure, with errno = EIO */ -int ntfs_set_mode( struct SECURITY_CONTEXT *scx, ntfs_inode *ni, mode_t mode ) +int ntfs_set_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, mode_t mode) { - const SECURITY_DESCRIPTOR_RELATIVE *phead; - const struct CACHED_PERMISSIONS *cached; - char *oldattr; - const SID *usid; - const SID *gsid; - uid_t processuid; - uid_t uid; - uid_t gid; - int res; + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const struct CACHED_PERMISSIONS *cached; + char *oldattr; + const SID *usid; + const SID *gsid; + uid_t processuid; + uid_t uid; + uid_t gid; + int res; #if POSIXACLS - BOOL isdir; - int pxsize; - const struct POSIX_SECURITY *oldpxdesc; - struct POSIX_SECURITY *newpxdesc = ( struct POSIX_SECURITY* )NULL; + BOOL isdir; + int pxsize; + const struct POSIX_SECURITY *oldpxdesc; + struct POSIX_SECURITY *newpxdesc = (struct POSIX_SECURITY*)NULL; #endif - /* get the current owner, either from cache or from old attribute */ - res = 0; - cached = fetch_cache( scx, ni ); - if ( cached ) - { - uid = cached->uid; - gid = cached->gid; + /* get the current owner, either from cache or from old attribute */ + res = 0; + cached = fetch_cache(scx, ni); + if (cached) { + uid = cached->uid; + gid = cached->gid; #if POSIXACLS - oldpxdesc = cached->pxdesc; - if ( oldpxdesc ) - { - /* must copy before merging */ - pxsize = sizeof( struct POSIX_SECURITY ) - + ( oldpxdesc->acccnt + oldpxdesc->defcnt ) * sizeof( struct POSIX_ACE ); - newpxdesc = ( struct POSIX_SECURITY* )malloc( pxsize ); - if ( newpxdesc ) - { - memcpy( newpxdesc, oldpxdesc, pxsize ); - if ( ntfs_merge_mode_posix( newpxdesc, mode ) ) - res = -1; - } - else - res = -1; - } - else - newpxdesc = ( struct POSIX_SECURITY* )NULL; + oldpxdesc = cached->pxdesc; + if (oldpxdesc) { + /* must copy before merging */ + pxsize = sizeof(struct POSIX_SECURITY) + + (oldpxdesc->acccnt + oldpxdesc->defcnt)*sizeof(struct POSIX_ACE); + newpxdesc = (struct POSIX_SECURITY*)malloc(pxsize); + if (newpxdesc) { + memcpy(newpxdesc, oldpxdesc, pxsize); + if (ntfs_merge_mode_posix(newpxdesc, mode)) + res = -1; + } else + res = -1; + } else + newpxdesc = (struct POSIX_SECURITY*)NULL; #endif - } - else - { - oldattr = getsecurityattr( scx->vol, ni ); - if ( oldattr ) - { - phead = ( const SECURITY_DESCRIPTOR_RELATIVE* )oldattr; + } else { + oldattr = getsecurityattr(scx->vol, ni); + if (oldattr) { + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr; #if OWNERFROMACL - usid = ntfs_acl_owner( oldattr ); + usid = ntfs_acl_owner(oldattr); #else - usid = ( const SID* ) & oldattr[le32_to_cpu( phead->owner )]; + usid = (const SID*)&oldattr[le32_to_cpu(phead->owner)]; #endif - gsid = ( const SID* ) & oldattr[le32_to_cpu( phead->group )]; - uid = ntfs_find_user( scx->mapping[MAPUSERS], usid ); - gid = ntfs_find_group( scx->mapping[MAPGROUPS], gsid ); + gsid = (const SID*)&oldattr[le32_to_cpu(phead->group)]; + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); + gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); #if POSIXACLS - isdir = ( ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ) != const_cpu_to_le16( 0 ); - newpxdesc = ntfs_build_permissions_posix( scx->mapping, - oldattr, usid, gsid, isdir ); - if ( !newpxdesc || ntfs_merge_mode_posix( newpxdesc, mode ) ) - res = -1; + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0); + newpxdesc = ntfs_build_permissions_posix(scx->mapping, + oldattr, usid, gsid, isdir); + if (!newpxdesc || ntfs_merge_mode_posix(newpxdesc, mode)) + res = -1; #endif - free( oldattr ); - } - else - res = -1; - } + free(oldattr); + } else + res = -1; + } - if ( !res ) - { - processuid = scx->uid; - /* TODO : use CAP_FOWNER process capability */ - if ( !processuid || ( uid == processuid ) ) - { - /* - * clear setgid if file group does - * not match process group - */ - if ( processuid && ( gid != scx->gid ) - && !groupmember( scx, scx->uid, gid ) ) - mode &= ~S_ISGID; + if (!res) { + processuid = scx->uid; +/* TODO : use CAP_FOWNER process capability */ + if (!processuid || (uid == processuid)) { + /* + * clear setgid if file group does + * not match process group + */ + if (processuid && (gid != scx->gid) + && !groupmember(scx, scx->uid, gid)) + mode &= ~S_ISGID; #if POSIXACLS - if ( newpxdesc ) - { - newpxdesc->mode = mode; - res = ntfs_set_owner_mode( scx, ni, uid, gid, - mode, newpxdesc ); - } - else - res = ntfs_set_owner_mode( scx, ni, uid, gid, - mode, newpxdesc ); + if (newpxdesc) { + newpxdesc->mode = mode; + res = ntfs_set_owner_mode(scx, ni, uid, gid, + mode, newpxdesc); + } else + res = ntfs_set_owner_mode(scx, ni, uid, gid, + mode, newpxdesc); #else - res = ntfs_set_owner_mode( scx, ni, uid, gid, mode ); + res = ntfs_set_owner_mode(scx, ni, uid, gid, mode); #endif - } - else - { - errno = EPERM; - res = -1; /* neither owner nor root */ - } - } - else - { - /* - * Should not happen : a default descriptor is generated - * by getsecurityattr() when there are none - */ - ntfs_log_error( "File has no security descriptor\n" ); - res = -1; - errno = EIO; - } + } else { + errno = EPERM; + res = -1; /* neither owner nor root */ + } + } else { + /* + * Should not happen : a default descriptor is generated + * by getsecurityattr() when there are none + */ + ntfs_log_error("File has no security descriptor\n"); + res = -1; + errno = EIO; + } #if POSIXACLS - if ( newpxdesc ) free( newpxdesc ); + if (newpxdesc) free(newpxdesc); #endif - return ( res ? -1 : 0 ); + return (res ? -1 : 0); } /* - * Create a default security descriptor for files whose descriptor - * cannot be inherited + * Create a default security descriptor for files whose descriptor + * cannot be inherited */ -int ntfs_sd_add_everyone( ntfs_inode *ni ) +int ntfs_sd_add_everyone(ntfs_inode *ni) { - /* JPA SECURITY_DESCRIPTOR_ATTR *sd; */ - SECURITY_DESCRIPTOR_RELATIVE *sd; - ACL *acl; - ACCESS_ALLOWED_ACE *ace; - SID *sid; - int ret, sd_len; + /* JPA SECURITY_DESCRIPTOR_ATTR *sd; */ + SECURITY_DESCRIPTOR_RELATIVE *sd; + ACL *acl; + ACCESS_ALLOWED_ACE *ace; + SID *sid; + int ret, sd_len; + + /* Create SECURITY_DESCRIPTOR attribute (everyone has full access). */ + /* + * Calculate security descriptor length. We have 2 sub-authorities in + * owner and group SIDs, but structure SID contain only one, so add + * 4 bytes to every SID. + */ + sd_len = sizeof(SECURITY_DESCRIPTOR_ATTR) + 2 * (sizeof(SID) + 4) + + sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE); + sd = (SECURITY_DESCRIPTOR_RELATIVE*)ntfs_calloc(sd_len); + if (!sd) + return -1; + + sd->revision = SECURITY_DESCRIPTOR_REVISION; + sd->control = SE_DACL_PRESENT | SE_SELF_RELATIVE; + + sid = (SID*)((u8*)sd + sizeof(SECURITY_DESCRIPTOR_ATTR)); + sid->revision = SID_REVISION; + sid->sub_authority_count = 2; + sid->sub_authority[0] = const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + sid->sub_authority[1] = const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + sid->identifier_authority.value[5] = 5; + sd->owner = cpu_to_le32((u8*)sid - (u8*)sd); + + sid = (SID*)((u8*)sid + sizeof(SID) + 4); + sid->revision = SID_REVISION; + sid->sub_authority_count = 2; + sid->sub_authority[0] = const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + sid->sub_authority[1] = const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + sid->identifier_authority.value[5] = 5; + sd->group = cpu_to_le32((u8*)sid - (u8*)sd); + + acl = (ACL*)((u8*)sid + sizeof(SID) + 4); + acl->revision = ACL_REVISION; + acl->size = const_cpu_to_le16(sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE)); + acl->ace_count = const_cpu_to_le16(1); + sd->dacl = cpu_to_le32((u8*)acl - (u8*)sd); + + ace = (ACCESS_ALLOWED_ACE*)((u8*)acl + sizeof(ACL)); + ace->type = ACCESS_ALLOWED_ACE_TYPE; + ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE; + ace->size = const_cpu_to_le16(sizeof(ACCESS_ALLOWED_ACE)); + ace->mask = const_cpu_to_le32(0x1f01ff); /* FIXME */ + ace->sid.revision = SID_REVISION; + ace->sid.sub_authority_count = 1; + ace->sid.sub_authority[0] = const_cpu_to_le32(0); + ace->sid.identifier_authority.value[5] = 1; - /* Create SECURITY_DESCRIPTOR attribute (everyone has full access). */ - /* - * Calculate security descriptor length. We have 2 sub-authorities in - * owner and group SIDs, but structure SID contain only one, so add - * 4 bytes to every SID. - */ - sd_len = sizeof( SECURITY_DESCRIPTOR_ATTR ) + 2 * ( sizeof( SID ) + 4 ) + - sizeof( ACL ) + sizeof( ACCESS_ALLOWED_ACE ); - sd = ( SECURITY_DESCRIPTOR_RELATIVE* )ntfs_calloc( sd_len ); - if ( !sd ) - return -1; - - sd->revision = SECURITY_DESCRIPTOR_REVISION; - sd->control = SE_DACL_PRESENT | SE_SELF_RELATIVE; - - sid = ( SID* )( ( u8* )sd + sizeof( SECURITY_DESCRIPTOR_ATTR ) ); - sid->revision = SID_REVISION; - sid->sub_authority_count = 2; - sid->sub_authority[0] = const_cpu_to_le32( SECURITY_BUILTIN_DOMAIN_RID ); - sid->sub_authority[1] = const_cpu_to_le32( DOMAIN_ALIAS_RID_ADMINS ); - sid->identifier_authority.value[5] = 5; - sd->owner = cpu_to_le32( ( u8* )sid - ( u8* )sd ); - - sid = ( SID* )( ( u8* )sid + sizeof( SID ) + 4 ); - sid->revision = SID_REVISION; - sid->sub_authority_count = 2; - sid->sub_authority[0] = const_cpu_to_le32( SECURITY_BUILTIN_DOMAIN_RID ); - sid->sub_authority[1] = const_cpu_to_le32( DOMAIN_ALIAS_RID_ADMINS ); - sid->identifier_authority.value[5] = 5; - sd->group = cpu_to_le32( ( u8* )sid - ( u8* )sd ); - - acl = ( ACL* )( ( u8* )sid + sizeof( SID ) + 4 ); - acl->revision = ACL_REVISION; - acl->size = const_cpu_to_le16( sizeof( ACL ) + sizeof( ACCESS_ALLOWED_ACE ) ); - acl->ace_count = const_cpu_to_le16( 1 ); - sd->dacl = cpu_to_le32( ( u8* )acl - ( u8* )sd ); - - ace = ( ACCESS_ALLOWED_ACE* )( ( u8* )acl + sizeof( ACL ) ); - ace->type = ACCESS_ALLOWED_ACE_TYPE; - ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE; - ace->size = const_cpu_to_le16( sizeof( ACCESS_ALLOWED_ACE ) ); - ace->mask = const_cpu_to_le32( 0x1f01ff ); /* FIXME */ - ace->sid.revision = SID_REVISION; - ace->sid.sub_authority_count = 1; - ace->sid.sub_authority[0] = const_cpu_to_le32( 0 ); - ace->sid.identifier_authority.value[5] = 1; - - ret = ntfs_attr_add( ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0, ( u8* )sd, - sd_len ); - if ( ret ) - ntfs_log_perror( "Failed to add initial SECURITY_DESCRIPTOR" ); - - free( sd ); - return ret; + ret = ntfs_attr_add(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0, (u8*)sd, + sd_len); + if (ret) + ntfs_log_perror("Failed to add initial SECURITY_DESCRIPTOR"); + + free(sd); + return ret; } /* - * Check whether user can access a file in a specific way + * Check whether user can access a file in a specific way * - * Returns 1 if access is allowed, including user is root or no - * user mapping defined - * 2 if sticky and accesstype is S_IWRITE + S_IEXEC + S_ISVTX - * 0 and sets errno if there is a problem or if access - * is not allowed + * Returns 1 if access is allowed, including user is root or no + * user mapping defined + * 2 if sticky and accesstype is S_IWRITE + S_IEXEC + S_ISVTX + * 0 and sets errno if there is a problem or if access + * is not allowed * - * This is used for Posix ACL and checking creation of DOS file names + * This is used for Posix ACL and checking creation of DOS file names */ -int ntfs_allowed_access( struct SECURITY_CONTEXT *scx, - ntfs_inode *ni, - int accesstype ) /* access type required (S_Ixxx values) */ +int ntfs_allowed_access(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, + int accesstype) /* access type required (S_Ixxx values) */ { - int perm; - int res; - int allow; - struct stat stbuf; + int perm; + int res; + int allow; + struct stat stbuf; - /* - * Always allow for root unless execution is requested. - * (was checked by fuse until kernel 2.6.29) - * Also always allow if no mapping has been defined - */ - if ( !scx->mapping[MAPUSERS] - || ( !scx->uid - && ( !( accesstype & S_IEXEC ) - || ( ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ) ) ) ) - allow = 1; - else - { - perm = ntfs_get_perm( scx, ni, accesstype ); - if ( perm >= 0 ) - { - res = EACCES; - switch ( accesstype ) - { - case S_IEXEC: - allow = ( perm & ( S_IXUSR | S_IXGRP | S_IXOTH ) ) != 0; - break; - case S_IWRITE: - allow = ( perm & ( S_IWUSR | S_IWGRP | S_IWOTH ) ) != 0; - break; - case S_IWRITE + S_IEXEC: - allow = ( ( perm & ( S_IWUSR | S_IWGRP | S_IWOTH ) ) != 0 ) - && ( ( perm & ( S_IXUSR | S_IXGRP | S_IXOTH ) ) != 0 ); - break; - case S_IREAD: - allow = ( perm & ( S_IRUSR | S_IRGRP | S_IROTH ) ) != 0; - break; - case S_IREAD + S_IEXEC: - allow = ( ( perm & ( S_IRUSR | S_IRGRP | S_IROTH ) ) != 0 ) - && ( ( perm & ( S_IXUSR | S_IXGRP | S_IXOTH ) ) != 0 ); - break; - case S_IREAD + S_IWRITE: - allow = ( ( perm & ( S_IRUSR | S_IRGRP | S_IROTH ) ) != 0 ) - && ( ( perm & ( S_IWUSR | S_IWGRP | S_IWOTH ) ) != 0 ); - break; - case S_IWRITE + S_IEXEC + S_ISVTX: - if ( perm & S_ISVTX ) - { - if ( ( ntfs_get_owner_mode( scx, ni, &stbuf ) >= 0 ) - && ( stbuf.st_uid == scx->uid ) ) - allow = 1; - else - allow = 2; - } - else - allow = ( ( perm & ( S_IWUSR | S_IWGRP | S_IWOTH ) ) != 0 ) - && ( ( perm & ( S_IXUSR | S_IXGRP | S_IXOTH ) ) != 0 ); - break; - case S_IREAD + S_IWRITE + S_IEXEC: - allow = ( ( perm & ( S_IRUSR | S_IRGRP | S_IROTH ) ) != 0 ) - && ( ( perm & ( S_IWUSR | S_IWGRP | S_IWOTH ) ) != 0 ) - && ( ( perm & ( S_IXUSR | S_IXGRP | S_IXOTH ) ) != 0 ); - break; - default : - res = EINVAL; - allow = 0; - break; - } - if ( !allow ) - errno = res; - } - else - allow = 0; - } - return ( allow ); + /* + * Always allow for root unless execution is requested. + * (was checked by fuse until kernel 2.6.29) + * Also always allow if no mapping has been defined + */ + if (!scx->mapping[MAPUSERS] + || (!scx->uid + && (!(accesstype & S_IEXEC) + || (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)))) + allow = 1; + else { + perm = ntfs_get_perm(scx, ni, accesstype); + if (perm >= 0) { + res = EACCES; + switch (accesstype) { + case S_IEXEC: + allow = (perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0; + break; + case S_IWRITE: + allow = (perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0; + break; + case S_IWRITE + S_IEXEC: + allow = ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0) + && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0); + break; + case S_IREAD: + allow = (perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0; + break; + case S_IREAD + S_IEXEC: + allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0) + && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0); + break; + case S_IREAD + S_IWRITE: + allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0) + && ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0); + break; + case S_IWRITE + S_IEXEC + S_ISVTX: + if (perm & S_ISVTX) { + if ((ntfs_get_owner_mode(scx,ni,&stbuf) >= 0) + && (stbuf.st_uid == scx->uid)) + allow = 1; + else + allow = 2; + } else + allow = ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0) + && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0); + break; + case S_IREAD + S_IWRITE + S_IEXEC: + allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0) + && ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0) + && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0); + break; + default : + res = EINVAL; + allow = 0; + break; + } + if (!allow) + errno = res; + } else + allow = 0; + } + return (allow); } #if 0 /* not needed any more */ /* - * Check whether user can access the parent directory - * of a file in a specific way + * Check whether user can access the parent directory + * of a file in a specific way * - * Returns true if access is allowed, including user is root and - * no user mapping defined + * Returns true if access is allowed, including user is root and + * no user mapping defined + * + * Sets errno if there is a problem or if not allowed * - * Sets errno if there is a problem or if not allowed - * - * This is used for Posix ACL and checking creation of DOS file names + * This is used for Posix ACL and checking creation of DOS file names */ -BOOL old_ntfs_allowed_dir_access( struct SECURITY_CONTEXT *scx, - const char *path, int accesstype ) +BOOL old_ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx, + const char *path, int accesstype) { - int allow; - char *dirpath; - char *name; - ntfs_inode *ni; - ntfs_inode *dir_ni; - struct stat stbuf; + int allow; + char *dirpath; + char *name; + ntfs_inode *ni; + ntfs_inode *dir_ni; + struct stat stbuf; - allow = 0; - dirpath = strdup( path ); - if ( dirpath ) - { - /* the root of file system is seen as a parent of itself */ - /* is that correct ? */ - name = strrchr( dirpath, '/' ); - *name = 0; - dir_ni = ntfs_pathname_to_inode( scx->vol, NULL, dirpath ); - if ( dir_ni ) - { - allow = ntfs_allowed_access( scx, - dir_ni, accesstype ); - ntfs_inode_close( dir_ni ); - /* - * for an not-owned sticky directory, have to - * check whether file itself is owned - */ - if ( ( accesstype == ( S_IWRITE + S_IEXEC + S_ISVTX ) ) - && ( allow == 2 ) ) - { - ni = ntfs_pathname_to_inode( scx->vol, NULL, - path ); - allow = FALSE; - if ( ni ) - { - allow = ( ntfs_get_owner_mode( scx, ni, &stbuf ) >= 0 ) - && ( stbuf.st_uid == scx->uid ); - ntfs_inode_close( ni ); - } - } - } - free( dirpath ); - } - return ( allow ); /* errno is set if not allowed */ + allow = 0; + dirpath = strdup(path); + if (dirpath) { + /* the root of file system is seen as a parent of itself */ + /* is that correct ? */ + name = strrchr(dirpath, '/'); + *name = 0; + dir_ni = ntfs_pathname_to_inode(scx->vol, NULL, dirpath); + if (dir_ni) { + allow = ntfs_allowed_access(scx, + dir_ni, accesstype); + ntfs_inode_close(dir_ni); + /* + * for an not-owned sticky directory, have to + * check whether file itself is owned + */ + if ((accesstype == (S_IWRITE + S_IEXEC + S_ISVTX)) + && (allow == 2)) { + ni = ntfs_pathname_to_inode(scx->vol, NULL, + path); + allow = FALSE; + if (ni) { + allow = (ntfs_get_owner_mode(scx,ni,&stbuf) >= 0) + && (stbuf.st_uid == scx->uid); + ntfs_inode_close(ni); + } + } + } + free(dirpath); + } + return (allow); /* errno is set if not allowed */ } #endif /* - * Define a new owner/group to a file + * Define a new owner/group to a file * - * returns zero if successful + * returns zero if successful */ -int ntfs_set_owner( struct SECURITY_CONTEXT *scx, ntfs_inode *ni, - uid_t uid, gid_t gid ) +int ntfs_set_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + uid_t uid, gid_t gid) { - const SECURITY_DESCRIPTOR_RELATIVE *phead; - const struct CACHED_PERMISSIONS *cached; - char *oldattr; - const SID *usid; - const SID *gsid; - uid_t fileuid; - uid_t filegid; - mode_t mode; - int perm; - BOOL isdir; - int res; + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const struct CACHED_PERMISSIONS *cached; + char *oldattr; + const SID *usid; + const SID *gsid; + uid_t fileuid; + uid_t filegid; + mode_t mode; + int perm; + BOOL isdir; + int res; #if POSIXACLS - struct POSIX_SECURITY *pxdesc; - BOOL pxdescbuilt = FALSE; + struct POSIX_SECURITY *pxdesc; + BOOL pxdescbuilt = FALSE; #endif - res = 0; - /* get the current owner and mode from cache or security attributes */ - oldattr = ( char* )NULL; - cached = fetch_cache( scx, ni ); - if ( cached ) - { - fileuid = cached->uid; - filegid = cached->gid; - mode = cached->mode; + res = 0; + /* get the current owner and mode from cache or security attributes */ + oldattr = (char*)NULL; + cached = fetch_cache(scx,ni); + if (cached) { + fileuid = cached->uid; + filegid = cached->gid; + mode = cached->mode; #if POSIXACLS - pxdesc = cached->pxdesc; - if ( !pxdesc ) - res = -1; + pxdesc = cached->pxdesc; + if (!pxdesc) + res = -1; #endif - } - else - { - fileuid = 0; - filegid = 0; - mode = 0; - oldattr = getsecurityattr( scx->vol, ni ); - if ( oldattr ) - { - isdir = ( ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ) - != const_cpu_to_le16( 0 ); - phead = ( const SECURITY_DESCRIPTOR_RELATIVE* ) - oldattr; - gsid = ( const SID* ) - & oldattr[le32_to_cpu( phead->group )]; + } else { + fileuid = 0; + filegid = 0; + mode = 0; + oldattr = getsecurityattr(scx->vol, ni); + if (oldattr) { + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + != const_cpu_to_le16(0); + phead = (const SECURITY_DESCRIPTOR_RELATIVE*) + oldattr; + gsid = (const SID*) + &oldattr[le32_to_cpu(phead->group)]; #if OWNERFROMACL - usid = ntfs_acl_owner( oldattr ); + usid = ntfs_acl_owner(oldattr); #else - usid = ( const SID* ) - & oldattr[le32_to_cpu( phead->owner )]; + usid = (const SID*) + &oldattr[le32_to_cpu(phead->owner)]; #endif #if POSIXACLS - pxdesc = ntfs_build_permissions_posix( scx->mapping, oldattr, - usid, gsid, isdir ); - if ( pxdesc ) - { - pxdescbuilt = TRUE; - fileuid = ntfs_find_user( scx->mapping[MAPUSERS], usid ); - filegid = ntfs_find_group( scx->mapping[MAPGROUPS], gsid ); - mode = perm = pxdesc->mode; - } - else - res = -1; + pxdesc = ntfs_build_permissions_posix(scx->mapping, oldattr, + usid, gsid, isdir); + if (pxdesc) { + pxdescbuilt = TRUE; + fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid); + filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); + mode = perm = pxdesc->mode; + } else + res = -1; #else - mode = perm = ntfs_build_permissions( oldattr, - usid, gsid, isdir ); - if ( perm >= 0 ) - { - fileuid = ntfs_find_user( scx->mapping[MAPUSERS], usid ); - filegid = ntfs_find_group( scx->mapping[MAPGROUPS], gsid ); - } - else - res = -1; + mode = perm = ntfs_build_permissions(oldattr, + usid, gsid, isdir); + if (perm >= 0) { + fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid); + filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); + } else + res = -1; #endif - free( oldattr ); - } - else - res = -1; - } - if ( !res ) - { - /* check requested by root */ - /* or chgrp requested by owner to an owned group */ - if ( !scx->uid - || ( ( ( ( int )uid < 0 ) || ( uid == fileuid ) ) - && ( ( gid == scx->gid ) || groupmember( scx, scx->uid, gid ) ) - && ( fileuid == scx->uid ) ) ) - { - /* replace by the new usid and gsid */ - /* or reuse old gid and sid for cacheing */ - if ( ( int )uid < 0 ) - uid = fileuid; - if ( ( int )gid < 0 ) - gid = filegid; - /* clear setuid and setgid if owner has changed */ - /* unless request originated by root */ - if ( uid && ( fileuid != uid ) ) - mode &= 01777; + free(oldattr); + } else + res = -1; + } + if (!res) { + /* check requested by root */ + /* or chgrp requested by owner to an owned group */ + if (!scx->uid + || ((((int)uid < 0) || (uid == fileuid)) + && ((gid == scx->gid) || groupmember(scx, scx->uid, gid)) + && (fileuid == scx->uid))) { + /* replace by the new usid and gsid */ + /* or reuse old gid and sid for cacheing */ + if ((int)uid < 0) + uid = fileuid; + if ((int)gid < 0) + gid = filegid; + /* clear setuid and setgid if owner has changed */ + /* unless request originated by root */ + if (uid && (fileuid != uid)) + mode &= 01777; #if POSIXACLS - res = ntfs_set_owner_mode( scx, ni, uid, gid, - mode, pxdesc ); + res = ntfs_set_owner_mode(scx, ni, uid, gid, + mode, pxdesc); #else - res = ntfs_set_owner_mode( scx, ni, uid, gid, mode ); + res = ntfs_set_owner_mode(scx, ni, uid, gid, mode); #endif - } - else - { - res = -1; /* neither owner nor root */ - errno = EPERM; - } + } else { + res = -1; /* neither owner nor root */ + errno = EPERM; + } #if POSIXACLS - if ( pxdescbuilt ) - free( pxdesc ); + if (pxdescbuilt) + free(pxdesc); #endif - } - else - { - /* - * Should not happen : a default descriptor is generated - * by getsecurityattr() when there are none - */ - ntfs_log_error( "File has no security descriptor\n" ); - res = -1; - errno = EIO; - } - return ( res ? -1 : 0 ); + } else { + /* + * Should not happen : a default descriptor is generated + * by getsecurityattr() when there are none + */ + ntfs_log_error("File has no security descriptor\n"); + res = -1; + errno = EIO; + } + return (res ? -1 : 0); } /* - * Define new owner/group and mode to a file + * Define new owner/group and mode to a file * - * returns zero if successful + * returns zero if successful */ -int ntfs_set_ownmod( struct SECURITY_CONTEXT *scx, ntfs_inode *ni, - uid_t uid, gid_t gid, const mode_t mode ) +int ntfs_set_ownmod(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + uid_t uid, gid_t gid, const mode_t mode) { - const SECURITY_DESCRIPTOR_RELATIVE *phead; - const struct CACHED_PERMISSIONS *cached; - char *oldattr; - const SID *usid; - const SID *gsid; - uid_t fileuid; - uid_t filegid; - BOOL isdir; - int res; + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const struct CACHED_PERMISSIONS *cached; + char *oldattr; + const SID *usid; + const SID *gsid; + uid_t fileuid; + uid_t filegid; + BOOL isdir; + int res; #if POSIXACLS - const struct POSIX_SECURITY *oldpxdesc; - struct POSIX_SECURITY *newpxdesc = ( struct POSIX_SECURITY* )NULL; - int pxsize; + const struct POSIX_SECURITY *oldpxdesc; + struct POSIX_SECURITY *newpxdesc = (struct POSIX_SECURITY*)NULL; + int pxsize; #endif - res = 0; - /* get the current owner and mode from cache or security attributes */ - oldattr = ( char* )NULL; - cached = fetch_cache( scx, ni ); - if ( cached ) - { - fileuid = cached->uid; - filegid = cached->gid; + res = 0; + /* get the current owner and mode from cache or security attributes */ + oldattr = (char*)NULL; + cached = fetch_cache(scx,ni); + if (cached) { + fileuid = cached->uid; + filegid = cached->gid; #if POSIXACLS - oldpxdesc = cached->pxdesc; - if ( oldpxdesc ) - { - /* must copy before merging */ - pxsize = sizeof( struct POSIX_SECURITY ) - + ( oldpxdesc->acccnt + oldpxdesc->defcnt ) * sizeof( struct POSIX_ACE ); - newpxdesc = ( struct POSIX_SECURITY* )malloc( pxsize ); - if ( newpxdesc ) - { - memcpy( newpxdesc, oldpxdesc, pxsize ); - if ( ntfs_merge_mode_posix( newpxdesc, mode ) ) - res = -1; - } - else - res = -1; - } + oldpxdesc = cached->pxdesc; + if (oldpxdesc) { + /* must copy before merging */ + pxsize = sizeof(struct POSIX_SECURITY) + + (oldpxdesc->acccnt + oldpxdesc->defcnt)*sizeof(struct POSIX_ACE); + newpxdesc = (struct POSIX_SECURITY*)malloc(pxsize); + if (newpxdesc) { + memcpy(newpxdesc, oldpxdesc, pxsize); + if (ntfs_merge_mode_posix(newpxdesc, mode)) + res = -1; + } else + res = -1; + } #endif - } - else - { - fileuid = 0; - filegid = 0; - oldattr = getsecurityattr( scx->vol, ni ); - if ( oldattr ) - { - isdir = ( ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ) - != const_cpu_to_le16( 0 ); - phead = ( const SECURITY_DESCRIPTOR_RELATIVE* ) - oldattr; - gsid = ( const SID* ) - & oldattr[le32_to_cpu( phead->group )]; + } else { + fileuid = 0; + filegid = 0; + oldattr = getsecurityattr(scx->vol, ni); + if (oldattr) { + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + != const_cpu_to_le16(0); + phead = (const SECURITY_DESCRIPTOR_RELATIVE*) + oldattr; + gsid = (const SID*) + &oldattr[le32_to_cpu(phead->group)]; #if OWNERFROMACL - usid = ntfs_acl_owner( oldattr ); + usid = ntfs_acl_owner(oldattr); #else - usid = ( const SID* ) - & oldattr[le32_to_cpu( phead->owner )]; + usid = (const SID*) + &oldattr[le32_to_cpu(phead->owner)]; #endif #if POSIXACLS - newpxdesc = ntfs_build_permissions_posix( scx->mapping, oldattr, - usid, gsid, isdir ); - if ( !newpxdesc || ntfs_merge_mode_posix( newpxdesc, mode ) ) - res = -1; - else - { - fileuid = ntfs_find_user( scx->mapping[MAPUSERS], usid ); - filegid = ntfs_find_group( scx->mapping[MAPGROUPS], gsid ); - } + newpxdesc = ntfs_build_permissions_posix(scx->mapping, oldattr, + usid, gsid, isdir); + if (!newpxdesc || ntfs_merge_mode_posix(newpxdesc, mode)) + res = -1; + else { + fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid); + filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); + } #endif - free( oldattr ); - } - else - res = -1; - } - if ( !res ) - { - /* check requested by root */ - /* or chgrp requested by owner to an owned group */ - if ( !scx->uid - || ( ( ( ( int )uid < 0 ) || ( uid == fileuid ) ) - && ( ( gid == scx->gid ) || groupmember( scx, scx->uid, gid ) ) - && ( fileuid == scx->uid ) ) ) - { - /* replace by the new usid and gsid */ - /* or reuse old gid and sid for cacheing */ - if ( ( int )uid < 0 ) - uid = fileuid; - if ( ( int )gid < 0 ) - gid = filegid; + free(oldattr); + } else + res = -1; + } + if (!res) { + /* check requested by root */ + /* or chgrp requested by owner to an owned group */ + if (!scx->uid + || ((((int)uid < 0) || (uid == fileuid)) + && ((gid == scx->gid) || groupmember(scx, scx->uid, gid)) + && (fileuid == scx->uid))) { + /* replace by the new usid and gsid */ + /* or reuse old gid and sid for cacheing */ + if ((int)uid < 0) + uid = fileuid; + if ((int)gid < 0) + gid = filegid; #if POSIXACLS - res = ntfs_set_owner_mode( scx, ni, uid, gid, - mode, newpxdesc ); + res = ntfs_set_owner_mode(scx, ni, uid, gid, + mode, newpxdesc); #else - res = ntfs_set_owner_mode( scx, ni, uid, gid, mode ); + res = ntfs_set_owner_mode(scx, ni, uid, gid, mode); #endif - } - else - { - res = -1; /* neither owner nor root */ - errno = EPERM; - } - } - else - { - /* - * Should not happen : a default descriptor is generated - * by getsecurityattr() when there are none - */ - ntfs_log_error( "File has no security descriptor\n" ); - res = -1; - errno = EIO; - } + } else { + res = -1; /* neither owner nor root */ + errno = EPERM; + } + } else { + /* + * Should not happen : a default descriptor is generated + * by getsecurityattr() when there are none + */ + ntfs_log_error("File has no security descriptor\n"); + res = -1; + errno = EIO; + } #if POSIXACLS - free( newpxdesc ); + free(newpxdesc); #endif - return ( res ? -1 : 0 ); + return (res ? -1 : 0); } /* - * Build a security id for a descriptor inherited from - * parent directory the Windows way + * Build a security id for a descriptor inherited from + * parent directory the Windows way */ -static le32 build_inherited_id( struct SECURITY_CONTEXT *scx, - const char *parentattr, BOOL fordir ) +static le32 build_inherited_id(struct SECURITY_CONTEXT *scx, + const char *parentattr, BOOL fordir) { - const SECURITY_DESCRIPTOR_RELATIVE *pphead; - const ACL *ppacl; - const SID *usid; - const SID *gsid; - BIGSID defusid; - BIGSID defgsid; - int offpacl; - int offowner; - int offgroup; - SECURITY_DESCRIPTOR_RELATIVE *pnhead; - ACL *pnacl; - int parentattrsz; - char *newattr; - int newattrsz; - int aclsz; - int usidsz; - int gsidsz; - int pos; - le32 securid; + const SECURITY_DESCRIPTOR_RELATIVE *pphead; + const ACL *ppacl; + const SID *usid; + const SID *gsid; + BIGSID defusid; + BIGSID defgsid; + int offpacl; + int offowner; + int offgroup; + SECURITY_DESCRIPTOR_RELATIVE *pnhead; + ACL *pnacl; + int parentattrsz; + char *newattr; + int newattrsz; + int aclsz; + int usidsz; + int gsidsz; + int pos; + le32 securid; - parentattrsz = ntfs_attr_size( parentattr ); - pphead = ( const SECURITY_DESCRIPTOR_RELATIVE* )parentattr; - if ( scx->mapping[MAPUSERS] ) - { - usid = ntfs_find_usid( scx->mapping[MAPUSERS], scx->uid, ( SID* ) & defusid ); - gsid = ntfs_find_gsid( scx->mapping[MAPGROUPS], scx->gid, ( SID* ) & defgsid ); - if ( !usid ) - usid = adminsid; - if ( !gsid ) - gsid = adminsid; - } - else - { - /* - * If there is no user mapping, we have to copy owner - * and group from parent directory. - * Windows never has to do that, because it can always - * rely on a user mapping - */ - offowner = le32_to_cpu( pphead->owner ); - usid = ( const SID* ) & parentattr[offowner]; - offgroup = le32_to_cpu( pphead->group ); - gsid = ( const SID* ) & parentattr[offgroup]; - } - /* - * new attribute is smaller than parent's - * except for differences in SIDs which appear in - * owner, group and possible grants and denials in - * generic creator-owner and creator-group ACEs. - * For directories, an ACE may be duplicated for - * access and inheritance, so we double the count. - */ - usidsz = ntfs_sid_size( usid ); - gsidsz = ntfs_sid_size( gsid ); - newattrsz = parentattrsz + 3 * usidsz + 3 * gsidsz; - if ( fordir ) - newattrsz *= 2; - newattr = ( char* )ntfs_malloc( newattrsz ); - if ( newattr ) - { - pnhead = ( SECURITY_DESCRIPTOR_RELATIVE* )newattr; - pnhead->revision = SECURITY_DESCRIPTOR_REVISION; - pnhead->alignment = 0; - pnhead->control = SE_SELF_RELATIVE; - pos = sizeof( SECURITY_DESCRIPTOR_RELATIVE ); - /* - * locate and inherit DACL - * do not test SE_DACL_PRESENT (wrong for "DR Watson") - */ - pnhead->dacl = const_cpu_to_le32( 0 ); - if ( pphead->dacl ) - { - offpacl = le32_to_cpu( pphead->dacl ); - ppacl = ( const ACL* ) & parentattr[offpacl]; - pnacl = ( ACL* ) & newattr[pos]; - aclsz = ntfs_inherit_acl( ppacl, pnacl, usid, gsid, fordir ); - if ( aclsz ) - { - pnhead->dacl = cpu_to_le32( pos ); - pos += aclsz; - pnhead->control |= SE_DACL_PRESENT; - } - } - /* - * locate and inherit SACL - */ - pnhead->sacl = const_cpu_to_le32( 0 ); - if ( pphead->sacl ) - { - offpacl = le32_to_cpu( pphead->sacl ); - ppacl = ( const ACL* ) & parentattr[offpacl]; - pnacl = ( ACL* ) & newattr[pos]; - aclsz = ntfs_inherit_acl( ppacl, pnacl, usid, gsid, fordir ); - if ( aclsz ) - { - pnhead->sacl = cpu_to_le32( pos ); - pos += aclsz; - pnhead->control |= SE_SACL_PRESENT; - } - } - /* - * inherit or redefine owner - */ - memcpy( &newattr[pos], usid, usidsz ); - pnhead->owner = cpu_to_le32( pos ); - pos += usidsz; - /* - * inherit or redefine group - */ - memcpy( &newattr[pos], gsid, gsidsz ); - pnhead->group = cpu_to_le32( pos ); - pos += usidsz; - securid = setsecurityattr( scx->vol, - ( SECURITY_DESCRIPTOR_RELATIVE* )newattr, pos ); - free( newattr ); - } - else - securid = const_cpu_to_le32( 0 ); - return ( securid ); + parentattrsz = ntfs_attr_size(parentattr); + pphead = (const SECURITY_DESCRIPTOR_RELATIVE*)parentattr; + if (scx->mapping[MAPUSERS]) { + usid = ntfs_find_usid(scx->mapping[MAPUSERS], scx->uid, (SID*)&defusid); + gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS], scx->gid, (SID*)&defgsid); + if (!usid) + usid = adminsid; + if (!gsid) + gsid = adminsid; + } else { + /* + * If there is no user mapping, we have to copy owner + * and group from parent directory. + * Windows never has to do that, because it can always + * rely on a user mapping + */ + offowner = le32_to_cpu(pphead->owner); + usid = (const SID*)&parentattr[offowner]; + offgroup = le32_to_cpu(pphead->group); + gsid = (const SID*)&parentattr[offgroup]; + } + /* + * new attribute is smaller than parent's + * except for differences in SIDs which appear in + * owner, group and possible grants and denials in + * generic creator-owner and creator-group ACEs. + * For directories, an ACE may be duplicated for + * access and inheritance, so we double the count. + */ + usidsz = ntfs_sid_size(usid); + gsidsz = ntfs_sid_size(gsid); + newattrsz = parentattrsz + 3*usidsz + 3*gsidsz; + if (fordir) + newattrsz *= 2; + newattr = (char*)ntfs_malloc(newattrsz); + if (newattr) { + pnhead = (SECURITY_DESCRIPTOR_RELATIVE*)newattr; + pnhead->revision = SECURITY_DESCRIPTOR_REVISION; + pnhead->alignment = 0; + pnhead->control = SE_SELF_RELATIVE; + pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE); + /* + * locate and inherit DACL + * do not test SE_DACL_PRESENT (wrong for "DR Watson") + */ + pnhead->dacl = const_cpu_to_le32(0); + if (pphead->dacl) { + offpacl = le32_to_cpu(pphead->dacl); + ppacl = (const ACL*)&parentattr[offpacl]; + pnacl = (ACL*)&newattr[pos]; + aclsz = ntfs_inherit_acl(ppacl, pnacl, usid, gsid, fordir); + if (aclsz) { + pnhead->dacl = cpu_to_le32(pos); + pos += aclsz; + pnhead->control |= SE_DACL_PRESENT; + } + } + /* + * locate and inherit SACL + */ + pnhead->sacl = const_cpu_to_le32(0); + if (pphead->sacl) { + offpacl = le32_to_cpu(pphead->sacl); + ppacl = (const ACL*)&parentattr[offpacl]; + pnacl = (ACL*)&newattr[pos]; + aclsz = ntfs_inherit_acl(ppacl, pnacl, usid, gsid, fordir); + if (aclsz) { + pnhead->sacl = cpu_to_le32(pos); + pos += aclsz; + pnhead->control |= SE_SACL_PRESENT; + } + } + /* + * inherit or redefine owner + */ + memcpy(&newattr[pos],usid,usidsz); + pnhead->owner = cpu_to_le32(pos); + pos += usidsz; + /* + * inherit or redefine group + */ + memcpy(&newattr[pos],gsid,gsidsz); + pnhead->group = cpu_to_le32(pos); + pos += usidsz; + securid = setsecurityattr(scx->vol, + (SECURITY_DESCRIPTOR_RELATIVE*)newattr, pos); + free(newattr); + } else + securid = const_cpu_to_le32(0); + return (securid); } /* - * Get an inherited security id + * Get an inherited security id * - * For Windows compatibility, the normal initial permission setting - * may be inherited from the parent directory instead of being - * defined by the creation arguments. + * For Windows compatibility, the normal initial permission setting + * may be inherited from the parent directory instead of being + * defined by the creation arguments. * - * The following creates an inherited id for that purpose. + * The following creates an inherited id for that purpose. * - * Note : the owner and group of parent directory are also - * inherited (which is not the case on Windows) if no user mapping - * is defined. + * Note : the owner and group of parent directory are also + * inherited (which is not the case on Windows) if no user mapping + * is defined. * - * Returns the inherited id, or zero if not possible (eg on NTFS 1.x) + * Returns the inherited id, or zero if not possible (eg on NTFS 1.x) */ -le32 ntfs_inherited_id( struct SECURITY_CONTEXT *scx, - ntfs_inode *dir_ni, BOOL fordir ) +le32 ntfs_inherited_id(struct SECURITY_CONTEXT *scx, + ntfs_inode *dir_ni, BOOL fordir) { - struct CACHED_PERMISSIONS *cached; - char *parentattr; - le32 securid; + struct CACHED_PERMISSIONS *cached; + char *parentattr; + le32 securid; - securid = const_cpu_to_le32( 0 ); - cached = ( struct CACHED_PERMISSIONS* )NULL; - /* - * Try to get inherited id from cache - */ - if ( test_nino_flag( dir_ni, v3_Extensions ) - && dir_ni->security_id ) - { - cached = fetch_cache( scx, dir_ni ); - if ( cached ) - securid = ( fordir ? cached->inh_dirid - : cached->inh_fileid ); - } - /* - * Not cached or not available in cache, compute it all - * Note : if parent directory has no id, it is not cacheable - */ - if ( !securid ) - { - parentattr = getsecurityattr( scx->vol, dir_ni ); - if ( parentattr ) - { - securid = build_inherited_id( scx, - parentattr, fordir ); - free( parentattr ); - /* - * Store the result into cache for further use - */ - if ( securid ) - { - cached = fetch_cache( scx, dir_ni ); - if ( cached ) - { - if ( fordir ) - cached->inh_dirid = securid; - else - cached->inh_fileid = securid; - } - } - } - } - return ( securid ); + securid = const_cpu_to_le32(0); + cached = (struct CACHED_PERMISSIONS*)NULL; + /* + * Try to get inherited id from cache + */ + if (test_nino_flag(dir_ni, v3_Extensions) + && dir_ni->security_id) { + cached = fetch_cache(scx, dir_ni); + if (cached) + securid = (fordir ? cached->inh_dirid + : cached->inh_fileid); + } + /* + * Not cached or not available in cache, compute it all + * Note : if parent directory has no id, it is not cacheable + */ + if (!securid) { + parentattr = getsecurityattr(scx->vol, dir_ni); + if (parentattr) { + securid = build_inherited_id(scx, + parentattr, fordir); + free(parentattr); + /* + * Store the result into cache for further use + */ + if (securid) { + cached = fetch_cache(scx, dir_ni); + if (cached) { + if (fordir) + cached->inh_dirid = securid; + else + cached->inh_fileid = securid; + } + } + } + } + return (securid); } /* - * Link a group to a member of group + * Link a group to a member of group * - * Returns 0 if OK, -1 (and errno set) if error + * Returns 0 if OK, -1 (and errno set) if error */ -static int link_single_group( struct MAPPING *usermapping, struct passwd *user, - gid_t gid ) +static int link_single_group(struct MAPPING *usermapping, struct passwd *user, + gid_t gid) { - struct group *group; - char **grmem; - int grcnt; - gid_t *groups; - int res; + struct group *group; + char **grmem; + int grcnt; + gid_t *groups; + int res; - res = 0; - group = getgrgid( gid ); - if ( group && group->gr_mem ) - { - grcnt = usermapping->grcnt; - groups = usermapping->groups; - grmem = group->gr_mem; - while ( *grmem && strcmp( user->pw_name, *grmem ) ) - grmem++; - if ( *grmem ) - { - if ( !grcnt ) - groups = ( gid_t* )malloc( sizeof( gid_t ) ); - else - groups = ( gid_t* )realloc( groups, - ( grcnt + 1 ) * sizeof( gid_t ) ); - if ( groups ) - groups[grcnt++] = gid; - else - { - res = -1; - errno = ENOMEM; - } - } - usermapping->grcnt = grcnt; - usermapping->groups = groups; - } - return ( res ); + res = 0; + group = getgrgid(gid); + if (group && group->gr_mem) { + grcnt = usermapping->grcnt; + groups = usermapping->groups; + grmem = group->gr_mem; + while (*grmem && strcmp(user->pw_name, *grmem)) + grmem++; + if (*grmem) { + if (!grcnt) + groups = (gid_t*)malloc(sizeof(gid_t)); + else + groups = (gid_t*)realloc(groups, + (grcnt+1)*sizeof(gid_t)); + if (groups) + groups[grcnt++] = gid; + else { + res = -1; + errno = ENOMEM; + } + } + usermapping->grcnt = grcnt; + usermapping->groups = groups; + } + return (res); } /* - * Statically link group to users - * This is based on groups defined in /etc/group and does not take - * the groups dynamically set by setgroups() nor any changes in - * /etc/group into account + * Statically link group to users + * This is based on groups defined in /etc/group and does not take + * the groups dynamically set by setgroups() nor any changes in + * /etc/group into account * - * Only mapped groups and root group are linked to mapped users + * Only mapped groups and root group are linked to mapped users * - * Returns 0 if OK, -1 (and errno set) if error + * Returns 0 if OK, -1 (and errno set) if error * */ -static int link_group_members( struct SECURITY_CONTEXT *scx ) +static int link_group_members(struct SECURITY_CONTEXT *scx) { - struct MAPPING *usermapping; - struct MAPPING *groupmapping; - struct passwd *user; - int res; + struct MAPPING *usermapping; + struct MAPPING *groupmapping; + struct passwd *user; + int res; - res = 0; - for ( usermapping = scx->mapping[MAPUSERS]; usermapping && !res; - usermapping = usermapping->next ) - { - usermapping->grcnt = 0; - usermapping->groups = ( gid_t* )NULL; - user = getpwuid( usermapping->xid ); - if ( user && user->pw_name ) - { - for ( groupmapping = scx->mapping[MAPGROUPS]; - groupmapping && !res; - groupmapping = groupmapping->next ) - { - if ( link_single_group( usermapping, user, - groupmapping->xid ) ) - res = -1; - } - if ( !res && link_single_group( usermapping, - user, ( gid_t )0 ) ) - res = -1; - } - } - return ( res ); + res = 0; + for (usermapping=scx->mapping[MAPUSERS]; usermapping && !res; + usermapping=usermapping->next) { + usermapping->grcnt = 0; + usermapping->groups = (gid_t*)NULL; + user = getpwuid(usermapping->xid); + if (user && user->pw_name) { + for (groupmapping=scx->mapping[MAPGROUPS]; + groupmapping && !res; + groupmapping=groupmapping->next) { + if (link_single_group(usermapping, user, + groupmapping->xid)) + res = -1; + } + if (!res && link_single_group(usermapping, + user, (gid_t)0)) + res = -1; + } + } + return (res); } /* - * Apply default single user mapping - * returns zero if successful + * Apply default single user mapping + * returns zero if successful */ -static int ntfs_do_default_mapping( struct SECURITY_CONTEXT *scx, - uid_t uid, gid_t gid, const SID *usid ) +static int ntfs_do_default_mapping(struct SECURITY_CONTEXT *scx, + uid_t uid, gid_t gid, const SID *usid) { - struct MAPPING *usermapping; - struct MAPPING *groupmapping; - SID *sid; - int sidsz; - int res; + struct MAPPING *usermapping; + struct MAPPING *groupmapping; + SID *sid; + int sidsz; + int res; - res = -1; - sidsz = ntfs_sid_size( usid ); - sid = ( SID* )ntfs_malloc( sidsz ); - if ( sid ) - { - memcpy( sid, usid, sidsz ); - usermapping = ( struct MAPPING* )ntfs_malloc( sizeof( struct MAPPING ) ); - if ( usermapping ) - { - groupmapping = ( struct MAPPING* )ntfs_malloc( sizeof( struct MAPPING ) ); - if ( groupmapping ) - { - usermapping->sid = sid; - usermapping->xid = uid; - usermapping->next = ( struct MAPPING* )NULL; - groupmapping->sid = sid; - groupmapping->xid = gid; - groupmapping->next = ( struct MAPPING* )NULL; - scx->mapping[MAPUSERS] = usermapping; - scx->mapping[MAPGROUPS] = groupmapping; - res = 0; - } - } - } - return ( res ); + res = -1; + sidsz = ntfs_sid_size(usid); + sid = (SID*)ntfs_malloc(sidsz); + if (sid) { + memcpy(sid,usid,sidsz); + usermapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING)); + if (usermapping) { + groupmapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING)); + if (groupmapping) { + usermapping->sid = sid; + usermapping->xid = uid; + usermapping->next = (struct MAPPING*)NULL; + groupmapping->sid = sid; + groupmapping->xid = gid; + groupmapping->next = (struct MAPPING*)NULL; + scx->mapping[MAPUSERS] = usermapping; + scx->mapping[MAPGROUPS] = groupmapping; + res = 0; + } + } + } + return (res); } /* - * Make sure there are no ambiguous mapping - * Ambiguous mapping may lead to undesired configurations and - * we had rather be safe until the consequences are understood + * Make sure there are no ambiguous mapping + * Ambiguous mapping may lead to undesired configurations and + * we had rather be safe until the consequences are understood */ #if 0 /* not activated for now */ -static BOOL check_mapping( const struct MAPPING *usermapping, - const struct MAPPING *groupmapping ) +static BOOL check_mapping(const struct MAPPING *usermapping, + const struct MAPPING *groupmapping) { - const struct MAPPING *mapping1; - const struct MAPPING *mapping2; - BOOL ambiguous; + const struct MAPPING *mapping1; + const struct MAPPING *mapping2; + BOOL ambiguous; - ambiguous = FALSE; - for ( mapping1 = usermapping; mapping1; mapping1 = mapping1->next ) - for ( mapping2 = mapping1->next; mapping2; mapping1 = mapping2->next ) - if ( ntfs_same_sid( mapping1->sid, mapping2->sid ) ) - { - if ( mapping1->xid != mapping2->xid ) - ambiguous = TRUE; - } - else - { - if ( mapping1->xid == mapping2->xid ) - ambiguous = TRUE; - } - for ( mapping1 = groupmapping; mapping1; mapping1 = mapping1->next ) - for ( mapping2 = mapping1->next; mapping2; mapping1 = mapping2->next ) - if ( ntfs_same_sid( mapping1->sid, mapping2->sid ) ) - { - if ( mapping1->xid != mapping2->xid ) - ambiguous = TRUE; - } - else - { - if ( mapping1->xid == mapping2->xid ) - ambiguous = TRUE; - } - return ( ambiguous ); + ambiguous = FALSE; + for (mapping1=usermapping; mapping1; mapping1=mapping1->next) + for (mapping2=mapping1->next; mapping2; mapping1=mapping2->next) + if (ntfs_same_sid(mapping1->sid,mapping2->sid)) { + if (mapping1->xid != mapping2->xid) + ambiguous = TRUE; + } else { + if (mapping1->xid == mapping2->xid) + ambiguous = TRUE; + } + for (mapping1=groupmapping; mapping1; mapping1=mapping1->next) + for (mapping2=mapping1->next; mapping2; mapping1=mapping2->next) + if (ntfs_same_sid(mapping1->sid,mapping2->sid)) { + if (mapping1->xid != mapping2->xid) + ambiguous = TRUE; + } else { + if (mapping1->xid == mapping2->xid) + ambiguous = TRUE; + } + return (ambiguous); } #endif @@ -4422,687 +4048,612 @@ static BOOL check_mapping( const struct MAPPING *usermapping, #if 0 /* not used any more */ /* - * Try and apply default single user mapping - * returns zero if successful + * Try and apply default single user mapping + * returns zero if successful */ -static int ntfs_default_mapping( struct SECURITY_CONTEXT *scx ) +static int ntfs_default_mapping(struct SECURITY_CONTEXT *scx) { - const SECURITY_DESCRIPTOR_RELATIVE *phead; - ntfs_inode *ni; - char *securattr; - const SID *usid; - int res; + const SECURITY_DESCRIPTOR_RELATIVE *phead; + ntfs_inode *ni; + char *securattr; + const SID *usid; + int res; - res = -1; - ni = ntfs_pathname_to_inode( scx->vol, NULL, "/." ); - if ( ni ) - { - securattr = getsecurityattr( scx->vol, ni ); - if ( securattr ) - { - phead = ( const SECURITY_DESCRIPTOR_RELATIVE* )securattr; - usid = ( SID* ) & securattr[le32_to_cpu( phead->owner )]; - if ( ntfs_is_user_sid( usid ) ) - res = ntfs_do_default_mapping( scx, - scx->uid, scx->gid, usid ); - free( securattr ); - } - ntfs_inode_close( ni ); - } - return ( res ); + res = -1; + ni = ntfs_pathname_to_inode(scx->vol, NULL, "/."); + if (ni) { + securattr = getsecurityattr(scx->vol, ni); + if (securattr) { + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; + usid = (SID*)&securattr[le32_to_cpu(phead->owner)]; + if (ntfs_is_user_sid(usid)) + res = ntfs_do_default_mapping(scx, + scx->uid, scx->gid, usid); + free(securattr); + } + ntfs_inode_close(ni); + } + return (res); } #endif /* - * Basic read from a user mapping file on another volume + * Basic read from a user mapping file on another volume */ -static int basicread( void *fileid, char *buf, size_t size, off_t offs __attribute__( ( unused ) ) ) +static int basicread(void *fileid, char *buf, size_t size, off_t offs __attribute__((unused))) { - return ( read( *( int* )fileid, buf, size ) ); + return (read(*(int*)fileid, buf, size)); } /* - * Read from a user mapping file on current NTFS partition + * Read from a user mapping file on current NTFS partition */ -static int localread( void *fileid, char *buf, size_t size, off_t offs ) +static int localread(void *fileid, char *buf, size_t size, off_t offs) { - return ( ntfs_local_read( ( ntfs_inode* )fileid, - AT_UNNAMED, 0, buf, size, offs ) ); + return (ntfs_local_read((ntfs_inode*)fileid, + AT_UNNAMED, 0, buf, size, offs)); } /* - * Build the user mapping - * - according to a mapping file if defined (or default present), - * - or try default single user mapping if possible + * Build the user mapping + * - according to a mapping file if defined (or default present), + * - or try default single user mapping if possible * - * The mapping is specific to a mounted device - * No locking done, mounting assumed non multithreaded + * The mapping is specific to a mounted device + * No locking done, mounting assumed non multithreaded * - * returns zero if mapping is successful - * (failure should not be interpreted as an error) + * returns zero if mapping is successful + * (failure should not be interpreted as an error) */ -int ntfs_build_mapping( struct SECURITY_CONTEXT *scx, const char *usermap_path, - BOOL allowdef ) +int ntfs_build_mapping(struct SECURITY_CONTEXT *scx, const char *usermap_path, + BOOL allowdef) { - struct MAPLIST *item; - struct MAPLIST *firstitem; - struct MAPPING *usermapping; - struct MAPPING *groupmapping; - ntfs_inode *ni; - int fd; - static struct - { - u8 revision; - u8 levels; - be16 highbase; - be32 lowbase; - le32 level1; - le32 level2; - le32 level3; - le32 level4; - le32 level5; - } defmap = - { - 1, 5, const_cpu_to_be16( 0 ), const_cpu_to_be32( 5 ), - const_cpu_to_le32( 21 ), - const_cpu_to_le32( DEFSECAUTH1 ), const_cpu_to_le32( DEFSECAUTH2 ), - const_cpu_to_le32( DEFSECAUTH3 ), const_cpu_to_le32( DEFSECBASE ) - } ; + struct MAPLIST *item; + struct MAPLIST *firstitem; + struct MAPPING *usermapping; + struct MAPPING *groupmapping; + ntfs_inode *ni; + int fd; + static struct { + u8 revision; + u8 levels; + be16 highbase; + be32 lowbase; + le32 level1; + le32 level2; + le32 level3; + le32 level4; + le32 level5; + } defmap = { + 1, 5, const_cpu_to_be16(0), const_cpu_to_be32(5), + const_cpu_to_le32(21), + const_cpu_to_le32(DEFSECAUTH1), const_cpu_to_le32(DEFSECAUTH2), + const_cpu_to_le32(DEFSECAUTH3), const_cpu_to_le32(DEFSECBASE) + } ; - /* be sure not to map anything until done */ - scx->mapping[MAPUSERS] = ( struct MAPPING* )NULL; - scx->mapping[MAPGROUPS] = ( struct MAPPING* )NULL; + /* be sure not to map anything until done */ + scx->mapping[MAPUSERS] = (struct MAPPING*)NULL; + scx->mapping[MAPGROUPS] = (struct MAPPING*)NULL; - if ( !usermap_path ) usermap_path = MAPPINGFILE; - if ( usermap_path[0] == '/' ) - { - fd = open( usermap_path, O_RDONLY ); - if ( fd > 0 ) - { - firstitem = ntfs_read_mapping( basicread, ( void* ) & fd ); - close( fd ); - } - else - firstitem = ( struct MAPLIST* )NULL; - } - else - { - ni = ntfs_pathname_to_inode( scx->vol, NULL, usermap_path ); - if ( ni ) - { - firstitem = ntfs_read_mapping( localread, ni ); - ntfs_inode_close( ni ); - } - else - firstitem = ( struct MAPLIST* )NULL; - } + if (!usermap_path) usermap_path = MAPPINGFILE; + if (usermap_path[0] == '/') { + fd = open(usermap_path,O_RDONLY); + if (fd > 0) { + firstitem = ntfs_read_mapping(basicread, (void*)&fd); + close(fd); + } else + firstitem = (struct MAPLIST*)NULL; + } else { + ni = ntfs_pathname_to_inode(scx->vol, NULL, usermap_path); + if (ni) { + firstitem = ntfs_read_mapping(localread, ni); + ntfs_inode_close(ni); + } else + firstitem = (struct MAPLIST*)NULL; + } - if ( firstitem ) - { - usermapping = ntfs_do_user_mapping( firstitem ); - groupmapping = ntfs_do_group_mapping( firstitem ); - if ( usermapping && groupmapping ) - { - scx->mapping[MAPUSERS] = usermapping; - scx->mapping[MAPGROUPS] = groupmapping; - } - else - ntfs_log_error( "There were no valid user or no valid group\n" ); - /* now we can free the memory copy of input text */ - /* and rely on internal representation */ - while ( firstitem ) - { - item = firstitem->next; - free( firstitem ); - firstitem = item; - } - } - else - { - /* no mapping file, try a default mapping */ - if ( allowdef ) - { - if ( !ntfs_do_default_mapping( scx, - 0, 0, ( const SID* )&defmap ) ) - ntfs_log_info( "Using default user mapping\n" ); - } - } - return ( !scx->mapping[MAPUSERS] || link_group_members( scx ) ); + if (firstitem) { + usermapping = ntfs_do_user_mapping(firstitem); + groupmapping = ntfs_do_group_mapping(firstitem); + if (usermapping && groupmapping) { + scx->mapping[MAPUSERS] = usermapping; + scx->mapping[MAPGROUPS] = groupmapping; + } else + ntfs_log_error("There were no valid user or no valid group\n"); + /* now we can free the memory copy of input text */ + /* and rely on internal representation */ + while (firstitem) { + item = firstitem->next; + free(firstitem); + firstitem = item; + } + } else { + /* no mapping file, try a default mapping */ + if (allowdef) { + if (!ntfs_do_default_mapping(scx, + 0, 0, (const SID*)&defmap)) + ntfs_log_info("Using default user mapping\n"); + } + } + return (!scx->mapping[MAPUSERS] || link_group_members(scx)); } #ifdef HAVE_SETXATTR /* extended attributes interface required */ /* - * Get the ntfs attribute into an extended attribute - * The attribute is returned according to cpu endianness + * Get the ntfs attribute into an extended attribute + * The attribute is returned according to cpu endianness */ -int ntfs_get_ntfs_attrib( ntfs_inode *ni, char *value, size_t size ) +int ntfs_get_ntfs_attrib(ntfs_inode *ni, char *value, size_t size) { - u32 attrib; - size_t outsize; + u32 attrib; + size_t outsize; - outsize = 0; /* default to no data and no error */ - if ( ni ) - { - attrib = le32_to_cpu( ni->flags ); - if ( ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ) - attrib |= const_le32_to_cpu( FILE_ATTR_DIRECTORY ); - else - attrib &= ~const_le32_to_cpu( FILE_ATTR_DIRECTORY ); - if ( !attrib ) - attrib |= const_le32_to_cpu( FILE_ATTR_NORMAL ); - outsize = sizeof( FILE_ATTR_FLAGS ); - if ( size >= outsize ) - { - if ( value ) - memcpy( value, &attrib, outsize ); - else - errno = EINVAL; - } - } - return ( outsize ? ( int )outsize : -errno ); + outsize = 0; /* default to no data and no error */ + if (ni) { + attrib = le32_to_cpu(ni->flags); + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + attrib |= const_le32_to_cpu(FILE_ATTR_DIRECTORY); + else + attrib &= ~const_le32_to_cpu(FILE_ATTR_DIRECTORY); + if (!attrib) + attrib |= const_le32_to_cpu(FILE_ATTR_NORMAL); + outsize = sizeof(FILE_ATTR_FLAGS); + if (size >= outsize) { + if (value) + memcpy(value,&attrib,outsize); + else + errno = EINVAL; + } + } + return (outsize ? (int)outsize : -errno); } /* - * Return the ntfs attribute into an extended attribute - * The attribute is expected according to cpu endianness + * Return the ntfs attribute into an extended attribute + * The attribute is expected according to cpu endianness * - * Returns 0, or -1 if there is a problem + * Returns 0, or -1 if there is a problem */ -int ntfs_set_ntfs_attrib( ntfs_inode *ni, - const char *value, size_t size, int flags ) +int ntfs_set_ntfs_attrib(ntfs_inode *ni, + const char *value, size_t size, int flags) { - u32 attrib; - le32 settable; - ATTR_FLAGS dirflags; - int res; + u32 attrib; + le32 settable; + ATTR_FLAGS dirflags; + int res; - res = -1; - if ( ni && value && ( size >= sizeof( FILE_ATTR_FLAGS ) ) ) - { - if ( !( flags & XATTR_CREATE ) ) - { - /* copy to avoid alignment problems */ - memcpy( &attrib, value, sizeof( FILE_ATTR_FLAGS ) ); - settable = FILE_ATTR_SETTABLE; - res = 0; - if ( ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ) - { - /* - * Accept changing compression for a directory - * and set index root accordingly - */ - settable |= FILE_ATTR_COMPRESSED; - if ( ( ni->flags ^ cpu_to_le32( attrib ) ) - & FILE_ATTR_COMPRESSED ) - { - if ( ni->flags & FILE_ATTR_COMPRESSED ) - dirflags = const_cpu_to_le16( 0 ); - else - dirflags = ATTR_IS_COMPRESSED; - res = ntfs_attr_set_flags( ni, - AT_INDEX_ROOT, - NTFS_INDEX_I30, 4, - dirflags, - ATTR_COMPRESSION_MASK ); - } - } - if ( !res ) - { - ni->flags = ( ni->flags & ~settable ) - | ( cpu_to_le32( attrib ) & settable ); - NInoFileNameSetDirty( ni ); - NInoSetDirty( ni ); - } - } - else - errno = EEXIST; - } - else - errno = EINVAL; - return ( res ? -1 : 0 ); + res = -1; + if (ni && value && (size >= sizeof(FILE_ATTR_FLAGS))) { + if (!(flags & XATTR_CREATE)) { + /* copy to avoid alignment problems */ + memcpy(&attrib,value,sizeof(FILE_ATTR_FLAGS)); + settable = FILE_ATTR_SETTABLE; + res = 0; + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + /* + * Accept changing compression for a directory + * and set index root accordingly + */ + settable |= FILE_ATTR_COMPRESSED; + if ((ni->flags ^ cpu_to_le32(attrib)) + & FILE_ATTR_COMPRESSED) { + if (ni->flags & FILE_ATTR_COMPRESSED) + dirflags = const_cpu_to_le16(0); + else + dirflags = ATTR_IS_COMPRESSED; + res = ntfs_attr_set_flags(ni, + AT_INDEX_ROOT, + NTFS_INDEX_I30, 4, + dirflags, + ATTR_COMPRESSION_MASK); + } + } + if (!res) { + ni->flags = (ni->flags & ~settable) + | (cpu_to_le32(attrib) & settable); + NInoFileNameSetDirty(ni); + NInoSetDirty(ni); + } + } else + errno = EEXIST; + } else + errno = EINVAL; + return (res ? -1 : 0); } #endif /* HAVE_SETXATTR */ /* - * Open $Secure once for all - * returns zero if it succeeds - * non-zero if it fails. This is not an error (on NTFS v1.x) + * Open $Secure once for all + * returns zero if it succeeds + * non-zero if it fails. This is not an error (on NTFS v1.x) */ -int ntfs_open_secure( ntfs_volume *vol ) +int ntfs_open_secure(ntfs_volume *vol) { - ntfs_inode *ni; - int res; + ntfs_inode *ni; + int res; - res = -1; - vol->secure_ni = ( ntfs_inode* )NULL; - vol->secure_xsii = ( ntfs_index_context* )NULL; - vol->secure_xsdh = ( ntfs_index_context* )NULL; - if ( vol->major_ver >= 3 ) - { - /* make sure this is a genuine $Secure inode 9 */ - ni = ntfs_pathname_to_inode( vol, NULL, "$Secure" ); - if ( ni && ( ni->mft_no == 9 ) ) - { - vol->secure_reentry = 0; - vol->secure_xsii = ntfs_index_ctx_get( ni, - sii_stream, 4 ); - vol->secure_xsdh = ntfs_index_ctx_get( ni, - sdh_stream, 4 ); - if ( ni && vol->secure_xsii && vol->secure_xsdh ) - { - vol->secure_ni = ni; - res = 0; - } - } - } - return ( res ); + res = -1; + vol->secure_ni = (ntfs_inode*)NULL; + vol->secure_xsii = (ntfs_index_context*)NULL; + vol->secure_xsdh = (ntfs_index_context*)NULL; + if (vol->major_ver >= 3) { + /* make sure this is a genuine $Secure inode 9 */ + ni = ntfs_pathname_to_inode(vol, NULL, "$Secure"); + if (ni && (ni->mft_no == 9)) { + vol->secure_reentry = 0; + vol->secure_xsii = ntfs_index_ctx_get(ni, + sii_stream, 4); + vol->secure_xsdh = ntfs_index_ctx_get(ni, + sdh_stream, 4); + if (ni && vol->secure_xsii && vol->secure_xsdh) { + vol->secure_ni = ni; + res = 0; + } + } + } + return (res); } /* - * Final cleaning - * Allocated memory is freed to facilitate the detection of memory leaks + * Final cleaning + * Allocated memory is freed to facilitate the detection of memory leaks */ -void ntfs_close_secure( struct SECURITY_CONTEXT *scx ) +void ntfs_close_secure(struct SECURITY_CONTEXT *scx) { - ntfs_volume *vol; + ntfs_volume *vol; - vol = scx->vol; - if ( vol->secure_ni ) - { - ntfs_index_ctx_put( vol->secure_xsii ); - ntfs_index_ctx_put( vol->secure_xsdh ); - ntfs_inode_close( vol->secure_ni ); - - } - ntfs_free_mapping( scx->mapping ); - free_caches( scx ); + vol = scx->vol; + if (vol->secure_ni) { + ntfs_index_ctx_put(vol->secure_xsii); + ntfs_index_ctx_put(vol->secure_xsdh); + ntfs_inode_close(vol->secure_ni); + + } + ntfs_free_mapping(scx->mapping); + free_caches(scx); } /* - * API for direct access to security descriptors - * based on Win32 API + * API for direct access to security descriptors + * based on Win32 API */ /* - * Selective feeding of a security descriptor into user buffer + * Selective feeding of a security descriptor into user buffer * - * Returns TRUE if successful + * Returns TRUE if successful */ -static BOOL feedsecurityattr( const char *attr, u32 selection, - char *buf, u32 buflen, u32 *psize ) +static BOOL feedsecurityattr(const char *attr, u32 selection, + char *buf, u32 buflen, u32 *psize) { - const SECURITY_DESCRIPTOR_RELATIVE *phead; - SECURITY_DESCRIPTOR_RELATIVE *pnhead; - const ACL *pdacl; - const ACL *psacl; - const SID *pusid; - const SID *pgsid; - unsigned int offdacl; - unsigned int offsacl; - unsigned int offowner; - unsigned int offgroup; - unsigned int daclsz; - unsigned int saclsz; - unsigned int usidsz; - unsigned int gsidsz; - unsigned int size; /* size of requested attributes */ - BOOL ok; - unsigned int pos; - unsigned int avail; - le16 control; + const SECURITY_DESCRIPTOR_RELATIVE *phead; + SECURITY_DESCRIPTOR_RELATIVE *pnhead; + const ACL *pdacl; + const ACL *psacl; + const SID *pusid; + const SID *pgsid; + unsigned int offdacl; + unsigned int offsacl; + unsigned int offowner; + unsigned int offgroup; + unsigned int daclsz; + unsigned int saclsz; + unsigned int usidsz; + unsigned int gsidsz; + unsigned int size; /* size of requested attributes */ + BOOL ok; + unsigned int pos; + unsigned int avail; + le16 control; - avail = 0; - control = SE_SELF_RELATIVE; - phead = ( const SECURITY_DESCRIPTOR_RELATIVE* )attr; - size = sizeof( SECURITY_DESCRIPTOR_RELATIVE ); + avail = 0; + control = SE_SELF_RELATIVE; + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr; + size = sizeof(SECURITY_DESCRIPTOR_RELATIVE); - /* locate DACL if requested and available */ - if ( phead->dacl && ( selection & DACL_SECURITY_INFORMATION ) ) - { - offdacl = le32_to_cpu( phead->dacl ); - pdacl = ( const ACL* ) & attr[offdacl]; - daclsz = le16_to_cpu( pdacl->size ); - size += daclsz; - avail |= DACL_SECURITY_INFORMATION; - } - else - offdacl = daclsz = 0; + /* locate DACL if requested and available */ + if (phead->dacl && (selection & DACL_SECURITY_INFORMATION)) { + offdacl = le32_to_cpu(phead->dacl); + pdacl = (const ACL*)&attr[offdacl]; + daclsz = le16_to_cpu(pdacl->size); + size += daclsz; + avail |= DACL_SECURITY_INFORMATION; + } else + offdacl = daclsz = 0; - /* locate owner if requested and available */ - offowner = le32_to_cpu( phead->owner ); - if ( offowner && ( selection & OWNER_SECURITY_INFORMATION ) ) - { - /* find end of USID */ - pusid = ( const SID* ) & attr[offowner]; - usidsz = ntfs_sid_size( pusid ); - size += usidsz; - avail |= OWNER_SECURITY_INFORMATION; - } - else - offowner = usidsz = 0; + /* locate owner if requested and available */ + offowner = le32_to_cpu(phead->owner); + if (offowner && (selection & OWNER_SECURITY_INFORMATION)) { + /* find end of USID */ + pusid = (const SID*)&attr[offowner]; + usidsz = ntfs_sid_size(pusid); + size += usidsz; + avail |= OWNER_SECURITY_INFORMATION; + } else + offowner = usidsz = 0; - /* locate group if requested and available */ - offgroup = le32_to_cpu( phead->group ); - if ( offgroup && ( selection & GROUP_SECURITY_INFORMATION ) ) - { - /* find end of GSID */ - pgsid = ( const SID* ) & attr[offgroup]; - gsidsz = ntfs_sid_size( pgsid ); - size += gsidsz; - avail |= GROUP_SECURITY_INFORMATION; - } - else - offgroup = gsidsz = 0; + /* locate group if requested and available */ + offgroup = le32_to_cpu(phead->group); + if (offgroup && (selection & GROUP_SECURITY_INFORMATION)) { + /* find end of GSID */ + pgsid = (const SID*)&attr[offgroup]; + gsidsz = ntfs_sid_size(pgsid); + size += gsidsz; + avail |= GROUP_SECURITY_INFORMATION; + } else + offgroup = gsidsz = 0; - /* locate SACL if requested and available */ - if ( phead->sacl && ( selection & SACL_SECURITY_INFORMATION ) ) - { - /* find end of SACL */ - offsacl = le32_to_cpu( phead->sacl ); - psacl = ( const ACL* ) & attr[offsacl]; - saclsz = le16_to_cpu( psacl->size ); - size += saclsz; - avail |= SACL_SECURITY_INFORMATION; - } - else - offsacl = saclsz = 0; + /* locate SACL if requested and available */ + if (phead->sacl && (selection & SACL_SECURITY_INFORMATION)) { + /* find end of SACL */ + offsacl = le32_to_cpu(phead->sacl); + psacl = (const ACL*)&attr[offsacl]; + saclsz = le16_to_cpu(psacl->size); + size += saclsz; + avail |= SACL_SECURITY_INFORMATION; + } else + offsacl = saclsz = 0; - /* - * Check having enough size in destination buffer - * (required size is returned nevertheless so that - * the request can be reissued with adequate size) - */ - if ( size > buflen ) - { - *psize = size; - errno = EINVAL; - ok = FALSE; - } - else - { - if ( selection & OWNER_SECURITY_INFORMATION ) - control |= phead->control & SE_OWNER_DEFAULTED; - if ( selection & GROUP_SECURITY_INFORMATION ) - control |= phead->control & SE_GROUP_DEFAULTED; - if ( selection & DACL_SECURITY_INFORMATION ) - control |= phead->control - & ( SE_DACL_PRESENT - | SE_DACL_DEFAULTED - | SE_DACL_AUTO_INHERITED - | SE_DACL_PROTECTED ); - if ( selection & SACL_SECURITY_INFORMATION ) - control |= phead->control - & ( SE_SACL_PRESENT - | SE_SACL_DEFAULTED - | SE_SACL_AUTO_INHERITED - | SE_SACL_PROTECTED ); - /* - * copy header and feed new flags, even if no detailed data - */ - memcpy( buf, attr, sizeof( SECURITY_DESCRIPTOR_RELATIVE ) ); - pnhead = ( SECURITY_DESCRIPTOR_RELATIVE* )buf; - pnhead->control = control; - pos = sizeof( SECURITY_DESCRIPTOR_RELATIVE ); + /* + * Check having enough size in destination buffer + * (required size is returned nevertheless so that + * the request can be reissued with adequate size) + */ + if (size > buflen) { + *psize = size; + errno = EINVAL; + ok = FALSE; + } else { + if (selection & OWNER_SECURITY_INFORMATION) + control |= phead->control & SE_OWNER_DEFAULTED; + if (selection & GROUP_SECURITY_INFORMATION) + control |= phead->control & SE_GROUP_DEFAULTED; + if (selection & DACL_SECURITY_INFORMATION) + control |= phead->control + & (SE_DACL_PRESENT + | SE_DACL_DEFAULTED + | SE_DACL_AUTO_INHERITED + | SE_DACL_PROTECTED); + if (selection & SACL_SECURITY_INFORMATION) + control |= phead->control + & (SE_SACL_PRESENT + | SE_SACL_DEFAULTED + | SE_SACL_AUTO_INHERITED + | SE_SACL_PROTECTED); + /* + * copy header and feed new flags, even if no detailed data + */ + memcpy(buf,attr,sizeof(SECURITY_DESCRIPTOR_RELATIVE)); + pnhead = (SECURITY_DESCRIPTOR_RELATIVE*)buf; + pnhead->control = control; + pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE); - /* copy DACL if requested and available */ - if ( selection & avail & DACL_SECURITY_INFORMATION ) - { - pnhead->dacl = cpu_to_le32( pos ); - memcpy( &buf[pos], &attr[offdacl], daclsz ); - pos += daclsz; - } - else - pnhead->dacl = const_cpu_to_le32( 0 ); + /* copy DACL if requested and available */ + if (selection & avail & DACL_SECURITY_INFORMATION) { + pnhead->dacl = cpu_to_le32(pos); + memcpy(&buf[pos],&attr[offdacl],daclsz); + pos += daclsz; + } else + pnhead->dacl = const_cpu_to_le32(0); - /* copy SACL if requested and available */ - if ( selection & avail & SACL_SECURITY_INFORMATION ) - { - pnhead->sacl = cpu_to_le32( pos ); - memcpy( &buf[pos], &attr[offsacl], saclsz ); - pos += saclsz; - } - else - pnhead->sacl = const_cpu_to_le32( 0 ); + /* copy SACL if requested and available */ + if (selection & avail & SACL_SECURITY_INFORMATION) { + pnhead->sacl = cpu_to_le32(pos); + memcpy(&buf[pos],&attr[offsacl],saclsz); + pos += saclsz; + } else + pnhead->sacl = const_cpu_to_le32(0); - /* copy owner if requested and available */ - if ( selection & avail & OWNER_SECURITY_INFORMATION ) - { - pnhead->owner = cpu_to_le32( pos ); - memcpy( &buf[pos], &attr[offowner], usidsz ); - pos += usidsz; - } - else - pnhead->owner = const_cpu_to_le32( 0 ); + /* copy owner if requested and available */ + if (selection & avail & OWNER_SECURITY_INFORMATION) { + pnhead->owner = cpu_to_le32(pos); + memcpy(&buf[pos],&attr[offowner],usidsz); + pos += usidsz; + } else + pnhead->owner = const_cpu_to_le32(0); - /* copy group if requested and available */ - if ( selection & avail & GROUP_SECURITY_INFORMATION ) - { - pnhead->group = cpu_to_le32( pos ); - memcpy( &buf[pos], &attr[offgroup], gsidsz ); - pos += gsidsz; - } - else - pnhead->group = const_cpu_to_le32( 0 ); - if ( pos != size ) - ntfs_log_error( "Error in security descriptor size\n" ); - *psize = size; - ok = TRUE; - } + /* copy group if requested and available */ + if (selection & avail & GROUP_SECURITY_INFORMATION) { + pnhead->group = cpu_to_le32(pos); + memcpy(&buf[pos],&attr[offgroup],gsidsz); + pos += gsidsz; + } else + pnhead->group = const_cpu_to_le32(0); + if (pos != size) + ntfs_log_error("Error in security descriptor size\n"); + *psize = size; + ok = TRUE; + } - return ( ok ); + return (ok); } /* - * Merge a new security descriptor into the old one - * and assign to designated file + * Merge a new security descriptor into the old one + * and assign to designated file * - * Returns TRUE if successful + * Returns TRUE if successful */ -static BOOL mergesecurityattr( ntfs_volume *vol, const char *oldattr, - const char *newattr, u32 selection, ntfs_inode *ni ) +static BOOL mergesecurityattr(ntfs_volume *vol, const char *oldattr, + const char *newattr, u32 selection, ntfs_inode *ni) { - const SECURITY_DESCRIPTOR_RELATIVE *oldhead; - const SECURITY_DESCRIPTOR_RELATIVE *newhead; - SECURITY_DESCRIPTOR_RELATIVE *targhead; - const ACL *pdacl; - const ACL *psacl; - const SID *powner; - const SID *pgroup; - int offdacl; - int offsacl; - int offowner; - int offgroup; - unsigned int size; - le16 control; - char *target; - int pos; - int oldattrsz; - int newattrsz; - BOOL ok; + const SECURITY_DESCRIPTOR_RELATIVE *oldhead; + const SECURITY_DESCRIPTOR_RELATIVE *newhead; + SECURITY_DESCRIPTOR_RELATIVE *targhead; + const ACL *pdacl; + const ACL *psacl; + const SID *powner; + const SID *pgroup; + int offdacl; + int offsacl; + int offowner; + int offgroup; + unsigned int size; + le16 control; + char *target; + int pos; + int oldattrsz; + int newattrsz; + BOOL ok; - ok = FALSE; /* default return */ - oldhead = ( const SECURITY_DESCRIPTOR_RELATIVE* )oldattr; - newhead = ( const SECURITY_DESCRIPTOR_RELATIVE* )newattr; - oldattrsz = ntfs_attr_size( oldattr ); - newattrsz = ntfs_attr_size( newattr ); - target = ( char* )ntfs_malloc( oldattrsz + newattrsz ); - if ( target ) - { - targhead = ( SECURITY_DESCRIPTOR_RELATIVE* )target; - pos = sizeof( SECURITY_DESCRIPTOR_RELATIVE ); - control = SE_SELF_RELATIVE; - /* - * copy new DACL if selected - * or keep old DACL if any - */ - if ( ( selection & DACL_SECURITY_INFORMATION ) ? - newhead->dacl : oldhead->dacl ) - { - if ( selection & DACL_SECURITY_INFORMATION ) - { - offdacl = le32_to_cpu( newhead->dacl ); - pdacl = ( const ACL* ) & newattr[offdacl]; - } - else - { - offdacl = le32_to_cpu( oldhead->dacl ); - pdacl = ( const ACL* ) & oldattr[offdacl]; - } - size = le16_to_cpu( pdacl->size ); - memcpy( &target[pos], pdacl, size ); - targhead->dacl = cpu_to_le32( pos ); - pos += size; - } - else - targhead->dacl = const_cpu_to_le32( 0 ); - if ( selection & DACL_SECURITY_INFORMATION ) - { - control |= newhead->control - & ( SE_DACL_PRESENT - | SE_DACL_DEFAULTED - | SE_DACL_PROTECTED ); - if ( newhead->control & SE_DACL_AUTO_INHERIT_REQ ) - control |= SE_DACL_AUTO_INHERITED; - } - else - control |= oldhead->control - & ( SE_DACL_PRESENT - | SE_DACL_DEFAULTED - | SE_DACL_AUTO_INHERITED - | SE_DACL_PROTECTED ); - /* - * copy new SACL if selected - * or keep old SACL if any - */ - if ( ( selection & SACL_SECURITY_INFORMATION ) ? - newhead->sacl : oldhead->sacl ) - { - if ( selection & SACL_SECURITY_INFORMATION ) - { - offsacl = le32_to_cpu( newhead->sacl ); - psacl = ( const ACL* ) & newattr[offsacl]; - } - else - { - offsacl = le32_to_cpu( oldhead->sacl ); - psacl = ( const ACL* ) & oldattr[offsacl]; - } - size = le16_to_cpu( psacl->size ); - memcpy( &target[pos], psacl, size ); - targhead->sacl = cpu_to_le32( pos ); - pos += size; - } - else - targhead->sacl = const_cpu_to_le32( 0 ); - if ( selection & SACL_SECURITY_INFORMATION ) - { - control |= newhead->control - & ( SE_SACL_PRESENT - | SE_SACL_DEFAULTED - | SE_SACL_PROTECTED ); - if ( newhead->control & SE_SACL_AUTO_INHERIT_REQ ) - control |= SE_SACL_AUTO_INHERITED; - } - else - control |= oldhead->control - & ( SE_SACL_PRESENT - | SE_SACL_DEFAULTED - | SE_SACL_AUTO_INHERITED - | SE_SACL_PROTECTED ); - /* - * copy new OWNER if selected - * or keep old OWNER if any - */ - if ( ( selection & OWNER_SECURITY_INFORMATION ) ? - newhead->owner : oldhead->owner ) - { - if ( selection & OWNER_SECURITY_INFORMATION ) - { - offowner = le32_to_cpu( newhead->owner ); - powner = ( const SID* ) & newattr[offowner]; - } - else - { - offowner = le32_to_cpu( oldhead->owner ); - powner = ( const SID* ) & oldattr[offowner]; - } - size = ntfs_sid_size( powner ); - memcpy( &target[pos], powner, size ); - targhead->owner = cpu_to_le32( pos ); - pos += size; - } - else - targhead->owner = const_cpu_to_le32( 0 ); - if ( selection & OWNER_SECURITY_INFORMATION ) - control |= newhead->control & SE_OWNER_DEFAULTED; - else - control |= oldhead->control & SE_OWNER_DEFAULTED; - /* - * copy new GROUP if selected - * or keep old GROUP if any - */ - if ( ( selection & GROUP_SECURITY_INFORMATION ) ? - newhead->group : oldhead->group ) - { - if ( selection & GROUP_SECURITY_INFORMATION ) - { - offgroup = le32_to_cpu( newhead->group ); - pgroup = ( const SID* ) & newattr[offgroup]; - control |= newhead->control - & SE_GROUP_DEFAULTED; - } - else - { - offgroup = le32_to_cpu( oldhead->group ); - pgroup = ( const SID* ) & oldattr[offgroup]; - control |= oldhead->control - & SE_GROUP_DEFAULTED; - } - size = ntfs_sid_size( pgroup ); - memcpy( &target[pos], pgroup, size ); - targhead->group = cpu_to_le32( pos ); - pos += size; - } - else - targhead->group = const_cpu_to_le32( 0 ); - if ( selection & GROUP_SECURITY_INFORMATION ) - control |= newhead->control & SE_GROUP_DEFAULTED; - else - control |= oldhead->control & SE_GROUP_DEFAULTED; - targhead->revision = SECURITY_DESCRIPTOR_REVISION; - targhead->alignment = 0; - targhead->control = control; - ok = !update_secur_descr( vol, target, ni ); - free( target ); - } - return ( ok ); + ok = FALSE; /* default return */ + oldhead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr; + newhead = (const SECURITY_DESCRIPTOR_RELATIVE*)newattr; + oldattrsz = ntfs_attr_size(oldattr); + newattrsz = ntfs_attr_size(newattr); + target = (char*)ntfs_malloc(oldattrsz + newattrsz); + if (target) { + targhead = (SECURITY_DESCRIPTOR_RELATIVE*)target; + pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE); + control = SE_SELF_RELATIVE; + /* + * copy new DACL if selected + * or keep old DACL if any + */ + if ((selection & DACL_SECURITY_INFORMATION) ? + newhead->dacl : oldhead->dacl) { + if (selection & DACL_SECURITY_INFORMATION) { + offdacl = le32_to_cpu(newhead->dacl); + pdacl = (const ACL*)&newattr[offdacl]; + } else { + offdacl = le32_to_cpu(oldhead->dacl); + pdacl = (const ACL*)&oldattr[offdacl]; + } + size = le16_to_cpu(pdacl->size); + memcpy(&target[pos], pdacl, size); + targhead->dacl = cpu_to_le32(pos); + pos += size; + } else + targhead->dacl = const_cpu_to_le32(0); + if (selection & DACL_SECURITY_INFORMATION) { + control |= newhead->control + & (SE_DACL_PRESENT + | SE_DACL_DEFAULTED + | SE_DACL_PROTECTED); + if (newhead->control & SE_DACL_AUTO_INHERIT_REQ) + control |= SE_DACL_AUTO_INHERITED; + } else + control |= oldhead->control + & (SE_DACL_PRESENT + | SE_DACL_DEFAULTED + | SE_DACL_AUTO_INHERITED + | SE_DACL_PROTECTED); + /* + * copy new SACL if selected + * or keep old SACL if any + */ + if ((selection & SACL_SECURITY_INFORMATION) ? + newhead->sacl : oldhead->sacl) { + if (selection & SACL_SECURITY_INFORMATION) { + offsacl = le32_to_cpu(newhead->sacl); + psacl = (const ACL*)&newattr[offsacl]; + } else { + offsacl = le32_to_cpu(oldhead->sacl); + psacl = (const ACL*)&oldattr[offsacl]; + } + size = le16_to_cpu(psacl->size); + memcpy(&target[pos], psacl, size); + targhead->sacl = cpu_to_le32(pos); + pos += size; + } else + targhead->sacl = const_cpu_to_le32(0); + if (selection & SACL_SECURITY_INFORMATION) { + control |= newhead->control + & (SE_SACL_PRESENT + | SE_SACL_DEFAULTED + | SE_SACL_PROTECTED); + if (newhead->control & SE_SACL_AUTO_INHERIT_REQ) + control |= SE_SACL_AUTO_INHERITED; + } else + control |= oldhead->control + & (SE_SACL_PRESENT + | SE_SACL_DEFAULTED + | SE_SACL_AUTO_INHERITED + | SE_SACL_PROTECTED); + /* + * copy new OWNER if selected + * or keep old OWNER if any + */ + if ((selection & OWNER_SECURITY_INFORMATION) ? + newhead->owner : oldhead->owner) { + if (selection & OWNER_SECURITY_INFORMATION) { + offowner = le32_to_cpu(newhead->owner); + powner = (const SID*)&newattr[offowner]; + } else { + offowner = le32_to_cpu(oldhead->owner); + powner = (const SID*)&oldattr[offowner]; + } + size = ntfs_sid_size(powner); + memcpy(&target[pos], powner, size); + targhead->owner = cpu_to_le32(pos); + pos += size; + } else + targhead->owner = const_cpu_to_le32(0); + if (selection & OWNER_SECURITY_INFORMATION) + control |= newhead->control & SE_OWNER_DEFAULTED; + else + control |= oldhead->control & SE_OWNER_DEFAULTED; + /* + * copy new GROUP if selected + * or keep old GROUP if any + */ + if ((selection & GROUP_SECURITY_INFORMATION) ? + newhead->group : oldhead->group) { + if (selection & GROUP_SECURITY_INFORMATION) { + offgroup = le32_to_cpu(newhead->group); + pgroup = (const SID*)&newattr[offgroup]; + control |= newhead->control + & SE_GROUP_DEFAULTED; + } else { + offgroup = le32_to_cpu(oldhead->group); + pgroup = (const SID*)&oldattr[offgroup]; + control |= oldhead->control + & SE_GROUP_DEFAULTED; + } + size = ntfs_sid_size(pgroup); + memcpy(&target[pos], pgroup, size); + targhead->group = cpu_to_le32(pos); + pos += size; + } else + targhead->group = const_cpu_to_le32(0); + if (selection & GROUP_SECURITY_INFORMATION) + control |= newhead->control & SE_GROUP_DEFAULTED; + else + control |= oldhead->control & SE_GROUP_DEFAULTED; + targhead->revision = SECURITY_DESCRIPTOR_REVISION; + targhead->alignment = 0; + targhead->control = control; + ok = !update_secur_descr(vol, target, ni); + free(target); + } + return (ok); } /* - * Return the security descriptor of a file - * This is intended to be similar to GetFileSecurity() from Win32 - * in order to facilitate the development of portable tools + * Return the security descriptor of a file + * This is intended to be similar to GetFileSecurity() from Win32 + * in order to facilitate the development of portable tools * - * returns zero if unsuccessful (following Win32 conventions) - * -1 if no securid - * the securid if any + * returns zero if unsuccessful (following Win32 conventions) + * -1 if no securid + * the securid if any * * The Win32 API is : * @@ -5116,55 +4667,49 @@ static BOOL mergesecurityattr( ntfs_volume *vol, const char *oldattr, * */ -int ntfs_get_file_security( struct SECURITY_API *scapi, - const char *path, u32 selection, - char *buf, u32 buflen, u32 *psize ) +int ntfs_get_file_security(struct SECURITY_API *scapi, + const char *path, u32 selection, + char *buf, u32 buflen, u32 *psize) { - ntfs_inode *ni; - char *attr; - int res; + ntfs_inode *ni; + char *attr; + int res; - res = 0; /* default return */ - if ( scapi && ( scapi->magic == MAGIC_API ) ) - { - ni = ntfs_pathname_to_inode( scapi->security.vol, NULL, path ); - if ( ni ) - { - attr = getsecurityattr( scapi->security.vol, ni ); - if ( attr ) - { - if ( feedsecurityattr( attr, selection, - buf, buflen, psize ) ) - { - if ( test_nino_flag( ni, v3_Extensions ) - && ni->security_id ) - res = le32_to_cpu( - ni->security_id ); - else - res = -1; - } - free( attr ); - } - ntfs_inode_close( ni ); - } - else - errno = ENOENT; - if ( !res ) *psize = 0; - } - else - errno = EINVAL; /* do not clear *psize */ - return ( res ); + res = 0; /* default return */ + if (scapi && (scapi->magic == MAGIC_API)) { + ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path); + if (ni) { + attr = getsecurityattr(scapi->security.vol, ni); + if (attr) { + if (feedsecurityattr(attr,selection, + buf,buflen,psize)) { + if (test_nino_flag(ni, v3_Extensions) + && ni->security_id) + res = le32_to_cpu( + ni->security_id); + else + res = -1; + } + free(attr); + } + ntfs_inode_close(ni); + } else + errno = ENOENT; + if (!res) *psize = 0; + } else + errno = EINVAL; /* do not clear *psize */ + return (res); } /* - * Set the security descriptor of a file or directory - * This is intended to be similar to SetFileSecurity() from Win32 - * in order to facilitate the development of portable tools + * Set the security descriptor of a file or directory + * This is intended to be similar to SetFileSecurity() from Win32 + * in order to facilitate the development of portable tools * - * returns zero if unsuccessful (following Win32 conventions) - * -1 if no securid - * the securid if any + * returns zero if unsuccessful (following Win32 conventions) + * -1 if no securid + * the securid if any * * The Win32 API is : * @@ -5175,72 +4720,65 @@ int ntfs_get_file_security( struct SECURITY_API *scapi, * ); */ -int ntfs_set_file_security( struct SECURITY_API *scapi, - const char *path, u32 selection, const char *attr ) +int ntfs_set_file_security(struct SECURITY_API *scapi, + const char *path, u32 selection, const char *attr) { - const SECURITY_DESCRIPTOR_RELATIVE *phead; - ntfs_inode *ni; - int attrsz; - BOOL missing; - char *oldattr; - int res; + const SECURITY_DESCRIPTOR_RELATIVE *phead; + ntfs_inode *ni; + int attrsz; + BOOL missing; + char *oldattr; + int res; - res = 0; /* default return */ - if ( scapi && ( scapi->magic == MAGIC_API ) && attr ) - { - phead = ( const SECURITY_DESCRIPTOR_RELATIVE* )attr; - attrsz = ntfs_attr_size( attr ); - /* if selected, owner and group must be present or defaulted */ - missing = ( ( selection & OWNER_SECURITY_INFORMATION ) - && !phead->owner - && !( phead->control & SE_OWNER_DEFAULTED ) ) - || ( ( selection & GROUP_SECURITY_INFORMATION ) - && !phead->group - && !( phead->control & SE_GROUP_DEFAULTED ) ); - if ( !missing - && ( phead->control & SE_SELF_RELATIVE ) - && ntfs_valid_descr( attr, attrsz ) ) - { - ni = ntfs_pathname_to_inode( scapi->security.vol, - NULL, path ); - if ( ni ) - { - oldattr = getsecurityattr( scapi->security.vol, - ni ); - if ( oldattr ) - { - if ( mergesecurityattr( - scapi->security.vol, - oldattr, attr, - selection, ni ) ) - { - if ( test_nino_flag( ni, - v3_Extensions ) ) - res = le32_to_cpu( - ni->security_id ); - else - res = -1; - } - free( oldattr ); - } - ntfs_inode_close( ni ); - } - } - else - errno = EINVAL; - } - else - errno = EINVAL; - return ( res ); + res = 0; /* default return */ + if (scapi && (scapi->magic == MAGIC_API) && attr) { + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr; + attrsz = ntfs_attr_size(attr); + /* if selected, owner and group must be present or defaulted */ + missing = ((selection & OWNER_SECURITY_INFORMATION) + && !phead->owner + && !(phead->control & SE_OWNER_DEFAULTED)) + || ((selection & GROUP_SECURITY_INFORMATION) + && !phead->group + && !(phead->control & SE_GROUP_DEFAULTED)); + if (!missing + && (phead->control & SE_SELF_RELATIVE) + && ntfs_valid_descr(attr, attrsz)) { + ni = ntfs_pathname_to_inode(scapi->security.vol, + NULL, path); + if (ni) { + oldattr = getsecurityattr(scapi->security.vol, + ni); + if (oldattr) { + if (mergesecurityattr( + scapi->security.vol, + oldattr, attr, + selection, ni)) { + if (test_nino_flag(ni, + v3_Extensions)) + res = le32_to_cpu( + ni->security_id); + else + res = -1; + } + free(oldattr); + } + ntfs_inode_close(ni); + } + } else + errno = EINVAL; + } else + errno = EINVAL; + return (res); } /* - * Return the attributes of a file - * This is intended to be similar to GetFileAttributes() from Win32 - * in order to facilitate the development of portable tools + * Return the attributes of a file + * This is intended to be similar to GetFileAttributes() from Win32 + * in order to facilitate the development of portable tools * - * returns -1 if unsuccessful (Win32 : INVALID_FILE_ATTRIBUTES) + * returns -1 if unsuccessful (Win32 : INVALID_FILE_ATTRIBUTES) * * The Win32 API is : * @@ -5249,45 +4787,41 @@ int ntfs_set_file_security( struct SECURITY_API *scapi, * ); */ -int ntfs_get_file_attributes( struct SECURITY_API *scapi, const char *path ) +int ntfs_get_file_attributes(struct SECURITY_API *scapi, const char *path) { - ntfs_inode *ni; - s32 attrib; + ntfs_inode *ni; + s32 attrib; - attrib = -1; /* default return */ - if ( scapi && ( scapi->magic == MAGIC_API ) && path ) - { - ni = ntfs_pathname_to_inode( scapi->security.vol, NULL, path ); - if ( ni ) - { - attrib = le32_to_cpu( ni->flags ); - if ( ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ) - attrib |= const_le32_to_cpu( FILE_ATTR_DIRECTORY ); - else - attrib &= ~const_le32_to_cpu( FILE_ATTR_DIRECTORY ); - if ( !attrib ) - attrib |= const_le32_to_cpu( FILE_ATTR_NORMAL ); + attrib = -1; /* default return */ + if (scapi && (scapi->magic == MAGIC_API) && path) { + ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path); + if (ni) { + attrib = le32_to_cpu(ni->flags); + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + attrib |= const_le32_to_cpu(FILE_ATTR_DIRECTORY); + else + attrib &= ~const_le32_to_cpu(FILE_ATTR_DIRECTORY); + if (!attrib) + attrib |= const_le32_to_cpu(FILE_ATTR_NORMAL); - ntfs_inode_close( ni ); - } - else - errno = ENOENT; - } - else - errno = EINVAL; /* do not clear *psize */ - return ( attrib ); + ntfs_inode_close(ni); + } else + errno = ENOENT; + } else + errno = EINVAL; /* do not clear *psize */ + return (attrib); } /* - * Set attributes to a file or directory - * This is intended to be similar to SetFileAttributes() from Win32 - * in order to facilitate the development of portable tools + * Set attributes to a file or directory + * This is intended to be similar to SetFileAttributes() from Win32 + * in order to facilitate the development of portable tools * - * Only a few flags can be set (same list as Win32) + * Only a few flags can be set (same list as Win32) * - * returns zero if unsuccessful (following Win32 conventions) - * nonzero if successful + * returns zero if unsuccessful (following Win32 conventions) + * nonzero if successful * * The Win32 API is : * @@ -5297,401 +4831,352 @@ int ntfs_get_file_attributes( struct SECURITY_API *scapi, const char *path ) * ); */ -BOOL ntfs_set_file_attributes( struct SECURITY_API *scapi, - const char *path, s32 attrib ) +BOOL ntfs_set_file_attributes(struct SECURITY_API *scapi, + const char *path, s32 attrib) { - ntfs_inode *ni; - le32 settable; - ATTR_FLAGS dirflags; - int res; + ntfs_inode *ni; + le32 settable; + ATTR_FLAGS dirflags; + int res; - res = 0; /* default return */ - if ( scapi && ( scapi->magic == MAGIC_API ) && path ) - { - ni = ntfs_pathname_to_inode( scapi->security.vol, NULL, path ); - if ( ni ) - { - settable = FILE_ATTR_SETTABLE; - if ( ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ) - { - /* - * Accept changing compression for a directory - * and set index root accordingly - */ - settable |= FILE_ATTR_COMPRESSED; - if ( ( ni->flags ^ cpu_to_le32( attrib ) ) - & FILE_ATTR_COMPRESSED ) - { - if ( ni->flags & FILE_ATTR_COMPRESSED ) - dirflags = const_cpu_to_le16( 0 ); - else - dirflags = ATTR_IS_COMPRESSED; - res = ntfs_attr_set_flags( ni, - AT_INDEX_ROOT, - NTFS_INDEX_I30, 4, - dirflags, - ATTR_COMPRESSION_MASK ); - } - } - if ( !res ) - { - ni->flags = ( ni->flags & ~settable ) - | ( cpu_to_le32( attrib ) & settable ); - NInoSetDirty( ni ); - } - if ( !ntfs_inode_close( ni ) ) - res = -1; - } - else - errno = ENOENT; - } - return ( res ); + res = 0; /* default return */ + if (scapi && (scapi->magic == MAGIC_API) && path) { + ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path); + if (ni) { + settable = FILE_ATTR_SETTABLE; + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + /* + * Accept changing compression for a directory + * and set index root accordingly + */ + settable |= FILE_ATTR_COMPRESSED; + if ((ni->flags ^ cpu_to_le32(attrib)) + & FILE_ATTR_COMPRESSED) { + if (ni->flags & FILE_ATTR_COMPRESSED) + dirflags = const_cpu_to_le16(0); + else + dirflags = ATTR_IS_COMPRESSED; + res = ntfs_attr_set_flags(ni, + AT_INDEX_ROOT, + NTFS_INDEX_I30, 4, + dirflags, + ATTR_COMPRESSION_MASK); + } + } + if (!res) { + ni->flags = (ni->flags & ~settable) + | (cpu_to_le32(attrib) & settable); + NInoSetDirty(ni); + } + if (!ntfs_inode_close(ni)) + res = -1; + } else + errno = ENOENT; + } + return (res); } -BOOL ntfs_read_directory( struct SECURITY_API *scapi, - const char *path, ntfs_filldir_t callback, void *context ) +BOOL ntfs_read_directory(struct SECURITY_API *scapi, + const char *path, ntfs_filldir_t callback, void *context) { - ntfs_inode *ni; - BOOL ok; - s64 pos; + ntfs_inode *ni; + BOOL ok; + s64 pos; - ok = FALSE; /* default return */ - if ( scapi && ( scapi->magic == MAGIC_API ) && callback ) - { - ni = ntfs_pathname_to_inode( scapi->security.vol, NULL, path ); - if ( ni ) - { - if ( ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ) - { - pos = 0; - ntfs_readdir( ni, &pos, context, callback ); - ok = !ntfs_inode_close( ni ); - } - else - { - ntfs_inode_close( ni ); - errno = ENOTDIR; - } - } - else - errno = ENOENT; - } - else - errno = EINVAL; /* do not clear *psize */ - return ( ok ); + ok = FALSE; /* default return */ + if (scapi && (scapi->magic == MAGIC_API) && callback) { + ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path); + if (ni) { + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + pos = 0; + ntfs_readdir(ni,&pos,context,callback); + ok = !ntfs_inode_close(ni); + } else { + ntfs_inode_close(ni); + errno = ENOTDIR; + } + } else + errno = ENOENT; + } else + errno = EINVAL; /* do not clear *psize */ + return (ok); } /* - * read $SDS (for auditing security data) + * read $SDS (for auditing security data) * - * Returns the number or read bytes, or -1 if there is an error + * Returns the number or read bytes, or -1 if there is an error */ -int ntfs_read_sds( struct SECURITY_API *scapi, - char *buf, u32 size, u32 offset ) +int ntfs_read_sds(struct SECURITY_API *scapi, + char *buf, u32 size, u32 offset) { - int got; + int got; - got = -1; /* default return */ - if ( scapi && ( scapi->magic == MAGIC_API ) ) - { - if ( scapi->security.vol->secure_ni ) - got = ntfs_local_read( scapi->security.vol->secure_ni, - STREAM_SDS, 4, buf, size, offset ); - else - errno = EOPNOTSUPP; - } - else - errno = EINVAL; - return ( got ); + got = -1; /* default return */ + if (scapi && (scapi->magic == MAGIC_API)) { + if (scapi->security.vol->secure_ni) + got = ntfs_local_read(scapi->security.vol->secure_ni, + STREAM_SDS, 4, buf, size, offset); + else + errno = EOPNOTSUPP; + } else + errno = EINVAL; + return (got); } /* - * read $SII (for auditing security data) + * read $SII (for auditing security data) * - * Returns next entry, or NULL if there is an error + * Returns next entry, or NULL if there is an error */ -INDEX_ENTRY *ntfs_read_sii( struct SECURITY_API *scapi, - INDEX_ENTRY *entry ) +INDEX_ENTRY *ntfs_read_sii(struct SECURITY_API *scapi, + INDEX_ENTRY *entry) { - SII_INDEX_KEY key; - INDEX_ENTRY *ret; - BOOL found; - ntfs_index_context *xsii; + SII_INDEX_KEY key; + INDEX_ENTRY *ret; + BOOL found; + ntfs_index_context *xsii; - ret = ( INDEX_ENTRY* )NULL; /* default return */ - if ( scapi && ( scapi->magic == MAGIC_API ) ) - { - xsii = scapi->security.vol->secure_xsii; - if ( xsii ) - { - if ( !entry ) - { - key.security_id = const_cpu_to_le32( 0 ); - found = !ntfs_index_lookup( ( char* ) & key, - sizeof( SII_INDEX_KEY ), xsii ); - /* not supposed to find */ - if ( !found && ( errno == ENOENT ) ) - ret = xsii->entry; - } - else - ret = ntfs_index_next( entry, xsii ); - if ( !ret ) - errno = ENODATA; - } - else - errno = EOPNOTSUPP; - } - else - errno = EINVAL; - return ( ret ); + ret = (INDEX_ENTRY*)NULL; /* default return */ + if (scapi && (scapi->magic == MAGIC_API)) { + xsii = scapi->security.vol->secure_xsii; + if (xsii) { + if (!entry) { + key.security_id = const_cpu_to_le32(0); + found = !ntfs_index_lookup((char*)&key, + sizeof(SII_INDEX_KEY), xsii); + /* not supposed to find */ + if (!found && (errno == ENOENT)) + ret = xsii->entry; + } else + ret = ntfs_index_next(entry,xsii); + if (!ret) + errno = ENODATA; + } else + errno = EOPNOTSUPP; + } else + errno = EINVAL; + return (ret); } /* - * read $SDH (for auditing security data) + * read $SDH (for auditing security data) * - * Returns next entry, or NULL if there is an error + * Returns next entry, or NULL if there is an error */ -INDEX_ENTRY *ntfs_read_sdh( struct SECURITY_API *scapi, - INDEX_ENTRY *entry ) +INDEX_ENTRY *ntfs_read_sdh(struct SECURITY_API *scapi, + INDEX_ENTRY *entry) { - SDH_INDEX_KEY key; - INDEX_ENTRY *ret; - BOOL found; - ntfs_index_context *xsdh; + SDH_INDEX_KEY key; + INDEX_ENTRY *ret; + BOOL found; + ntfs_index_context *xsdh; - ret = ( INDEX_ENTRY* )NULL; /* default return */ - if ( scapi && ( scapi->magic == MAGIC_API ) ) - { - xsdh = scapi->security.vol->secure_xsdh; - if ( xsdh ) - { - if ( !entry ) - { - key.hash = const_cpu_to_le32( 0 ); - key.security_id = const_cpu_to_le32( 0 ); - found = !ntfs_index_lookup( ( char* ) & key, - sizeof( SDH_INDEX_KEY ), xsdh ); - /* not supposed to find */ - if ( !found && ( errno == ENOENT ) ) - ret = xsdh->entry; - } - else - ret = ntfs_index_next( entry, xsdh ); - if ( !ret ) - errno = ENODATA; - } - else errno = ENOTSUP; - } - else - errno = EINVAL; - return ( ret ); + ret = (INDEX_ENTRY*)NULL; /* default return */ + if (scapi && (scapi->magic == MAGIC_API)) { + xsdh = scapi->security.vol->secure_xsdh; + if (xsdh) { + if (!entry) { + key.hash = const_cpu_to_le32(0); + key.security_id = const_cpu_to_le32(0); + found = !ntfs_index_lookup((char*)&key, + sizeof(SDH_INDEX_KEY), xsdh); + /* not supposed to find */ + if (!found && (errno == ENOENT)) + ret = xsdh->entry; + } else + ret = ntfs_index_next(entry,xsdh); + if (!ret) + errno = ENODATA; + } else errno = ENOTSUP; + } else + errno = EINVAL; + return (ret); } /* - * Get the mapped user SID - * A buffer of 40 bytes has to be supplied + * Get the mapped user SID + * A buffer of 40 bytes has to be supplied * - * returns the size of the SID, or zero and errno set if not found + * returns the size of the SID, or zero and errno set if not found */ -int ntfs_get_usid( struct SECURITY_API *scapi, uid_t uid, char *buf ) +int ntfs_get_usid(struct SECURITY_API *scapi, uid_t uid, char *buf) { - const SID *usid; - BIGSID defusid; - int size; + const SID *usid; + BIGSID defusid; + int size; - size = 0; - if ( scapi && ( scapi->magic == MAGIC_API ) ) - { - usid = ntfs_find_usid( scapi->security.mapping[MAPUSERS], uid, ( SID* ) & defusid ); - if ( usid ) - { - size = ntfs_sid_size( usid ); - memcpy( buf, usid, size ); - } - else - errno = ENODATA; - } - else - errno = EINVAL; - return ( size ); + size = 0; + if (scapi && (scapi->magic == MAGIC_API)) { + usid = ntfs_find_usid(scapi->security.mapping[MAPUSERS], uid, (SID*)&defusid); + if (usid) { + size = ntfs_sid_size(usid); + memcpy(buf,usid,size); + } else + errno = ENODATA; + } else + errno = EINVAL; + return (size); } /* - * Get the mapped group SID - * A buffer of 40 bytes has to be supplied + * Get the mapped group SID + * A buffer of 40 bytes has to be supplied * - * returns the size of the SID, or zero and errno set if not found + * returns the size of the SID, or zero and errno set if not found */ -int ntfs_get_gsid( struct SECURITY_API *scapi, gid_t gid, char *buf ) +int ntfs_get_gsid(struct SECURITY_API *scapi, gid_t gid, char *buf) { - const SID *gsid; - BIGSID defgsid; - int size; + const SID *gsid; + BIGSID defgsid; + int size; - size = 0; - if ( scapi && ( scapi->magic == MAGIC_API ) ) - { - gsid = ntfs_find_gsid( scapi->security.mapping[MAPGROUPS], gid, ( SID* ) & defgsid ); - if ( gsid ) - { - size = ntfs_sid_size( gsid ); - memcpy( buf, gsid, size ); - } - else - errno = ENODATA; - } - else - errno = EINVAL; - return ( size ); + size = 0; + if (scapi && (scapi->magic == MAGIC_API)) { + gsid = ntfs_find_gsid(scapi->security.mapping[MAPGROUPS], gid, (SID*)&defgsid); + if (gsid) { + size = ntfs_sid_size(gsid); + memcpy(buf,gsid,size); + } else + errno = ENODATA; + } else + errno = EINVAL; + return (size); } /* - * Get the user mapped to a SID + * Get the user mapped to a SID * - * returns the uid, or -1 if not found + * returns the uid, or -1 if not found */ -int ntfs_get_user( struct SECURITY_API *scapi, const SID *usid ) +int ntfs_get_user(struct SECURITY_API *scapi, const SID *usid) { - int uid; + int uid; - uid = -1; - if ( scapi && ( scapi->magic == MAGIC_API ) && ntfs_valid_sid( usid ) ) - { - if ( ntfs_same_sid( usid, adminsid ) ) - uid = 0; - else - { - uid = ntfs_find_user( scapi->security.mapping[MAPUSERS], usid ); - if ( !uid ) - { - uid = -1; - errno = ENODATA; - } - } - } - else - errno = EINVAL; - return ( uid ); + uid = -1; + if (scapi && (scapi->magic == MAGIC_API) && ntfs_valid_sid(usid)) { + if (ntfs_same_sid(usid,adminsid)) + uid = 0; + else { + uid = ntfs_find_user(scapi->security.mapping[MAPUSERS], usid); + if (!uid) { + uid = -1; + errno = ENODATA; + } + } + } else + errno = EINVAL; + return (uid); } /* - * Get the group mapped to a SID + * Get the group mapped to a SID * - * returns the uid, or -1 if not found + * returns the uid, or -1 if not found */ -int ntfs_get_group( struct SECURITY_API *scapi, const SID *gsid ) +int ntfs_get_group(struct SECURITY_API *scapi, const SID *gsid) { - int gid; + int gid; - gid = -1; - if ( scapi && ( scapi->magic == MAGIC_API ) && ntfs_valid_sid( gsid ) ) - { - if ( ntfs_same_sid( gsid, adminsid ) ) - gid = 0; - else - { - gid = ntfs_find_group( scapi->security.mapping[MAPGROUPS], gsid ); - if ( !gid ) - { - gid = -1; - errno = ENODATA; - } - } - } - else - errno = EINVAL; - return ( gid ); + gid = -1; + if (scapi && (scapi->magic == MAGIC_API) && ntfs_valid_sid(gsid)) { + if (ntfs_same_sid(gsid,adminsid)) + gid = 0; + else { + gid = ntfs_find_group(scapi->security.mapping[MAPGROUPS], gsid); + if (!gid) { + gid = -1; + errno = ENODATA; + } + } + } else + errno = EINVAL; + return (gid); } /* - * Initializations before calling ntfs_get_file_security() - * ntfs_set_file_security() and ntfs_read_directory() + * Initializations before calling ntfs_get_file_security() + * ntfs_set_file_security() and ntfs_read_directory() * - * Only allowed for root + * Only allowed for root * - * Returns an (obscured) struct SECURITY_API* needed for further calls - * NULL if not root (EPERM) or device is mounted (EBUSY) + * Returns an (obscured) struct SECURITY_API* needed for further calls + * NULL if not root (EPERM) or device is mounted (EBUSY) */ -struct SECURITY_API *ntfs_initialize_file_security( const char *device, - int flags ) +struct SECURITY_API *ntfs_initialize_file_security(const char *device, + int flags) { - ntfs_volume *vol; - unsigned long mntflag; - int mnt; - struct SECURITY_API *scapi; - struct SECURITY_CONTEXT *scx; + ntfs_volume *vol; + unsigned long mntflag; + int mnt; + struct SECURITY_API *scapi; + struct SECURITY_CONTEXT *scx; - scapi = ( struct SECURITY_API* )NULL; - mnt = ntfs_check_if_mounted( device, &mntflag ); - if ( !mnt && !( mntflag & NTFS_MF_MOUNTED ) && !getuid() ) - { - vol = ntfs_mount( device, flags ); - if ( vol ) - { - scapi = ( struct SECURITY_API* ) - ntfs_malloc( sizeof( struct SECURITY_API ) ); - if ( !ntfs_volume_get_free_space( vol ) - && scapi ) - { - scapi->magic = MAGIC_API; - scapi->seccache = ( struct PERMISSIONS_CACHE* )NULL; - scx = &scapi->security; - scx->vol = vol; - scx->uid = getuid(); - scx->gid = getgid(); - scx->pseccache = &scapi->seccache; - scx->vol->secure_flags = 0; - /* accept no mapping and no $Secure */ - ntfs_build_mapping( scx, ( const char* )NULL, TRUE ); - ntfs_open_secure( vol ); - } - else - { - if ( scapi ) - free( scapi ); - else - errno = ENOMEM; - mnt = ntfs_umount( vol, FALSE ); - scapi = ( struct SECURITY_API* )NULL; - } - } - } - else if ( getuid() ) - errno = EPERM; - else - errno = EBUSY; - return ( scapi ); + scapi = (struct SECURITY_API*)NULL; + mnt = ntfs_check_if_mounted(device, &mntflag); + if (!mnt && !(mntflag & NTFS_MF_MOUNTED) && !getuid()) { + vol = ntfs_mount(device, flags); + if (vol) { + scapi = (struct SECURITY_API*) + ntfs_malloc(sizeof(struct SECURITY_API)); + if (!ntfs_volume_get_free_space(vol) + && scapi) { + scapi->magic = MAGIC_API; + scapi->seccache = (struct PERMISSIONS_CACHE*)NULL; + scx = &scapi->security; + scx->vol = vol; + scx->uid = getuid(); + scx->gid = getgid(); + scx->pseccache = &scapi->seccache; + scx->vol->secure_flags = 0; + /* accept no mapping and no $Secure */ + ntfs_build_mapping(scx,(const char*)NULL,TRUE); + ntfs_open_secure(vol); + } else { + if (scapi) + free(scapi); + else + errno = ENOMEM; + mnt = ntfs_umount(vol,FALSE); + scapi = (struct SECURITY_API*)NULL; + } + } + } else + if (getuid()) + errno = EPERM; + else + errno = EBUSY; + return (scapi); } /* - * Leaving after ntfs_initialize_file_security() + * Leaving after ntfs_initialize_file_security() * - * Returns FALSE if FAILED + * Returns FALSE if FAILED */ -BOOL ntfs_leave_file_security( struct SECURITY_API *scapi ) +BOOL ntfs_leave_file_security(struct SECURITY_API *scapi) { - int ok; - ntfs_volume *vol; + int ok; + ntfs_volume *vol; - ok = FALSE; - if ( scapi && ( scapi->magic == MAGIC_API ) && scapi->security.vol ) - { - vol = scapi->security.vol; - ntfs_close_secure( &scapi->security ); - free( scapi ); - if ( !ntfs_umount( vol, 0 ) ) - ok = TRUE; - } - return ( ok ); + ok = FALSE; + if (scapi && (scapi->magic == MAGIC_API) && scapi->security.vol) { + vol = scapi->security.vol; + ntfs_close_secure(&scapi->security); + free(scapi); + if (!ntfs_umount(vol, 0)) + ok = TRUE; + } + return (ok); } diff --git a/source/libntfs/security.h b/source/libntfs/security.h index b4098d96..4f3b5c54 100644 --- a/source/libntfs/security.h +++ b/source/libntfs/security.h @@ -1,5 +1,5 @@ /* - * security.h - Exports for handling security/ACLs in NTFS. + * security.h - Exports for handling security/ACLs in NTFS. * Originated from the Linux-NTFS project. * * Copyright (c) 2004 Anton Altaparmakov @@ -40,7 +40,7 @@ typedef u32 be32; #if __BYTE_ORDER == __LITTLE_ENDIAN #define const_cpu_to_be16(x) ((((x) & 255L) << 8) + (((x) >> 8) & 255L)) #define const_cpu_to_be32(x) ((((x) & 255L) << 24) + (((x) & 0xff00L) << 8) \ - + (((x) >> 8) & 0xff00L) + (((x) >> 24) & 255L)) + + (((x) >> 8) & 0xff00L) + (((x) >> 24) & 255L)) #else #define const_cpu_to_be16(x) (x) #define const_cpu_to_be32(x) (x) @@ -50,284 +50,270 @@ typedef u32 be32; * item in the mapping list */ -struct MAPPING -{ - struct MAPPING *next; - int xid; /* linux id : uid or gid */ - SID *sid; /* Windows id : usid or gsid */ - int grcnt; /* group count (for users only) */ - gid_t *groups; /* groups which the user is member of */ +struct MAPPING { + struct MAPPING *next; + int xid; /* linux id : uid or gid */ + SID *sid; /* Windows id : usid or gsid */ + int grcnt; /* group count (for users only) */ + gid_t *groups; /* groups which the user is member of */ }; /* - * Entry in the permissions cache - * Note : this cache is not organized as a generic cache + * Entry in the permissions cache + * Note : this cache is not organized as a generic cache */ -struct CACHED_PERMISSIONS -{ - uid_t uid; - gid_t gid; - le32 inh_fileid; - le32 inh_dirid; +struct CACHED_PERMISSIONS { + uid_t uid; + gid_t gid; + le32 inh_fileid; + le32 inh_dirid; #if POSIXACLS - struct POSIX_SECURITY *pxdesc; - unsigned int pxdescsize: 16; + struct POSIX_SECURITY *pxdesc; + unsigned int pxdescsize:16; #endif - unsigned int mode: 12; - unsigned int valid: 1; + unsigned int mode:12; + unsigned int valid:1; } ; /* - * Entry in the permissions cache for directories with no security_id + * Entry in the permissions cache for directories with no security_id */ -struct CACHED_PERMISSIONS_LEGACY -{ - struct CACHED_PERMISSIONS_LEGACY *next; - struct CACHED_PERMISSIONS_LEGACY *previous; - void *variable; - size_t varsize; - /* above fields must match "struct CACHED_GENERIC" */ - u64 mft_no; - struct CACHED_PERMISSIONS perm; +struct CACHED_PERMISSIONS_LEGACY { + struct CACHED_PERMISSIONS_LEGACY *next; + struct CACHED_PERMISSIONS_LEGACY *previous; + void *variable; + size_t varsize; + /* above fields must match "struct CACHED_GENERIC" */ + u64 mft_no; + struct CACHED_PERMISSIONS perm; } ; /* - * Entry in the securid cache + * Entry in the securid cache */ -struct CACHED_SECURID -{ - struct CACHED_SECURID *next; - struct CACHED_SECURID *previous; - void *variable; - size_t varsize; - /* above fields must match "struct CACHED_GENERIC" */ - uid_t uid; - gid_t gid; - unsigned int dmode; - le32 securid; +struct CACHED_SECURID { + struct CACHED_SECURID *next; + struct CACHED_SECURID *previous; + void *variable; + size_t varsize; + /* above fields must match "struct CACHED_GENERIC" */ + uid_t uid; + gid_t gid; + unsigned int dmode; + le32 securid; } ; /* - * Header of the security cache - * (has no cache structure by itself) + * Header of the security cache + * (has no cache structure by itself) */ -struct CACHED_PERMISSIONS_HEADER -{ - unsigned int last; - /* statistics for permissions */ - unsigned long p_writes; - unsigned long p_reads; - unsigned long p_hits; +struct CACHED_PERMISSIONS_HEADER { + unsigned int last; + /* statistics for permissions */ + unsigned long p_writes; + unsigned long p_reads; + unsigned long p_hits; } ; /* - * The whole permissions cache + * The whole permissions cache */ -struct PERMISSIONS_CACHE -{ - struct CACHED_PERMISSIONS_HEADER head; - struct CACHED_PERMISSIONS *cachetable[1]; /* array of variable size */ +struct PERMISSIONS_CACHE { + struct CACHED_PERMISSIONS_HEADER head; + struct CACHED_PERMISSIONS *cachetable[1]; /* array of variable size */ } ; /* - * Security flags values + * Security flags values */ -enum -{ - SECURITY_DEFAULT, /* rely on fuse for permissions checking */ - SECURITY_RAW, /* force same ownership/permissions on files */ - SECURITY_ADDSECURIDS, /* upgrade old security descriptors */ - SECURITY_STATICGRPS, /* use static groups for access control */ - SECURITY_WANTED /* a security related option was present */ +enum { + SECURITY_DEFAULT, /* rely on fuse for permissions checking */ + SECURITY_RAW, /* force same ownership/permissions on files */ + SECURITY_ADDSECURIDS, /* upgrade old security descriptors */ + SECURITY_STATICGRPS, /* use static groups for access control */ + SECURITY_WANTED /* a security related option was present */ } ; /* - * Security context, needed by most security functions + * Security context, needed by most security functions */ enum { MAPUSERS, MAPGROUPS, MAPCOUNT } ; -struct SECURITY_CONTEXT -{ - ntfs_volume *vol; - struct MAPPING *mapping[MAPCOUNT]; - struct PERMISSIONS_CACHE **pseccache; - uid_t uid; /* uid of user requesting (not the mounter) */ - gid_t gid; /* gid of user requesting (not the mounter) */ - pid_t tid; /* thread id of thread requesting */ - mode_t umask; /* umask of requesting thread */ -} ; +struct SECURITY_CONTEXT { + ntfs_volume *vol; + struct MAPPING *mapping[MAPCOUNT]; + struct PERMISSIONS_CACHE **pseccache; + uid_t uid; /* uid of user requesting (not the mounter) */ + gid_t gid; /* gid of user requesting (not the mounter) */ + pid_t tid; /* thread id of thread requesting */ + mode_t umask; /* umask of requesting thread */ + } ; #if POSIXACLS /* - * Posix ACL structures + * Posix ACL structures */ - -struct POSIX_ACE -{ - u16 tag; - u16 perms; - s32 id; + +struct POSIX_ACE { + u16 tag; + u16 perms; + s32 id; +} ; + +struct POSIX_ACL { + u8 version; + u8 flags; + u16 filler; + struct POSIX_ACE ace[0]; } ; -struct POSIX_ACL -{ - u8 version; - u8 flags; - u16 filler; - struct POSIX_ACE ace[0]; +struct POSIX_SECURITY { + mode_t mode; + int acccnt; + int defcnt; + int firstdef; + u16 tagsset; + struct POSIX_ACL acl; } ; - -struct POSIX_SECURITY -{ - mode_t mode; - int acccnt; - int defcnt; - int firstdef; - u16 tagsset; - struct POSIX_ACL acl; -} ; - + /* - * Posix tags, cpu-endian 16 bits + * Posix tags, cpu-endian 16 bits */ - -enum -{ - POSIX_ACL_USER_OBJ = 1, - POSIX_ACL_USER = 2, - POSIX_ACL_GROUP_OBJ = 4, - POSIX_ACL_GROUP = 8, - POSIX_ACL_MASK = 16, - POSIX_ACL_OTHER = 32, - POSIX_ACL_SPECIAL = 64 /* internal use only */ + +enum { + POSIX_ACL_USER_OBJ = 1, + POSIX_ACL_USER = 2, + POSIX_ACL_GROUP_OBJ = 4, + POSIX_ACL_GROUP = 8, + POSIX_ACL_MASK = 16, + POSIX_ACL_OTHER = 32, + POSIX_ACL_SPECIAL = 64 /* internal use only */ } ; #define POSIX_ACL_EXTENSIONS (POSIX_ACL_USER | POSIX_ACL_GROUP | POSIX_ACL_MASK) - + /* - * Posix permissions, cpu-endian 16 bits + * Posix permissions, cpu-endian 16 bits */ - -enum -{ - POSIX_PERM_X = 1, - POSIX_PERM_W = 2, - POSIX_PERM_R = 4, - POSIX_PERM_DENIAL = 64 /* internal use only */ + +enum { + POSIX_PERM_X = 1, + POSIX_PERM_W = 2, + POSIX_PERM_R = 4, + POSIX_PERM_DENIAL = 64 /* internal use only */ } ; #define POSIX_VERSION 2 #endif -extern BOOL ntfs_guid_is_zero( const GUID *guid ); -extern char *ntfs_guid_to_mbs( const GUID *guid, char *guid_str ); +extern BOOL ntfs_guid_is_zero(const GUID *guid); +extern char *ntfs_guid_to_mbs(const GUID *guid, char *guid_str); /** * ntfs_sid_is_valid - determine if a SID is valid - * @sid: SID for which to determine if it is valid + * @sid: SID for which to determine if it is valid * * Determine if the SID pointed to by @sid is valid. * * Return TRUE if it is valid and FALSE otherwise. */ -static __inline__ BOOL ntfs_sid_is_valid( const SID *sid ) +static __inline__ BOOL ntfs_sid_is_valid(const SID *sid) { - if ( !sid || sid->revision != SID_REVISION || - sid->sub_authority_count > SID_MAX_SUB_AUTHORITIES ) - return FALSE; - return TRUE; + if (!sid || sid->revision != SID_REVISION || + sid->sub_authority_count > SID_MAX_SUB_AUTHORITIES) + return FALSE; + return TRUE; } -extern int ntfs_sid_to_mbs_size( const SID *sid ); -extern char *ntfs_sid_to_mbs( const SID *sid, char *sid_str, - size_t sid_str_size ); -extern void ntfs_generate_guid( GUID *guid ); -extern int ntfs_sd_add_everyone( ntfs_inode *ni ); +extern int ntfs_sid_to_mbs_size(const SID *sid); +extern char *ntfs_sid_to_mbs(const SID *sid, char *sid_str, + size_t sid_str_size); +extern void ntfs_generate_guid(GUID *guid); +extern int ntfs_sd_add_everyone(ntfs_inode *ni); -extern le32 ntfs_security_hash( const SECURITY_DESCRIPTOR_RELATIVE *sd, - const u32 len ); +extern le32 ntfs_security_hash(const SECURITY_DESCRIPTOR_RELATIVE *sd, + const u32 len); -int ntfs_build_mapping( struct SECURITY_CONTEXT *scx, const char *usermap_path, - BOOL allowdef ); -int ntfs_get_owner_mode( struct SECURITY_CONTEXT *scx, - ntfs_inode *ni, struct stat* ); -int ntfs_set_mode( struct SECURITY_CONTEXT *scx, ntfs_inode *ni, mode_t mode ); -BOOL ntfs_allowed_as_owner( struct SECURITY_CONTEXT *scx, ntfs_inode *ni ); -int ntfs_allowed_access( struct SECURITY_CONTEXT *scx, - ntfs_inode *ni, int accesstype ); -BOOL old_ntfs_allowed_dir_access( struct SECURITY_CONTEXT *scx, - const char *path, int accesstype ); +int ntfs_build_mapping(struct SECURITY_CONTEXT *scx, const char *usermap_path, + BOOL allowdef); +int ntfs_get_owner_mode(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, struct stat*); +int ntfs_set_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, mode_t mode); +BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni); +int ntfs_allowed_access(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, int accesstype); +BOOL old_ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx, + const char *path, int accesstype); #if POSIXACLS -le32 ntfs_alloc_securid( struct SECURITY_CONTEXT *scx, - uid_t uid, gid_t gid, ntfs_inode *dir_ni, - mode_t mode, BOOL isdir ); +le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx, + uid_t uid, gid_t gid, ntfs_inode *dir_ni, + mode_t mode, BOOL isdir); #else -le32 ntfs_alloc_securid( struct SECURITY_CONTEXT *scx, - uid_t uid, gid_t gid, mode_t mode, BOOL isdir ); +le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx, + uid_t uid, gid_t gid, mode_t mode, BOOL isdir); #endif -int ntfs_set_owner( struct SECURITY_CONTEXT *scx, ntfs_inode *ni, - uid_t uid, gid_t gid ); -int ntfs_set_ownmod( struct SECURITY_CONTEXT *scx, - ntfs_inode *ni, uid_t uid, gid_t gid, mode_t mode ); +int ntfs_set_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + uid_t uid, gid_t gid); +int ntfs_set_ownmod(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, uid_t uid, gid_t gid, mode_t mode); #if POSIXACLS -int ntfs_set_owner_mode( struct SECURITY_CONTEXT *scx, - ntfs_inode *ni, uid_t uid, gid_t gid, - mode_t mode, struct POSIX_SECURITY *pxdesc ); +int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, uid_t uid, gid_t gid, + mode_t mode, struct POSIX_SECURITY *pxdesc); #else -int ntfs_set_owner_mode( struct SECURITY_CONTEXT *scx, - ntfs_inode *ni, uid_t uid, gid_t gid, mode_t mode ); +int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, uid_t uid, gid_t gid, mode_t mode); #endif -le32 ntfs_inherited_id( struct SECURITY_CONTEXT *scx, - ntfs_inode *dir_ni, BOOL fordir ); -int ntfs_open_secure( ntfs_volume *vol ); -void ntfs_close_secure( struct SECURITY_CONTEXT *scx ); +le32 ntfs_inherited_id(struct SECURITY_CONTEXT *scx, + ntfs_inode *dir_ni, BOOL fordir); +int ntfs_open_secure(ntfs_volume *vol); +void ntfs_close_secure(struct SECURITY_CONTEXT *scx); #if POSIXACLS -int ntfs_set_inherited_posix( struct SECURITY_CONTEXT *scx, - ntfs_inode *ni, uid_t uid, gid_t gid, - ntfs_inode *dir_ni, mode_t mode ); -int ntfs_get_posix_acl( struct SECURITY_CONTEXT *scx, ntfs_inode *ni, - const char *name, char *value, size_t size ); -int ntfs_set_posix_acl( struct SECURITY_CONTEXT *scx, ntfs_inode *ni, - const char *name, const char *value, size_t size, - int flags ); -int ntfs_remove_posix_acl( struct SECURITY_CONTEXT *scx, ntfs_inode *ni, - const char *name ); +int ntfs_set_inherited_posix(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, uid_t uid, gid_t gid, + ntfs_inode *dir_ni, mode_t mode); +int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *name, char *value, size_t size); +int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *name, const char *value, size_t size, + int flags); +int ntfs_remove_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *name); #endif -int ntfs_get_ntfs_acl( struct SECURITY_CONTEXT *scx, ntfs_inode *ni, - char *value, size_t size ); -int ntfs_set_ntfs_acl( struct SECURITY_CONTEXT *scx, ntfs_inode *ni, - const char *value, size_t size, int flags ); - -int ntfs_get_ntfs_attrib( ntfs_inode *ni, char *value, size_t size ); -int ntfs_set_ntfs_attrib( ntfs_inode *ni, - const char *value, size_t size, int flags ); +int ntfs_get_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + char *value, size_t size); +int ntfs_set_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *value, size_t size, int flags); +int ntfs_get_ntfs_attrib(ntfs_inode *ni, char *value, size_t size); +int ntfs_set_ntfs_attrib(ntfs_inode *ni, + const char *value, size_t size, int flags); + /* - * Security API for direct access to security descriptors - * based on Win32 API + * Security API for direct access to security descriptors + * based on Win32 API */ #define MAGIC_API 0x09042009 -struct SECURITY_API -{ - u32 magic; - struct SECURITY_CONTEXT security; - struct PERMISSIONS_CACHE *seccache; +struct SECURITY_API { + u32 magic; + struct SECURITY_CONTEXT security; + struct PERMISSIONS_CACHE *seccache; } ; /* @@ -336,36 +322,36 @@ struct SECURITY_API * native cpu representation. * When disk representation (le) is needed, use SE_DACL_PRESENT, etc. */ -enum { OWNER_SECURITY_INFORMATION = 1, - GROUP_SECURITY_INFORMATION = 2, - DACL_SECURITY_INFORMATION = 4, - SACL_SECURITY_INFORMATION = 8 - } ; +enum { OWNER_SECURITY_INFORMATION = 1, + GROUP_SECURITY_INFORMATION = 2, + DACL_SECURITY_INFORMATION = 4, + SACL_SECURITY_INFORMATION = 8 +} ; -int ntfs_get_file_security( struct SECURITY_API *scapi, - const char *path, u32 selection, - char *buf, u32 buflen, u32 *psize ); -int ntfs_set_file_security( struct SECURITY_API *scapi, - const char *path, u32 selection, const char *attr ); -int ntfs_get_file_attributes( struct SECURITY_API *scapi, - const char *path ); -BOOL ntfs_set_file_attributes( struct SECURITY_API *scapi, - const char *path, s32 attrib ); -BOOL ntfs_read_directory( struct SECURITY_API *scapi, - const char *path, ntfs_filldir_t callback, void *context ); -int ntfs_read_sds( struct SECURITY_API *scapi, - char *buf, u32 size, u32 offset ); -INDEX_ENTRY *ntfs_read_sii( struct SECURITY_API *scapi, - INDEX_ENTRY *entry ); -INDEX_ENTRY *ntfs_read_sdh( struct SECURITY_API *scapi, - INDEX_ENTRY *entry ); -struct SECURITY_API *ntfs_initialize_file_security( const char *device, - int flags ); -BOOL ntfs_leave_file_security( struct SECURITY_API *scx ); +int ntfs_get_file_security(struct SECURITY_API *scapi, + const char *path, u32 selection, + char *buf, u32 buflen, u32 *psize); +int ntfs_set_file_security(struct SECURITY_API *scapi, + const char *path, u32 selection, const char *attr); +int ntfs_get_file_attributes(struct SECURITY_API *scapi, + const char *path); +BOOL ntfs_set_file_attributes(struct SECURITY_API *scapi, + const char *path, s32 attrib); +BOOL ntfs_read_directory(struct SECURITY_API *scapi, + const char *path, ntfs_filldir_t callback, void *context); +int ntfs_read_sds(struct SECURITY_API *scapi, + char *buf, u32 size, u32 offset); +INDEX_ENTRY *ntfs_read_sii(struct SECURITY_API *scapi, + INDEX_ENTRY *entry); +INDEX_ENTRY *ntfs_read_sdh(struct SECURITY_API *scapi, + INDEX_ENTRY *entry); +struct SECURITY_API *ntfs_initialize_file_security(const char *device, + int flags); +BOOL ntfs_leave_file_security(struct SECURITY_API *scx); -int ntfs_get_usid( struct SECURITY_API *scapi, uid_t uid, char *buf ); -int ntfs_get_gsid( struct SECURITY_API *scapi, gid_t gid, char *buf ); -int ntfs_get_user( struct SECURITY_API *scapi, const SID *usid ); -int ntfs_get_group( struct SECURITY_API *scapi, const SID *gsid ); +int ntfs_get_usid(struct SECURITY_API *scapi, uid_t uid, char *buf); +int ntfs_get_gsid(struct SECURITY_API *scapi, gid_t gid, char *buf); +int ntfs_get_user(struct SECURITY_API *scapi, const SID *usid); +int ntfs_get_group(struct SECURITY_API *scapi, const SID *gsid); #endif /* defined _NTFS_SECURITY_H */ diff --git a/source/libntfs/support.h b/source/libntfs/support.h index 11bdd8c0..6af4761e 100644 --- a/source/libntfs/support.h +++ b/source/libntfs/support.h @@ -33,24 +33,24 @@ /* * Our mailing list. Use this define to prevent typos in email address. */ -#define NTFS_DEV_LIST "ntfs-3g-devel@lists.sf.net" +#define NTFS_DEV_LIST "ntfs-3g-devel@lists.sf.net" /* * Generic macro to convert pointers to values for comparison purposes. */ #ifndef p2n -#define p2n(p) ((ptrdiff_t)((ptrdiff_t*)(p))) +#define p2n(p) ((ptrdiff_t)((ptrdiff_t*)(p))) #endif /* * The classic min and max macros. */ #ifndef min -#define min(a,b) ((a) <= (b) ? (a) : (b)) +#define min(a,b) ((a) <= (b) ? (a) : (b)) #endif #ifndef max -#define max(a,b) ((a) >= (b) ? (a) : (b)) +#define max(a,b) ((a) >= (b) ? (a) : (b)) #endif /* @@ -63,22 +63,22 @@ /* * Simple bit operation macros. NOTE: These are NOT atomic. */ -#define test_bit(bit, var) ((var) & (1 << (bit))) -#define set_bit(bit, var) (var) |= 1 << (bit) -#define clear_bit(bit, var) (var) &= ~(1 << (bit)) +#define test_bit(bit, var) ((var) & (1 << (bit))) +#define set_bit(bit, var) (var) |= 1 << (bit) +#define clear_bit(bit, var) (var) &= ~(1 << (bit)) -#define test_and_set_bit(bit, var) \ -({ \ - const BOOL old_state = test_bit(bit, var); \ - set_bit(bit, var); \ - old_state; \ +#define test_and_set_bit(bit, var) \ +({ \ + const BOOL old_state = test_bit(bit, var); \ + set_bit(bit, var); \ + old_state; \ }) -#define test_and_clear_bit(bit, var) \ -({ \ - const BOOL old_state = test_bit(bit, var); \ - clear_bit(bit, var); \ - old_state; \ +#define test_and_clear_bit(bit, var) \ +({ \ + const BOOL old_state = test_bit(bit, var); \ + clear_bit(bit, var); \ + old_state; \ }) #endif /* defined _NTFS_SUPPORT_H */ diff --git a/source/libntfs/types.h b/source/libntfs/types.h index 66b77025..3fafe8a7 100644 --- a/source/libntfs/types.h +++ b/source/libntfs/types.h @@ -39,12 +39,12 @@ #include "compat.h" #else /* GEKKO */ -typedef uint8_t u8; /* Unsigned types of an exact size */ +typedef uint8_t u8; /* Unsigned types of an exact size */ typedef uint16_t u16; typedef uint32_t u32; typedef uint64_t u64; -typedef int8_t s8; /* Signed types of an exact size */ +typedef int8_t s8; /* Signed types of an exact size */ typedef int16_t s16; typedef int32_t s32; typedef int64_t s64; @@ -63,7 +63,7 @@ typedef u16 sle16; typedef u32 sle32; typedef u64 sle64; -typedef u16 ntfschar; /* 2-byte Unicode character type. */ +typedef u16 ntfschar; /* 2-byte Unicode character type. */ #define UCHAR_T_SIZE_BITS 1 /* @@ -92,25 +92,24 @@ typedef sle64 leLSN; /** * enum BOOL - These are just to make the code more readable... */ -typedef enum -{ +typedef enum { #ifndef FALSE - FALSE = 0, + FALSE = 0, #endif #ifndef NO - NO = 0, + NO = 0, #endif #ifndef ZERO - ZERO = 0, + ZERO = 0, #endif #ifndef TRUE - TRUE = 1, + TRUE = 1, #endif #ifndef YES - YES = 1, + YES = 1, #endif #ifndef ONE - ONE = 1, + ONE = 1, #endif } BOOL; #endif /* defined _WINDEF_H */ @@ -119,17 +118,16 @@ typedef enum /** * enum IGNORE_CASE_BOOL - */ -typedef enum -{ - CASE_SENSITIVE = 0, - IGNORE_CASE = 1, +typedef enum { + CASE_SENSITIVE = 0, + IGNORE_CASE = 1, } IGNORE_CASE_BOOL; -#define STATUS_OK (0) -#define STATUS_ERROR (-1) -#define STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT (-2) -#define STATUS_KEEP_SEARCHING (-3) -#define STATUS_NOT_FOUND (-4) +#define STATUS_OK (0) +#define STATUS_ERROR (-1) +#define STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT (-2) +#define STATUS_KEEP_SEARCHING (-3) +#define STATUS_NOT_FOUND (-4) #endif /* defined _NTFS_TYPES_H */ diff --git a/source/libntfs/unistr.c b/source/libntfs/unistr.c index 6ea0392f..cc3a1b8d 100644 --- a/source/libntfs/unistr.c +++ b/source/libntfs/unistr.c @@ -88,167 +88,153 @@ static int nfconvert_utf8 = 1; * characters are (in)valid. */ #if 0 -static const u8 legal_ansi_char_array[0x40] = -{ - 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, +static const u8 legal_ansi_char_array[0x40] = { + 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x17, 0x07, 0x18, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x17, 0x17, 0x18, 0x16, 0x16, 0x17, 0x07, 0x00, + 0x17, 0x07, 0x18, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x18, 0x16, 0x16, 0x17, 0x07, 0x00, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x17, 0x17, 0x04, 0x16, 0x18, 0x16, 0x18, 0x18, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x04, 0x16, 0x18, 0x16, 0x18, 0x18, }; #endif /** * ntfs_names_are_equal - compare two Unicode names for equality - * @s1: name to compare to @s2 - * @s1_len: length in Unicode characters of @s1 - * @s2: name to compare to @s1 - * @s2_len: length in Unicode characters of @s2 - * @ic: ignore case bool - * @upcase: upcase table (only if @ic == IGNORE_CASE) - * @upcase_size: length in Unicode characters of @upcase (if present) + * @s1: name to compare to @s2 + * @s1_len: length in Unicode characters of @s1 + * @s2: name to compare to @s1 + * @s2_len: length in Unicode characters of @s2 + * @ic: ignore case bool + * @upcase: upcase table (only if @ic == IGNORE_CASE) + * @upcase_size: length in Unicode characters of @upcase (if present) * * Compare the names @s1 and @s2 and return TRUE (1) if the names are * identical, or FALSE (0) if they are not identical. If @ic is IGNORE_CASE, * the @upcase table is used to perform a case insensitive comparison. */ -BOOL ntfs_names_are_equal( const ntfschar *s1, size_t s1_len, - const ntfschar *s2, size_t s2_len, - const IGNORE_CASE_BOOL ic, - const ntfschar *upcase, const u32 upcase_size ) +BOOL ntfs_names_are_equal(const ntfschar *s1, size_t s1_len, + const ntfschar *s2, size_t s2_len, + const IGNORE_CASE_BOOL ic, + const ntfschar *upcase, const u32 upcase_size) { - if ( s1_len != s2_len ) - return FALSE; - if ( !s1_len ) - return TRUE; - if ( ic == CASE_SENSITIVE ) - return ntfs_ucsncmp( s1, s2, s1_len ) ? FALSE : TRUE; - return ntfs_ucsncasecmp( s1, s2, s1_len, upcase, upcase_size ) ? FALSE : - TRUE; + if (s1_len != s2_len) + return FALSE; + if (!s1_len) + return TRUE; + if (ic == CASE_SENSITIVE) + return ntfs_ucsncmp(s1, s2, s1_len) ? FALSE: TRUE; + return ntfs_ucsncasecmp(s1, s2, s1_len, upcase, upcase_size) ? FALSE: + TRUE; } /* * ntfs_names_full_collate() fully collate two Unicode names * - * @name1: first Unicode name to compare - * @name1_len: length of first Unicode name to compare - * @name2: second Unicode name to compare - * @name2_len: length of second Unicode name to compare - * @ic: either CASE_SENSITIVE or IGNORE_CASE - * @upcase: upcase table (ignored if @ic is CASE_SENSITIVE) - * @upcase_len: upcase table size (ignored if @ic is CASE_SENSITIVE) + * @name1: first Unicode name to compare + * @name1_len: length of first Unicode name to compare + * @name2: second Unicode name to compare + * @name2_len: length of second Unicode name to compare + * @ic: either CASE_SENSITIVE or IGNORE_CASE + * @upcase: upcase table (ignored if @ic is CASE_SENSITIVE) + * @upcase_len: upcase table size (ignored if @ic is CASE_SENSITIVE) * * -1 if the first name collates before the second one, * 0 if the names match, * 1 if the second name collates before the first one, or * */ -int ntfs_names_full_collate( const ntfschar *name1, const u32 name1_len, - const ntfschar *name2, const u32 name2_len, - const IGNORE_CASE_BOOL ic, const ntfschar *upcase, - const u32 upcase_len ) +int ntfs_names_full_collate(const ntfschar *name1, const u32 name1_len, + const ntfschar *name2, const u32 name2_len, + const IGNORE_CASE_BOOL ic, const ntfschar *upcase, + const u32 upcase_len) { - u32 cnt; - u16 c1, c2; - u16 u1, u2; + u32 cnt; + u16 c1, c2; + u16 u1, u2; #ifdef DEBUG - if ( !name1 || !name2 || ( ic && ( !upcase || !upcase_len ) ) ) - { - ntfs_log_debug( "ntfs_names_collate received NULL pointer!\n" ); - exit( 1 ); - } + if (!name1 || !name2 || (ic && (!upcase || !upcase_len))) { + ntfs_log_debug("ntfs_names_collate received NULL pointer!\n"); + exit(1); + } #endif - cnt = min( name1_len, name2_len ); - if ( cnt > 0 ) - { - if ( ic == CASE_SENSITIVE ) - { - do - { - c1 = le16_to_cpu( *name1 ); - name1++; - c2 = le16_to_cpu( *name2 ); - name2++; - } - while ( --cnt && ( c1 == c2 ) ); - u1 = c1; - u2 = c2; - if ( u1 < upcase_len ) - u1 = le16_to_cpu( upcase[u1] ); - if ( u2 < upcase_len ) - u2 = le16_to_cpu( upcase[u2] ); - if ( ( u1 == u2 ) && cnt ) - do - { - u1 = le16_to_cpu( *name1 ); - name1++; - u2 = le16_to_cpu( *name2 ); - name2++; - if ( u1 < upcase_len ) - u1 = le16_to_cpu( upcase[u1] ); - if ( u2 < upcase_len ) - u2 = le16_to_cpu( upcase[u2] ); - } - while ( ( u1 == u2 ) && --cnt ); - if ( u1 < u2 ) - return -1; - if ( u1 > u2 ) - return 1; - if ( name1_len < name2_len ) - return -1; - if ( name1_len > name2_len ) - return 1; - if ( c1 < c2 ) - return -1; - if ( c1 > c2 ) - return 1; - } - else - { - do - { - u1 = c1 = le16_to_cpu( *name1 ); - name1++; - u2 = c2 = le16_to_cpu( *name2 ); - name2++; - if ( u1 < upcase_len ) - u1 = le16_to_cpu( upcase[u1] ); - if ( u2 < upcase_len ) - u2 = le16_to_cpu( upcase[u2] ); - } - while ( ( u1 == u2 ) && --cnt ); - if ( u1 < u2 ) - return -1; - if ( u1 > u2 ) - return 1; - if ( name1_len < name2_len ) - return -1; - if ( name1_len > name2_len ) - return 1; - } - } - else - { - if ( name1_len < name2_len ) - return -1; - if ( name1_len > name2_len ) - return 1; - } - return 0; + cnt = min(name1_len, name2_len); + if (cnt > 0) { + if (ic == CASE_SENSITIVE) { + do { + c1 = le16_to_cpu(*name1); + name1++; + c2 = le16_to_cpu(*name2); + name2++; + } while (--cnt && (c1 == c2)); + u1 = c1; + u2 = c2; + if (u1 < upcase_len) + u1 = le16_to_cpu(upcase[u1]); + if (u2 < upcase_len) + u2 = le16_to_cpu(upcase[u2]); + if ((u1 == u2) && cnt) + do { + u1 = le16_to_cpu(*name1); + name1++; + u2 = le16_to_cpu(*name2); + name2++; + if (u1 < upcase_len) + u1 = le16_to_cpu(upcase[u1]); + if (u2 < upcase_len) + u2 = le16_to_cpu(upcase[u2]); + } while ((u1 == u2) && --cnt); + if (u1 < u2) + return -1; + if (u1 > u2) + return 1; + if (name1_len < name2_len) + return -1; + if (name1_len > name2_len) + return 1; + if (c1 < c2) + return -1; + if (c1 > c2) + return 1; + } else { + do { + u1 = c1 = le16_to_cpu(*name1); + name1++; + u2 = c2 = le16_to_cpu(*name2); + name2++; + if (u1 < upcase_len) + u1 = le16_to_cpu(upcase[u1]); + if (u2 < upcase_len) + u2 = le16_to_cpu(upcase[u2]); + } while ((u1 == u2) && --cnt); + if (u1 < u2) + return -1; + if (u1 > u2) + return 1; + if (name1_len < name2_len) + return -1; + if (name1_len > name2_len) + return 1; + } + } else { + if (name1_len < name2_len) + return -1; + if (name1_len > name2_len) + return 1; + } + return 0; } /** * ntfs_ucsncmp - compare two little endian Unicode strings - * @s1: first string - * @s2: second string - * @n: maximum unicode characters to compare + * @s1: first string + * @s2: second string + * @n: maximum unicode characters to compare * * Compare the first @n characters of the Unicode strings @s1 and @s2, * The strings in little endian format and appropriate le16_to_cpu() @@ -258,39 +244,37 @@ int ntfs_names_full_collate( const ntfschar *name1, const u32 name1_len, * if @s1 (or the first @n Unicode characters thereof) is found, respectively, * to be less than, to match, or be greater than @s2. */ -int ntfs_ucsncmp( const ntfschar *s1, const ntfschar *s2, size_t n ) +int ntfs_ucsncmp(const ntfschar *s1, const ntfschar *s2, size_t n) { - ntfschar c1, c2; - size_t i; + ntfschar c1, c2; + size_t i; #ifdef DEBUG - if ( !s1 || !s2 ) - { - ntfs_log_debug( "ntfs_wcsncmp() received NULL pointer!\n" ); - exit( 1 ); - } + if (!s1 || !s2) { + ntfs_log_debug("ntfs_wcsncmp() received NULL pointer!\n"); + exit(1); + } #endif - for ( i = 0; i < n; ++i ) - { - c1 = le16_to_cpu( s1[i] ); - c2 = le16_to_cpu( s2[i] ); - if ( c1 < c2 ) - return -1; - if ( c1 > c2 ) - return 1; - if ( !c1 ) - break; - } - return 0; + for (i = 0; i < n; ++i) { + c1 = le16_to_cpu(s1[i]); + c2 = le16_to_cpu(s2[i]); + if (c1 < c2) + return -1; + if (c1 > c2) + return 1; + if (!c1) + break; + } + return 0; } /** * ntfs_ucsncasecmp - compare two little endian Unicode strings, ignoring case - * @s1: first string - * @s2: second string - * @n: maximum unicode characters to compare - * @upcase: upcase table - * @upcase_size: upcase table size in Unicode characters + * @s1: first string + * @s2: second string + * @n: maximum unicode characters to compare + * @upcase: upcase table + * @upcase_size: upcase table size in Unicode characters * * Compare the first @n characters of the Unicode strings @s1 and @s2, * ignoring case. The strings in little endian format and appropriate @@ -302,39 +286,37 @@ int ntfs_ucsncmp( const ntfschar *s1, const ntfschar *s2, size_t n ) * if @s1 (or the first @n Unicode characters thereof) is found, respectively, * to be less than, to match, or be greater than @s2. */ -int ntfs_ucsncasecmp( const ntfschar *s1, const ntfschar *s2, size_t n, - const ntfschar *upcase, const u32 upcase_size ) +int ntfs_ucsncasecmp(const ntfschar *s1, const ntfschar *s2, size_t n, + const ntfschar *upcase, const u32 upcase_size) { - u16 c1, c2; - size_t i; + u16 c1, c2; + size_t i; #ifdef DEBUG - if ( !s1 || !s2 || !upcase ) - { - ntfs_log_debug( "ntfs_wcsncasecmp() received NULL pointer!\n" ); - exit( 1 ); - } + if (!s1 || !s2 || !upcase) { + ntfs_log_debug("ntfs_wcsncasecmp() received NULL pointer!\n"); + exit(1); + } #endif - for ( i = 0; i < n; ++i ) - { - if ( ( c1 = le16_to_cpu( s1[i] ) ) < upcase_size ) - c1 = le16_to_cpu( upcase[c1] ); - if ( ( c2 = le16_to_cpu( s2[i] ) ) < upcase_size ) - c2 = le16_to_cpu( upcase[c2] ); - if ( c1 < c2 ) - return -1; - if ( c1 > c2 ) - return 1; - if ( !c1 ) - break; - } - return 0; + for (i = 0; i < n; ++i) { + if ((c1 = le16_to_cpu(s1[i])) < upcase_size) + c1 = le16_to_cpu(upcase[c1]); + if ((c2 = le16_to_cpu(s2[i])) < upcase_size) + c2 = le16_to_cpu(upcase[c2]); + if (c1 < c2) + return -1; + if (c1 > c2) + return 1; + if (!c1) + break; + } + return 0; } /** * ntfs_ucsnlen - determine the length of a little endian Unicode string - * @s: pointer to Unicode string - * @maxlen: maximum length of string @s + * @s: pointer to Unicode string + * @maxlen: maximum length of string @s * * Return the number of Unicode characters in the little endian Unicode * string @s up to a maximum of maxlen Unicode characters, not including @@ -343,22 +325,21 @@ int ntfs_ucsncasecmp( const ntfschar *s1, const ntfschar *s2, size_t n, * * This function never looks beyond @s + @maxlen. */ -u32 ntfs_ucsnlen( const ntfschar *s, u32 maxlen ) +u32 ntfs_ucsnlen(const ntfschar *s, u32 maxlen) { - u32 i; + u32 i; - for ( i = 0; i < maxlen; i++ ) - { - if ( !le16_to_cpu( s[i] ) ) - break; - } - return i; + for (i = 0; i < maxlen; i++) { + if (!le16_to_cpu(s[i])) + break; + } + return i; } /** * ntfs_ucsndup - duplicate little endian Unicode string - * @s: pointer to Unicode string - * @maxlen: maximum length of string @s + * @s: pointer to Unicode string + * @maxlen: maximum length of string @s * * Return a pointer to a new little endian Unicode string which is a duplicate * of the string s. Memory for the new string is obtained with ntfs_malloc(3), @@ -372,19 +353,18 @@ u32 ntfs_ucsnlen( const ntfschar *s, u32 maxlen ) * Return a pointer to the new little endian Unicode string on success and NULL * on failure with errno set to the error code. */ -ntfschar *ntfs_ucsndup( const ntfschar *s, u32 maxlen ) +ntfschar *ntfs_ucsndup(const ntfschar *s, u32 maxlen) { - ntfschar *dst; - u32 len; + ntfschar *dst; + u32 len; - len = ntfs_ucsnlen( s, maxlen ); - dst = ntfs_malloc( ( len + 1 ) * sizeof( ntfschar ) ); - if ( dst ) - { - memcpy( dst, s, len * sizeof( ntfschar ) ); - dst[len] = cpu_to_le16( L'\0' ); - } - return dst; + len = ntfs_ucsnlen(s, maxlen); + dst = ntfs_malloc((len + 1) * sizeof(ntfschar)); + if (dst) { + memcpy(dst, s, len * sizeof(ntfschar)); + dst[len] = cpu_to_le16(L'\0'); + } + return dst; } /** @@ -398,30 +378,30 @@ ntfschar *ntfs_ucsndup( const ntfschar *s, u32 maxlen ) * * Returns: */ -void ntfs_name_upcase( ntfschar *name, u32 name_len, const ntfschar *upcase, - const u32 upcase_len ) +void ntfs_name_upcase(ntfschar *name, u32 name_len, const ntfschar *upcase, + const u32 upcase_len) { - u32 i; - u16 u; + u32 i; + u16 u; - for ( i = 0; i < name_len; i++ ) - if ( ( u = le16_to_cpu( name[i] ) ) < upcase_len ) - name[i] = upcase[u]; + for (i = 0; i < name_len; i++) + if ((u = le16_to_cpu(name[i])) < upcase_len) + name[i] = upcase[u]; } /** * ntfs_name_locase - Map a Unicode name to its lowercase equivalent */ -void ntfs_name_locase( ntfschar *name, u32 name_len, const ntfschar *locase, - const u32 locase_len ) +void ntfs_name_locase(ntfschar *name, u32 name_len, const ntfschar *locase, + const u32 locase_len) { - u32 i; - u16 u; + u32 i; + u16 u; - if ( locase ) - for ( i = 0; i < name_len; i++ ) - if ( ( u = le16_to_cpu( name[i] ) ) < locase_len ) - name[i] = locase[u]; + if (locase) + for (i = 0; i < name_len; i++) + if ((u = le16_to_cpu(name[i])) < locase_len) + name[i] = locase[u]; } /** @@ -434,11 +414,11 @@ void ntfs_name_locase( ntfschar *name, u32 name_len, const ntfschar *locase, * * Returns: */ -void ntfs_file_value_upcase( FILE_NAME_ATTR *file_name_attr, - const ntfschar *upcase, const u32 upcase_len ) +void ntfs_file_value_upcase(FILE_NAME_ATTR *file_name_attr, + const ntfschar *upcase, const u32 upcase_len) { - ntfs_name_upcase( ( ntfschar* )&file_name_attr->file_name, - file_name_attr->file_name_length, upcase, upcase_len ); + ntfs_name_upcase((ntfschar*)&file_name_attr->file_name, + file_name_attr->file_name_length, upcase, upcase_len); } /* @@ -454,430 +434,379 @@ void ntfs_file_value_upcase( FILE_NAME_ATTR *file_name_attr, so this patch fixes the resulting issues for systems which use UTF-8 and for others, specifying the locale in fstab brings them the encoding which they want. - + If no locale is defined or there was a problem with setting one up and whenever nl_langinfo(CODESET) returns a sting starting with "ANSI", use an internal UCS-2LE <-> UTF-8 codeset converter to fix the bug where NTFS-3G does not show any path names which include international characters!!! (and also fails on creating them) as result. - + Author: Bernhard Kaindl Jean-Pierre Andre made it compliant with RFC3629/RFC2781. */ - -/* + +/* * Return the amount of 8-bit elements in UTF-8 needed (without the terminating * null) to store a given UTF-16LE string. * * Return -1 with errno set if string has invalid byte sequence or too long. */ -static int utf16_to_utf8_size( const ntfschar *ins, const int ins_len, int outs_len ) +static int utf16_to_utf8_size(const ntfschar *ins, const int ins_len, int outs_len) { - int i, ret = -1; - int count = 0; - BOOL surrog; + int i, ret = -1; + int count = 0; + BOOL surrog; - surrog = FALSE; - for ( i = 0; i < ins_len && ins[i]; i++ ) - { - unsigned short c = le16_to_cpu( ins[i] ); - if ( surrog ) - { - if ( ( c >= 0xdc00 ) && ( c < 0xe000 ) ) - { - surrog = FALSE; - count += 4; - } - else - goto fail; - } - else if ( c < 0x80 ) - count++; - else if ( c < 0x800 ) - count += 2; - else if ( c < 0xd800 ) - count += 3; - else if ( c < 0xdc00 ) - surrog = TRUE; + surrog = FALSE; + for (i = 0; i < ins_len && ins[i]; i++) { + unsigned short c = le16_to_cpu(ins[i]); + if (surrog) { + if ((c >= 0xdc00) && (c < 0xe000)) { + surrog = FALSE; + count += 4; + } else + goto fail; + } else + if (c < 0x80) + count++; + else if (c < 0x800) + count += 2; + else if (c < 0xd800) + count += 3; + else if (c < 0xdc00) + surrog = TRUE; #if NOREVBOM - else if ( ( c >= 0xe000 ) && ( c < 0xfffe ) ) + else if ((c >= 0xe000) && (c < 0xfffe)) #else - else if ( c >= 0xe000 ) + else if (c >= 0xe000) #endif - count += 3; - else - goto fail; - if ( count > outs_len ) - { - errno = ENAMETOOLONG; - goto out; - } - } - if ( surrog ) - goto fail; + count += 3; + else + goto fail; + if (count > outs_len) { + errno = ENAMETOOLONG; + goto out; + } + } + if (surrog) + goto fail; - ret = count; + ret = count; out: - return ret; + return ret; fail: - errno = EILSEQ; - goto out; + errno = EILSEQ; + goto out; } /* * ntfs_utf16_to_utf8 - convert a little endian UTF16LE string to an UTF-8 string - * @ins: input utf16 string buffer - * @ins_len: length of input string in utf16 characters - * @outs: on return contains the (allocated) output multibyte string - * @outs_len: length of output buffer in bytes + * @ins: input utf16 string buffer + * @ins_len: length of input string in utf16 characters + * @outs: on return contains the (allocated) output multibyte string + * @outs_len: length of output buffer in bytes * * Return -1 with errno set if string has invalid byte sequence or too long. */ -static int ntfs_utf16_to_utf8( const ntfschar *ins, const int ins_len, - char **outs, int outs_len ) +static int ntfs_utf16_to_utf8(const ntfschar *ins, const int ins_len, + char **outs, int outs_len) { #if defined(__APPLE__) || defined(__DARWIN__) #ifdef ENABLE_NFCONV - char *original_outs_value = *outs; - int original_outs_len = outs_len; + char *original_outs_value = *outs; + int original_outs_len = outs_len; #endif /* ENABLE_NFCONV */ #endif /* defined(__APPLE__) || defined(__DARWIN__) */ - char *t; - int i, size, ret = -1; - int halfpair; + char *t; + int i, size, ret = -1; + int halfpair; - halfpair = 0; - if ( !*outs ) - outs_len = PATH_MAX; + halfpair = 0; + if (!*outs) + outs_len = PATH_MAX; - size = utf16_to_utf8_size( ins, ins_len, outs_len ); + size = utf16_to_utf8_size(ins, ins_len, outs_len); - if ( size < 0 ) - goto out; + if (size < 0) + goto out; - if ( !*outs ) - { - outs_len = size + 1; - *outs = ntfs_malloc( outs_len ); - if ( !*outs ) - goto out; - } + if (!*outs) { + outs_len = size + 1; + *outs = ntfs_malloc(outs_len); + if (!*outs) + goto out; + } - t = *outs; - - for ( i = 0; i < ins_len && ins[i]; i++ ) - { - unsigned short c = le16_to_cpu( ins[i] ); - /* size not double-checked */ - if ( halfpair ) - { - if ( ( c >= 0xdc00 ) && ( c < 0xe000 ) ) - { - *t++ = 0xf0 + ( ( ( halfpair + 64 ) >> 8 ) & 7 ); - *t++ = 0x80 + ( ( ( halfpair + 64 ) >> 2 ) & 63 ); - *t++ = 0x80 + ( ( c >> 6 ) & 15 ) + ( ( halfpair & 3 ) << 4 ); - *t++ = 0x80 + ( c & 63 ); - halfpair = 0; - } - else - goto fail; - } - else if ( c < 0x80 ) - { - *t++ = c; - } - else - { - if ( c < 0x800 ) - { - *t++ = ( 0xc0 | ( ( c >> 6 ) & 0x3f ) ); - *t++ = 0x80 | ( c & 0x3f ); - } - else if ( c < 0xd800 ) - { - *t++ = 0xe0 | ( c >> 12 ); - *t++ = 0x80 | ( ( c >> 6 ) & 0x3f ); - *t++ = 0x80 | ( c & 0x3f ); - } - else if ( c < 0xdc00 ) - halfpair = c; - else if ( c >= 0xe000 ) - { - *t++ = 0xe0 | ( c >> 12 ); - *t++ = 0x80 | ( ( c >> 6 ) & 0x3f ); - *t++ = 0x80 | ( c & 0x3f ); - } - else - goto fail; - } - } - *t = '\0'; + t = *outs; + for (i = 0; i < ins_len && ins[i]; i++) { + unsigned short c = le16_to_cpu(ins[i]); + /* size not double-checked */ + if (halfpair) { + if ((c >= 0xdc00) && (c < 0xe000)) { + *t++ = 0xf0 + (((halfpair + 64) >> 8) & 7); + *t++ = 0x80 + (((halfpair + 64) >> 2) & 63); + *t++ = 0x80 + ((c >> 6) & 15) + ((halfpair & 3) << 4); + *t++ = 0x80 + (c & 63); + halfpair = 0; + } else + goto fail; + } else if (c < 0x80) { + *t++ = c; + } else { + if (c < 0x800) { + *t++ = (0xc0 | ((c >> 6) & 0x3f)); + *t++ = 0x80 | (c & 0x3f); + } else if (c < 0xd800) { + *t++ = 0xe0 | (c >> 12); + *t++ = 0x80 | ((c >> 6) & 0x3f); + *t++ = 0x80 | (c & 0x3f); + } else if (c < 0xdc00) + halfpair = c; + else if (c >= 0xe000) { + *t++ = 0xe0 | (c >> 12); + *t++ = 0x80 | ((c >> 6) & 0x3f); + *t++ = 0x80 | (c & 0x3f); + } else + goto fail; + } + } + *t = '\0'; + #if defined(__APPLE__) || defined(__DARWIN__) #ifdef ENABLE_NFCONV - if ( nfconvert_utf8 && ( t - *outs ) > 0 ) - { - char *new_outs = NULL; - int new_outs_len = ntfs_macosx_normalize_utf8( *outs, &new_outs, 0 ); // Normalize to decomposed form - if ( new_outs_len >= 0 && new_outs != NULL ) - { - if ( original_outs_value != *outs ) - { - // We have allocated outs ourselves. - free( *outs ); - *outs = new_outs; - t = *outs + new_outs_len; - } - else - { - // We need to copy new_outs into the fixed outs buffer. - memset( *outs, 0, original_outs_len ); - strncpy( *outs, new_outs, original_outs_len - 1 ); - t = *outs + original_outs_len; - free( new_outs ); - } - } - else - { - ntfs_log_error( "Failed to normalize NTFS string to UTF-8 NFD: %s\n", *outs ); - ntfs_log_error( " new_outs=0x%p\n", new_outs ); - ntfs_log_error( " new_outs_len=%d\n", new_outs_len ); - } - } + if(nfconvert_utf8 && (t - *outs) > 0) { + char *new_outs = NULL; + int new_outs_len = ntfs_macosx_normalize_utf8(*outs, &new_outs, 0); // Normalize to decomposed form + if(new_outs_len >= 0 && new_outs != NULL) { + if(original_outs_value != *outs) { + // We have allocated outs ourselves. + free(*outs); + *outs = new_outs; + t = *outs + new_outs_len; + } + else { + // We need to copy new_outs into the fixed outs buffer. + memset(*outs, 0, original_outs_len); + strncpy(*outs, new_outs, original_outs_len-1); + t = *outs + original_outs_len; + free(new_outs); + } + } + else { + ntfs_log_error("Failed to normalize NTFS string to UTF-8 NFD: %s\n", *outs); + ntfs_log_error(" new_outs=0x%p\n", new_outs); + ntfs_log_error(" new_outs_len=%d\n", new_outs_len); + } + } #endif /* ENABLE_NFCONV */ #endif /* defined(__APPLE__) || defined(__DARWIN__) */ - - ret = t - *outs; + + ret = t - *outs; out: - return ret; + return ret; fail: - errno = EILSEQ; - goto out; + errno = EILSEQ; + goto out; } -/* - * Return the amount of 16-bit elements in UTF-16LE needed +/* + * Return the amount of 16-bit elements in UTF-16LE needed * (without the terminating null) to store given UTF-8 string. * * Return -1 with errno set if it's longer than PATH_MAX or string is invalid. * * Note: This does not check whether the input sequence is a valid utf8 string, - * and should be used only in context where such check is made! + * and should be used only in context where such check is made! */ -static int utf8_to_utf16_size( const char *s ) +static int utf8_to_utf16_size(const char *s) { - int ret = -1; - unsigned int byte; - size_t count = 0; + int ret = -1; + unsigned int byte; + size_t count = 0; - while ( ( byte = *( ( const unsigned char * )s++ ) ) ) - { - if ( ++count >= PATH_MAX ) - goto fail; - if ( byte >= 0xc0 ) - { - if ( byte >= 0xF5 ) - { - errno = EILSEQ; - goto out; - } - if ( !*s ) - break; - if ( byte >= 0xC0 ) - s++; - if ( !*s ) - break; - if ( byte >= 0xE0 ) - s++; - if ( !*s ) - break; - if ( byte >= 0xF0 ) - { - s++; - if ( ++count >= PATH_MAX ) - goto fail; - } - } - } - ret = count; + while ((byte = *((const unsigned char *)s++))) { + if (++count >= PATH_MAX) + goto fail; + if (byte >= 0xc0) { + if (byte >= 0xF5) { + errno = EILSEQ; + goto out; + } + if (!*s) + break; + if (byte >= 0xC0) + s++; + if (!*s) + break; + if (byte >= 0xE0) + s++; + if (!*s) + break; + if (byte >= 0xF0) { + s++; + if (++count >= PATH_MAX) + goto fail; + } + } + } + ret = count; out: - return ret; + return ret; fail: - errno = ENAMETOOLONG; - goto out; + errno = ENAMETOOLONG; + goto out; } -/* +/* * This converts one UTF-8 sequence to cpu-endian Unicode value * within range U+0 .. U+10ffff and excluding U+D800 .. U+DFFF * - * Return the number of used utf8 bytes or -1 with errno set + * Return the number of used utf8 bytes or -1 with errno set * if sequence is invalid. */ -static int utf8_to_unicode( u32 *wc, const char *s ) +static int utf8_to_unicode(u32 *wc, const char *s) { - unsigned int byte = *( ( const unsigned char * )s ); + unsigned int byte = *((const unsigned char *)s); - /* single byte */ - if ( byte == 0 ) - { - *wc = ( u32 ) 0; - return 0; - } - else if ( byte < 0x80 ) - { - *wc = ( u32 ) byte; - return 1; - /* double byte */ - } - else if ( byte < 0xc2 ) - { - goto fail; - } - else if ( byte < 0xE0 ) - { - if ( ( s[1] & 0xC0 ) == 0x80 ) - { - *wc = ( ( u32 )( byte & 0x1F ) << 6 ) - | ( ( u32 )( s[1] & 0x3F ) ); - return 2; - } - else - goto fail; - /* three-byte */ - } - else if ( byte < 0xF0 ) - { - if ( ( ( s[1] & 0xC0 ) == 0x80 ) && ( ( s[2] & 0xC0 ) == 0x80 ) ) - { - *wc = ( ( u32 )( byte & 0x0F ) << 12 ) - | ( ( u32 )( s[1] & 0x3F ) << 6 ) - | ( ( u32 )( s[2] & 0x3F ) ); - /* Check valid ranges */ + /* single byte */ + if (byte == 0) { + *wc = (u32) 0; + return 0; + } else if (byte < 0x80) { + *wc = (u32) byte; + return 1; + /* double byte */ + } else if (byte < 0xc2) { + goto fail; + } else if (byte < 0xE0) { + if ((s[1] & 0xC0) == 0x80) { + *wc = ((u32)(byte & 0x1F) << 6) + | ((u32)(s[1] & 0x3F)); + return 2; + } else + goto fail; + /* three-byte */ + } else if (byte < 0xF0) { + if (((s[1] & 0xC0) == 0x80) && ((s[2] & 0xC0) == 0x80)) { + *wc = ((u32)(byte & 0x0F) << 12) + | ((u32)(s[1] & 0x3F) << 6) + | ((u32)(s[2] & 0x3F)); + /* Check valid ranges */ #if NOREVBOM - if ( ( ( *wc >= 0x800 ) && ( *wc <= 0xD7FF ) ) - || ( ( *wc >= 0xe000 ) && ( *wc <= 0xFFFD ) ) ) - return 3; + if (((*wc >= 0x800) && (*wc <= 0xD7FF)) + || ((*wc >= 0xe000) && (*wc <= 0xFFFD))) + return 3; #else - if ( ( ( *wc >= 0x800 ) && ( *wc <= 0xD7FF ) ) - || ( ( *wc >= 0xe000 ) && ( *wc <= 0xFFFF ) ) ) - return 3; + if (((*wc >= 0x800) && (*wc <= 0xD7FF)) + || ((*wc >= 0xe000) && (*wc <= 0xFFFF))) + return 3; #endif - } - goto fail; - /* four-byte */ - } - else if ( byte < 0xF5 ) - { - if ( ( ( s[1] & 0xC0 ) == 0x80 ) && ( ( s[2] & 0xC0 ) == 0x80 ) - && ( ( s[3] & 0xC0 ) == 0x80 ) ) - { - *wc = ( ( u32 )( byte & 0x07 ) << 18 ) - | ( ( u32 )( s[1] & 0x3F ) << 12 ) - | ( ( u32 )( s[2] & 0x3F ) << 6 ) - | ( ( u32 )( s[3] & 0x3F ) ); - /* Check valid ranges */ - if ( ( *wc <= 0x10ffff ) && ( *wc >= 0x10000 ) ) - return 4; - } - goto fail; - } + } + goto fail; + /* four-byte */ + } else if (byte < 0xF5) { + if (((s[1] & 0xC0) == 0x80) && ((s[2] & 0xC0) == 0x80) + && ((s[3] & 0xC0) == 0x80)) { + *wc = ((u32)(byte & 0x07) << 18) + | ((u32)(s[1] & 0x3F) << 12) + | ((u32)(s[2] & 0x3F) << 6) + | ((u32)(s[3] & 0x3F)); + /* Check valid ranges */ + if ((*wc <= 0x10ffff) && (*wc >= 0x10000)) + return 4; + } + goto fail; + } fail: - errno = EILSEQ; - return -1; + errno = EILSEQ; + return -1; } /** * ntfs_utf8_to_utf16 - convert a UTF-8 string to a UTF-16LE string - * @ins: input multibyte string buffer - * @outs: on return contains the (allocated) output utf16 string - * @outs_len: length of output buffer in utf16 characters - * + * @ins: input multibyte string buffer + * @outs: on return contains the (allocated) output utf16 string + * @outs_len: length of output buffer in utf16 characters + * * Return -1 with errno set. */ -static int ntfs_utf8_to_utf16( const char *ins, ntfschar **outs ) +static int ntfs_utf8_to_utf16(const char *ins, ntfschar **outs) { #if defined(__APPLE__) || defined(__DARWIN__) #ifdef ENABLE_NFCONV - char *new_ins = NULL; - if ( nfconvert_utf8 ) - { - int new_ins_len; - new_ins_len = ntfs_macosx_normalize_utf8( ins, &new_ins, 1 ); // Normalize to composed form - if ( new_ins_len >= 0 ) - ins = new_ins; - else - ntfs_log_error( "Failed to normalize NTFS string to UTF-8 NFC: %s\n", ins ); - } + char *new_ins = NULL; + if(nfconvert_utf8) { + int new_ins_len; + new_ins_len = ntfs_macosx_normalize_utf8(ins, &new_ins, 1); // Normalize to composed form + if(new_ins_len >= 0) + ins = new_ins; + else + ntfs_log_error("Failed to normalize NTFS string to UTF-8 NFC: %s\n", ins); + } #endif /* ENABLE_NFCONV */ #endif /* defined(__APPLE__) || defined(__DARWIN__) */ - const char *t = ins; - u32 wc; - BOOL allocated; - ntfschar *outpos; - int shorts, ret = -1; + const char *t = ins; + u32 wc; + BOOL allocated; + ntfschar *outpos; + int shorts, ret = -1; - shorts = utf8_to_utf16_size( ins ); - if ( shorts < 0 ) - goto fail; + shorts = utf8_to_utf16_size(ins); + if (shorts < 0) + goto fail; - allocated = FALSE; - if ( !*outs ) - { - *outs = ntfs_malloc( ( shorts + 1 ) * sizeof( ntfschar ) ); - if ( !*outs ) - goto fail; - allocated = TRUE; - } + allocated = FALSE; + if (!*outs) { + *outs = ntfs_malloc((shorts + 1) * sizeof(ntfschar)); + if (!*outs) + goto fail; + allocated = TRUE; + } - outpos = *outs; + outpos = *outs; - while ( 1 ) - { - int m = utf8_to_unicode( &wc, t ); - if ( m <= 0 ) - { - if ( m < 0 ) - { - /* do not leave space allocated if failed */ - if ( allocated ) - { - free( *outs ); - *outs = ( ntfschar* )NULL; - } - goto fail; - } - *outpos++ = const_cpu_to_le16( 0 ); - break; - } - if ( wc < 0x10000 ) - *outpos++ = cpu_to_le16( wc ); - else - { - wc -= 0x10000; - *outpos++ = cpu_to_le16( ( wc >> 10 ) + 0xd800 ); - *outpos++ = cpu_to_le16( ( wc & 0x3ff ) + 0xdc00 ); - } - t += m; - } - - ret = --outpos - *outs; + while(1) { + int m = utf8_to_unicode(&wc, t); + if (m <= 0) { + if (m < 0) { + /* do not leave space allocated if failed */ + if (allocated) { + free(*outs); + *outs = (ntfschar*)NULL; + } + goto fail; + } + *outpos++ = const_cpu_to_le16(0); + break; + } + if (wc < 0x10000) + *outpos++ = cpu_to_le16(wc); + else { + wc -= 0x10000; + *outpos++ = cpu_to_le16((wc >> 10) + 0xd800); + *outpos++ = cpu_to_le16((wc & 0x3ff) + 0xdc00); + } + t += m; + } + + ret = --outpos - *outs; fail: #if defined(__APPLE__) || defined(__DARWIN__) #ifdef ENABLE_NFCONV - if ( new_ins != NULL ) - free( new_ins ); + if(new_ins != NULL) + free(new_ins); #endif /* ENABLE_NFCONV */ #endif /* defined(__APPLE__) || defined(__DARWIN__) */ - return ret; + return ret; } /** * ntfs_ucstombs - convert a little endian Unicode string to a multibyte string - * @ins: input Unicode string buffer - * @ins_len: length of input string in Unicode characters - * @outs: on return contains the (allocated) output multibyte string - * @outs_len: length of output buffer in bytes + * @ins: input Unicode string buffer + * @ins_len: length of input string in Unicode characters + * @outs: on return contains the (allocated) output multibyte string + * @outs_len: length of output buffer in bytes * * Convert the input little endian, 2-byte Unicode string @ins, of length * @ins_len into the multibyte string format dictated by the current locale. @@ -891,121 +820,112 @@ fail: * * On error, -1 is returned, and errno is set to the error code. The following * error codes can be expected: - * EINVAL Invalid arguments (e.g. @ins or @outs is NULL). - * EILSEQ The input string cannot be represented as a multibyte - * sequence according to the current locale. - * ENAMETOOLONG Destination buffer is too small for input string. - * ENOMEM Not enough memory to allocate destination buffer. + * EINVAL Invalid arguments (e.g. @ins or @outs is NULL). + * EILSEQ The input string cannot be represented as a multibyte + * sequence according to the current locale. + * ENAMETOOLONG Destination buffer is too small for input string. + * ENOMEM Not enough memory to allocate destination buffer. */ -int ntfs_ucstombs( const ntfschar *ins, const int ins_len, char **outs, - int outs_len ) +int ntfs_ucstombs(const ntfschar *ins, const int ins_len, char **outs, + int outs_len) { - char *mbs; - wchar_t wc; - int i, o, mbs_len; - int cnt = 0; + char *mbs; + wchar_t wc; + int i, o, mbs_len; + int cnt = 0; #ifdef HAVE_MBSINIT - mbstate_t mbstate; + mbstate_t mbstate; #endif - if ( !ins || !outs ) - { - errno = EINVAL; - return -1; - } - mbs = *outs; - mbs_len = outs_len; - if ( mbs && !mbs_len ) - { - errno = ENAMETOOLONG; - return -1; - } - if ( use_utf8 ) - return ntfs_utf16_to_utf8( ins, ins_len, outs, outs_len ); - if ( !mbs ) - { - mbs_len = ( ins_len + 1 ) * MB_CUR_MAX; - mbs = ntfs_malloc( mbs_len ); - if ( !mbs ) - return -1; - } + if (!ins || !outs) { + errno = EINVAL; + return -1; + } + mbs = *outs; + mbs_len = outs_len; + if (mbs && !mbs_len) { + errno = ENAMETOOLONG; + return -1; + } + if (use_utf8) + return ntfs_utf16_to_utf8(ins, ins_len, outs, outs_len); + if (!mbs) { + mbs_len = (ins_len + 1) * MB_CUR_MAX; + mbs = ntfs_malloc(mbs_len); + if (!mbs) + return -1; + } #ifdef HAVE_MBSINIT - memset( &mbstate, 0, sizeof( mbstate ) ); + memset(&mbstate, 0, sizeof(mbstate)); #else - wctomb( NULL, 0 ); + wctomb(NULL, 0); #endif - for ( i = o = 0; i < ins_len; i++ ) - { - /* Reallocate memory if necessary or abort. */ - if ( ( int )( o + MB_CUR_MAX ) > mbs_len ) - { - char *tc; - if ( mbs == *outs ) - { - errno = ENAMETOOLONG; - return -1; - } - tc = ntfs_malloc( ( mbs_len + 64 ) & ~63 ); - if ( !tc ) - goto err_out; - memcpy( tc, mbs, mbs_len ); - mbs_len = ( mbs_len + 64 ) & ~63; - free( mbs ); - mbs = tc; - } - /* Convert the LE Unicode character to a CPU wide character. */ - wc = ( wchar_t )le16_to_cpu( ins[i] ); - if ( !wc ) - break; - /* Convert the CPU endian wide character to multibyte. */ + for (i = o = 0; i < ins_len; i++) { + /* Reallocate memory if necessary or abort. */ + if ((int)(o + MB_CUR_MAX) > mbs_len) { + char *tc; + if (mbs == *outs) { + errno = ENAMETOOLONG; + return -1; + } + tc = ntfs_malloc((mbs_len + 64) & ~63); + if (!tc) + goto err_out; + memcpy(tc, mbs, mbs_len); + mbs_len = (mbs_len + 64) & ~63; + free(mbs); + mbs = tc; + } + /* Convert the LE Unicode character to a CPU wide character. */ + wc = (wchar_t)le16_to_cpu(ins[i]); + if (!wc) + break; + /* Convert the CPU endian wide character to multibyte. */ #ifdef HAVE_MBSINIT - cnt = wcrtomb( mbs + o, wc, &mbstate ); + cnt = wcrtomb(mbs + o, wc, &mbstate); #else - cnt = wctomb( mbs + o, wc ); + cnt = wctomb(mbs + o, wc); #endif - if ( cnt == -1 ) - goto err_out; - if ( cnt <= 0 ) - { - ntfs_log_debug( "Eeek. cnt <= 0, cnt = %i\n", cnt ); - errno = EINVAL; - goto err_out; - } - o += cnt; - } + if (cnt == -1) + goto err_out; + if (cnt <= 0) { + ntfs_log_debug("Eeek. cnt <= 0, cnt = %i\n", cnt); + errno = EINVAL; + goto err_out; + } + o += cnt; + } #ifdef HAVE_MBSINIT - /* Make sure we are back in the initial state. */ - if ( !mbsinit( &mbstate ) ) - { - ntfs_log_debug( "Eeek. mbstate not in initial state!\n" ); - errno = EILSEQ; - goto err_out; - } + /* Make sure we are back in the initial state. */ + if (!mbsinit(&mbstate)) { + ntfs_log_debug("Eeek. mbstate not in initial state!\n"); + errno = EILSEQ; + goto err_out; + } #endif - /* Now write the NULL character. */ - mbs[o] = '\0'; - if ( *outs != mbs ) - *outs = mbs; - return o; + /* Now write the NULL character. */ + mbs[o] = '\0'; + if (*outs != mbs) + *outs = mbs; + return o; err_out: - if ( mbs != *outs ) - { - int eo = errno; - free( mbs ); - errno = eo; - } - return -1; + if (mbs != *outs) { + int eo = errno; + free(mbs); + errno = eo; + } + return -1; } /** * ntfs_mbstoucs - convert a multibyte string to a little endian Unicode string - * @ins: input multibyte string buffer - * @outs: on return contains the (allocated) output Unicode string + * @ins: input multibyte string buffer + * @outs: on return contains the (allocated) output Unicode string * * Convert the input multibyte string @ins, from the current locale into the * corresponding little endian, 2-byte Unicode string. * - * The function allocates the string and the caller is responsible for calling + * The function allocates the string and the caller is responsible for calling * free(*@outs); when finished with it. * * On success the function returns the number of Unicode characters written to @@ -1014,426 +934,394 @@ err_out: * * On error, -1 is returned, and errno is set to the error code. The following * error codes can be expected: - * EINVAL Invalid arguments (e.g. @ins or @outs is NULL). - * EILSEQ The input string cannot be represented as a Unicode - * string according to the current locale. - * ENAMETOOLONG Destination buffer is too small for input string. - * ENOMEM Not enough memory to allocate destination buffer. + * EINVAL Invalid arguments (e.g. @ins or @outs is NULL). + * EILSEQ The input string cannot be represented as a Unicode + * string according to the current locale. + * ENAMETOOLONG Destination buffer is too small for input string. + * ENOMEM Not enough memory to allocate destination buffer. */ -int ntfs_mbstoucs( const char *ins, ntfschar **outs ) +int ntfs_mbstoucs(const char *ins, ntfschar **outs) { - ntfschar *ucs; - const char *s; - wchar_t wc; - int i, o, cnt, ins_len, ucs_len, ins_size; + ntfschar *ucs; + const char *s; + wchar_t wc; + int i, o, cnt, ins_len, ucs_len, ins_size; #ifdef HAVE_MBSINIT - mbstate_t mbstate; + mbstate_t mbstate; #endif - if ( !ins || !outs ) - { - errno = EINVAL; - return -1; - } + if (!ins || !outs) { + errno = EINVAL; + return -1; + } + + if (use_utf8) + return ntfs_utf8_to_utf16(ins, outs); - if ( use_utf8 ) - return ntfs_utf8_to_utf16( ins, outs ); - - /* Determine the size of the multi-byte string in bytes. */ - ins_size = strlen( ins ); - /* Determine the length of the multi-byte string. */ - s = ins; + /* Determine the size of the multi-byte string in bytes. */ + ins_size = strlen(ins); + /* Determine the length of the multi-byte string. */ + s = ins; #if defined(HAVE_MBSINIT) - memset( &mbstate, 0, sizeof( mbstate ) ); - ins_len = mbsrtowcs( NULL, ( const char ** ) & s, 0, &mbstate ); + memset(&mbstate, 0, sizeof(mbstate)); + ins_len = mbsrtowcs(NULL, (const char **)&s, 0, &mbstate); #ifdef __CYGWIN32__ - if ( !ins_len && *ins ) - { - /* Older Cygwin had broken mbsrtowcs() implementation. */ - ins_len = strlen( ins ); - } + if (!ins_len && *ins) { + /* Older Cygwin had broken mbsrtowcs() implementation. */ + ins_len = strlen(ins); + } #endif #elif !defined(DJGPP) - ins_len = mbstowcs( NULL, s, 0 ); + ins_len = mbstowcs(NULL, s, 0); #else - /* Eeek!!! DJGPP has broken mbstowcs() implementation!!! */ - ins_len = strlen( ins ); + /* Eeek!!! DJGPP has broken mbstowcs() implementation!!! */ + ins_len = strlen(ins); #endif - if ( ins_len == -1 ) - return ins_len; + if (ins_len == -1) + return ins_len; #ifdef HAVE_MBSINIT - if ( ( s != ins ) || !mbsinit( &mbstate ) ) - { + if ((s != ins) || !mbsinit(&mbstate)) { #else - if ( s != ins ) - { + if (s != ins) { #endif - errno = EILSEQ; - return -1; - } - /* Add the NULL terminator. */ - ins_len++; - ucs_len = ins_len; - ucs = ntfs_malloc( ucs_len * sizeof( ntfschar ) ); - if ( !ucs ) - return -1; + errno = EILSEQ; + return -1; + } + /* Add the NULL terminator. */ + ins_len++; + ucs_len = ins_len; + ucs = ntfs_malloc(ucs_len * sizeof(ntfschar)); + if (!ucs) + return -1; #ifdef HAVE_MBSINIT - memset( &mbstate, 0, sizeof( mbstate ) ); + memset(&mbstate, 0, sizeof(mbstate)); #else - mbtowc( NULL, NULL, 0 ); + mbtowc(NULL, NULL, 0); #endif - for ( i = o = cnt = 0; i < ins_size; i += cnt, o++ ) - { - /* Reallocate memory if necessary. */ - if ( o >= ucs_len ) - { - ntfschar *tc; - ucs_len = ( ucs_len * sizeof( ntfschar ) + 64 ) & ~63; - tc = realloc( ucs, ucs_len ); - if ( !tc ) - goto err_out; - ucs = tc; - ucs_len /= sizeof( ntfschar ); - } - /* Convert the multibyte character to a wide character. */ + for (i = o = cnt = 0; i < ins_size; i += cnt, o++) { + /* Reallocate memory if necessary. */ + if (o >= ucs_len) { + ntfschar *tc; + ucs_len = (ucs_len * sizeof(ntfschar) + 64) & ~63; + tc = realloc(ucs, ucs_len); + if (!tc) + goto err_out; + ucs = tc; + ucs_len /= sizeof(ntfschar); + } + /* Convert the multibyte character to a wide character. */ #ifdef HAVE_MBSINIT - cnt = mbrtowc( &wc, ins + i, ins_size - i, &mbstate ); + cnt = mbrtowc(&wc, ins + i, ins_size - i, &mbstate); #else - cnt = mbtowc( &wc, ins + i, ins_size - i ); + cnt = mbtowc(&wc, ins + i, ins_size - i); #endif - if ( !cnt ) - break; - if ( cnt == -1 ) - goto err_out; - if ( cnt < -1 ) - { - ntfs_log_trace( "Eeek. cnt = %i\n", cnt ); - errno = EINVAL; - goto err_out; - } - /* Make sure we are not overflowing the NTFS Unicode set. */ - if ( ( unsigned long )wc >= ( unsigned long )( 1 << - ( 8 * sizeof( ntfschar ) ) ) ) - { - errno = EILSEQ; - goto err_out; - } - /* Convert the CPU wide character to a LE Unicode character. */ - ucs[o] = cpu_to_le16( wc ); - } + if (!cnt) + break; + if (cnt == -1) + goto err_out; + if (cnt < -1) { + ntfs_log_trace("Eeek. cnt = %i\n", cnt); + errno = EINVAL; + goto err_out; + } + /* Make sure we are not overflowing the NTFS Unicode set. */ + if ((unsigned long)wc >= (unsigned long)(1 << + (8 * sizeof(ntfschar)))) { + errno = EILSEQ; + goto err_out; + } + /* Convert the CPU wide character to a LE Unicode character. */ + ucs[o] = cpu_to_le16(wc); + } #ifdef HAVE_MBSINIT - /* Make sure we are back in the initial state. */ - if ( !mbsinit( &mbstate ) ) - { - ntfs_log_trace( "Eeek. mbstate not in initial state!\n" ); - errno = EILSEQ; - goto err_out; - } + /* Make sure we are back in the initial state. */ + if (!mbsinit(&mbstate)) { + ntfs_log_trace("Eeek. mbstate not in initial state!\n"); + errno = EILSEQ; + goto err_out; + } #endif - /* Now write the NULL character. */ - ucs[o] = cpu_to_le16( L'\0' ); - *outs = ucs; - return o; + /* Now write the NULL character. */ + ucs[o] = cpu_to_le16(L'\0'); + *outs = ucs; + return o; err_out: - free( ucs ); - return -1; + free(ucs); + return -1; } /* - * Turn a UTF8 name uppercase + * Turn a UTF8 name uppercase * - * Returns an allocated uppercase name which has to be freed by caller - * or NULL if there is an error (described by errno) + * Returns an allocated uppercase name which has to be freed by caller + * or NULL if there is an error (described by errno) */ -char *ntfs_uppercase_mbs( const char *low, - const ntfschar *upcase, u32 upcase_size ) +char *ntfs_uppercase_mbs(const char *low, + const ntfschar *upcase, u32 upcase_size) { - int size; - char *upp; - u32 wc; - int n; - const char *s; - char *t; + int size; + char *upp; + u32 wc; + int n; + const char *s; + char *t; - size = strlen( low ); - upp = ( char* )ntfs_malloc( 3 * size + 1 ); - if ( upp ) - { - s = low; - t = upp; - do - { - n = utf8_to_unicode( &wc, s ); - if ( n > 0 ) - { - if ( wc < upcase_size ) - wc = le16_to_cpu( upcase[wc] ); - if ( wc < 0x80 ) - *t++ = wc; - else if ( wc < 0x800 ) - { - *t++ = ( 0xc0 | ( ( wc >> 6 ) & 0x3f ) ); - *t++ = 0x80 | ( wc & 0x3f ); - } - else if ( wc < 0x10000 ) - { - *t++ = 0xe0 | ( wc >> 12 ); - *t++ = 0x80 | ( ( wc >> 6 ) & 0x3f ); - *t++ = 0x80 | ( wc & 0x3f ); - } - else - { - *t++ = 0xf0 | ( ( wc >> 18 ) & 7 ); - *t++ = 0x80 | ( ( wc >> 12 ) & 63 ); - *t++ = 0x80 | ( ( wc >> 6 ) & 0x3f ); - *t++ = 0x80 | ( wc & 0x3f ); - } - s += n; - } - } - while ( n > 0 ); - if ( n < 0 ) - { - free( upp ); - upp = ( char* )NULL; - errno = EILSEQ; - } - *t = 0; - } - return ( upp ); + size = strlen(low); + upp = (char*)ntfs_malloc(3*size + 1); + if (upp) { + s = low; + t = upp; + do { + n = utf8_to_unicode(&wc, s); + if (n > 0) { + if (wc < upcase_size) + wc = le16_to_cpu(upcase[wc]); + if (wc < 0x80) + *t++ = wc; + else if (wc < 0x800) { + *t++ = (0xc0 | ((wc >> 6) & 0x3f)); + *t++ = 0x80 | (wc & 0x3f); + } else if (wc < 0x10000) { + *t++ = 0xe0 | (wc >> 12); + *t++ = 0x80 | ((wc >> 6) & 0x3f); + *t++ = 0x80 | (wc & 0x3f); + } else { + *t++ = 0xf0 | ((wc >> 18) & 7); + *t++ = 0x80 | ((wc >> 12) & 63); + *t++ = 0x80 | ((wc >> 6) & 0x3f); + *t++ = 0x80 | (wc & 0x3f); + } + s += n; + } + } while (n > 0); + if (n < 0) { + free(upp); + upp = (char*)NULL; + errno = EILSEQ; + } + *t = 0; + } + return (upp); } /** * ntfs_upcase_table_build - build the default upcase table for NTFS - * @uc: destination buffer where to store the built table - * @uc_len: size of destination buffer in bytes + * @uc: destination buffer where to store the built table + * @uc_len: size of destination buffer in bytes * * ntfs_upcase_table_build() builds the default upcase table for NTFS and * stores it in the caller supplied buffer @uc of size @uc_len. * * Note, @uc_len must be at least 128kiB in size or bad things will happen! */ -void ntfs_upcase_table_build( ntfschar *uc, u32 uc_len ) +void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len) { - static int uc_run_table[][3] = /* Start, End, Add */ - { - {0x0061, 0x007B, -32}, {0x0451, 0x045D, -80}, {0x1F70, 0x1F72, 74}, - {0x00E0, 0x00F7, -32}, {0x045E, 0x0460, -80}, {0x1F72, 0x1F76, 86}, - {0x00F8, 0x00FF, -32}, {0x0561, 0x0587, -48}, {0x1F76, 0x1F78, 100}, - {0x0256, 0x0258, -205}, {0x1F00, 0x1F08, 8}, {0x1F78, 0x1F7A, 128}, - {0x028A, 0x028C, -217}, {0x1F10, 0x1F16, 8}, {0x1F7A, 0x1F7C, 112}, - {0x03AC, 0x03AD, -38}, {0x1F20, 0x1F28, 8}, {0x1F7C, 0x1F7E, 126}, - {0x03AD, 0x03B0, -37}, {0x1F30, 0x1F38, 8}, {0x1FB0, 0x1FB2, 8}, - {0x03B1, 0x03C2, -32}, {0x1F40, 0x1F46, 8}, {0x1FD0, 0x1FD2, 8}, - {0x03C2, 0x03C3, -31}, {0x1F51, 0x1F52, 8}, {0x1FE0, 0x1FE2, 8}, - {0x03C3, 0x03CC, -32}, {0x1F53, 0x1F54, 8}, {0x1FE5, 0x1FE6, 7}, - {0x03CC, 0x03CD, -64}, {0x1F55, 0x1F56, 8}, {0x2170, 0x2180, -16}, - {0x03CD, 0x03CF, -63}, {0x1F57, 0x1F58, 8}, {0x24D0, 0x24EA, -26}, - {0x0430, 0x0450, -32}, {0x1F60, 0x1F68, 8}, {0xFF41, 0xFF5B, -32}, - {0} - }; - static int uc_dup_table[][2] = /* Start, End */ - { - {0x0100, 0x012F}, {0x01A0, 0x01A6}, {0x03E2, 0x03EF}, {0x04CB, 0x04CC}, - {0x0132, 0x0137}, {0x01B3, 0x01B7}, {0x0460, 0x0481}, {0x04D0, 0x04EB}, - {0x0139, 0x0149}, {0x01CD, 0x01DD}, {0x0490, 0x04BF}, {0x04EE, 0x04F5}, - {0x014A, 0x0178}, {0x01DE, 0x01EF}, {0x04BF, 0x04BF}, {0x04F8, 0x04F9}, - {0x0179, 0x017E}, {0x01F4, 0x01F5}, {0x04C1, 0x04C4}, {0x1E00, 0x1E95}, - {0x018B, 0x018B}, {0x01FA, 0x0218}, {0x04C7, 0x04C8}, {0x1EA0, 0x1EF9}, - {0} - }; - static int uc_byte_table[][2] = /* Offset, Value */ - { - {0x00FF, 0x0178}, {0x01AD, 0x01AC}, {0x01F3, 0x01F1}, {0x0269, 0x0196}, - {0x0183, 0x0182}, {0x01B0, 0x01AF}, {0x0253, 0x0181}, {0x026F, 0x019C}, - {0x0185, 0x0184}, {0x01B9, 0x01B8}, {0x0254, 0x0186}, {0x0272, 0x019D}, - {0x0188, 0x0187}, {0x01BD, 0x01BC}, {0x0259, 0x018F}, {0x0275, 0x019F}, - {0x018C, 0x018B}, {0x01C6, 0x01C4}, {0x025B, 0x0190}, {0x0283, 0x01A9}, - {0x0192, 0x0191}, {0x01C9, 0x01C7}, {0x0260, 0x0193}, {0x0288, 0x01AE}, - {0x0199, 0x0198}, {0x01CC, 0x01CA}, {0x0263, 0x0194}, {0x0292, 0x01B7}, - {0x01A8, 0x01A7}, {0x01DD, 0x018E}, {0x0268, 0x0197}, - {0} - }; - int i, r; - int k, off; + static int uc_run_table[][3] = { /* Start, End, Add */ + {0x0061, 0x007B, -32}, {0x0451, 0x045D, -80}, {0x1F70, 0x1F72, 74}, + {0x00E0, 0x00F7, -32}, {0x045E, 0x0460, -80}, {0x1F72, 0x1F76, 86}, + {0x00F8, 0x00FF, -32}, {0x0561, 0x0587, -48}, {0x1F76, 0x1F78, 100}, + {0x0256, 0x0258, -205}, {0x1F00, 0x1F08, 8}, {0x1F78, 0x1F7A, 128}, + {0x028A, 0x028C, -217}, {0x1F10, 0x1F16, 8}, {0x1F7A, 0x1F7C, 112}, + {0x03AC, 0x03AD, -38}, {0x1F20, 0x1F28, 8}, {0x1F7C, 0x1F7E, 126}, + {0x03AD, 0x03B0, -37}, {0x1F30, 0x1F38, 8}, {0x1FB0, 0x1FB2, 8}, + {0x03B1, 0x03C2, -32}, {0x1F40, 0x1F46, 8}, {0x1FD0, 0x1FD2, 8}, + {0x03C2, 0x03C3, -31}, {0x1F51, 0x1F52, 8}, {0x1FE0, 0x1FE2, 8}, + {0x03C3, 0x03CC, -32}, {0x1F53, 0x1F54, 8}, {0x1FE5, 0x1FE6, 7}, + {0x03CC, 0x03CD, -64}, {0x1F55, 0x1F56, 8}, {0x2170, 0x2180, -16}, + {0x03CD, 0x03CF, -63}, {0x1F57, 0x1F58, 8}, {0x24D0, 0x24EA, -26}, + {0x0430, 0x0450, -32}, {0x1F60, 0x1F68, 8}, {0xFF41, 0xFF5B, -32}, + {0} + }; + static int uc_dup_table[][2] = { /* Start, End */ + {0x0100, 0x012F}, {0x01A0, 0x01A6}, {0x03E2, 0x03EF}, {0x04CB, 0x04CC}, + {0x0132, 0x0137}, {0x01B3, 0x01B7}, {0x0460, 0x0481}, {0x04D0, 0x04EB}, + {0x0139, 0x0149}, {0x01CD, 0x01DD}, {0x0490, 0x04BF}, {0x04EE, 0x04F5}, + {0x014A, 0x0178}, {0x01DE, 0x01EF}, {0x04BF, 0x04BF}, {0x04F8, 0x04F9}, + {0x0179, 0x017E}, {0x01F4, 0x01F5}, {0x04C1, 0x04C4}, {0x1E00, 0x1E95}, + {0x018B, 0x018B}, {0x01FA, 0x0218}, {0x04C7, 0x04C8}, {0x1EA0, 0x1EF9}, + {0} + }; + static int uc_byte_table[][2] = { /* Offset, Value */ + {0x00FF, 0x0178}, {0x01AD, 0x01AC}, {0x01F3, 0x01F1}, {0x0269, 0x0196}, + {0x0183, 0x0182}, {0x01B0, 0x01AF}, {0x0253, 0x0181}, {0x026F, 0x019C}, + {0x0185, 0x0184}, {0x01B9, 0x01B8}, {0x0254, 0x0186}, {0x0272, 0x019D}, + {0x0188, 0x0187}, {0x01BD, 0x01BC}, {0x0259, 0x018F}, {0x0275, 0x019F}, + {0x018C, 0x018B}, {0x01C6, 0x01C4}, {0x025B, 0x0190}, {0x0283, 0x01A9}, + {0x0192, 0x0191}, {0x01C9, 0x01C7}, {0x0260, 0x0193}, {0x0288, 0x01AE}, + {0x0199, 0x0198}, {0x01CC, 0x01CA}, {0x0263, 0x0194}, {0x0292, 0x01B7}, + {0x01A8, 0x01A7}, {0x01DD, 0x018E}, {0x0268, 0x0197}, + {0} + }; + int i, r; + int k, off; - memset( ( char* )uc, 0, uc_len ); - uc_len >>= 1; - if ( uc_len > 65536 ) - uc_len = 65536; - for ( i = 0; ( u32 )i < uc_len; i++ ) - uc[i] = cpu_to_le16( i ); - for ( r = 0; uc_run_table[r][0]; r++ ) - { - off = uc_run_table[r][2]; - for ( i = uc_run_table[r][0]; i < uc_run_table[r][1]; i++ ) - uc[i] = cpu_to_le16( i + off ); - } - for ( r = 0; uc_dup_table[r][0]; r++ ) - for ( i = uc_dup_table[r][0]; i < uc_dup_table[r][1]; i += 2 ) - uc[i + 1] = cpu_to_le16( i ); - for ( r = 0; uc_byte_table[r][0]; r++ ) - { - k = uc_byte_table[r][1]; - uc[uc_byte_table[r][0]] = cpu_to_le16( k ); - } + memset((char*)uc, 0, uc_len); + uc_len >>= 1; + if (uc_len > 65536) + uc_len = 65536; + for (i = 0; (u32)i < uc_len; i++) + uc[i] = cpu_to_le16(i); + for (r = 0; uc_run_table[r][0]; r++) { + off = uc_run_table[r][2]; + for (i = uc_run_table[r][0]; i < uc_run_table[r][1]; i++) + uc[i] = cpu_to_le16(i + off); + } + for (r = 0; uc_dup_table[r][0]; r++) + for (i = uc_dup_table[r][0]; i < uc_dup_table[r][1]; i += 2) + uc[i + 1] = cpu_to_le16(i); + for (r = 0; uc_byte_table[r][0]; r++) { + k = uc_byte_table[r][1]; + uc[uc_byte_table[r][0]] = cpu_to_le16(k); + } } /* - * Build a table for converting to lower case + * Build a table for converting to lower case * - * This is only meaningful when there is a single lower case - * character leading to an upper case one, and currently the - * only exception is the greek letter sigma which has a single - * upper case glyph (code U+03A3), but two lower case glyphs - * (code U+03C3 and U+03C2, the latter to be used at the end - * of a word). In the following implementation the upper case - * sigma will be lowercased as U+03C3. + * This is only meaningful when there is a single lower case + * character leading to an upper case one, and currently the + * only exception is the greek letter sigma which has a single + * upper case glyph (code U+03A3), but two lower case glyphs + * (code U+03C3 and U+03C2, the latter to be used at the end + * of a word). In the following implementation the upper case + * sigma will be lowercased as U+03C3. */ -ntfschar *ntfs_locase_table_build( const ntfschar *uc, u32 uc_cnt ) +ntfschar *ntfs_locase_table_build(const ntfschar *uc, u32 uc_cnt) { - ntfschar *lc; - u32 upp; - u32 i; + ntfschar *lc; + u32 upp; + u32 i; - lc = ( ntfschar* )ntfs_malloc( uc_cnt * sizeof( ntfschar ) ); - if ( lc ) - { - for ( i = 0; i < uc_cnt; i++ ) - lc[i] = cpu_to_le16( i ); - for ( i = 0; i < uc_cnt; i++ ) - { - upp = le16_to_cpu( uc[i] ); - if ( ( upp != i ) && ( upp < uc_cnt ) ) - lc[upp] = cpu_to_le16( i ); - } - } - else - ntfs_log_error( "Could not build the locase table\n" ); - return ( lc ); + lc = (ntfschar*)ntfs_malloc(uc_cnt*sizeof(ntfschar)); + if (lc) { + for (i=0; i NTFS_MAX_NAME_LEN ) - { - free( ucs ); - errno = ENAMETOOLONG; - return NULL; - } - if ( !ucs || !*len ) - { - ucs = AT_UNNAMED; - *len = 0; - } - return ucs; + if (s && ((*len = ntfs_mbstoucs(s, &ucs)) == -1)) { + ntfs_log_perror("Couldn't convert '%s' to Unicode", s); + return NULL; + } + if (*len > NTFS_MAX_NAME_LEN) { + free(ucs); + errno = ENAMETOOLONG; + return NULL; + } + if (!ucs || !*len) { + ucs = AT_UNNAMED; + *len = 0; + } + return ucs; } /** * ntfs_ucsfree - free memory allocated by ntfs_str2ucs() - * @ucs input string to be freed + * @ucs input string to be freed * * Free memory at @ucs and which was allocated by ntfs_str2ucs. * * Return value: none. */ -void ntfs_ucsfree( ntfschar *ucs ) +void ntfs_ucsfree(ntfschar *ucs) { - if ( ucs && ( ucs != AT_UNNAMED ) ) - free( ucs ); + if (ucs && (ucs != AT_UNNAMED)) + free(ucs); } /* - * Check whether a name contains no chars forbidden - * for DOS or Win32 use + * Check whether a name contains no chars forbidden + * for DOS or Win32 use * - * If there is a bad char, errno is set to EINVAL + * If there is a bad char, errno is set to EINVAL */ -BOOL ntfs_forbidden_chars( const ntfschar *name, int len ) +BOOL ntfs_forbidden_chars(const ntfschar *name, int len) { - BOOL forbidden; - int ch; - int i; - u32 mainset = ( 1L << ( '\"' - 0x20 ) ) - | ( 1L << ( '*' - 0x20 ) ) - | ( 1L << ( '/' - 0x20 ) ) - | ( 1L << ( ':' - 0x20 ) ) - | ( 1L << ( '<' - 0x20 ) ) - | ( 1L << ( '>' - 0x20 ) ) - | ( 1L << ( '?' - 0x20 ) ); + BOOL forbidden; + int ch; + int i; + u32 mainset = (1L << ('\"' - 0x20)) + | (1L << ('*' - 0x20)) + | (1L << ('/' - 0x20)) + | (1L << (':' - 0x20)) + | (1L << ('<' - 0x20)) + | (1L << ('>' - 0x20)) + | (1L << ('?' - 0x20)); - forbidden = ( len == 0 ) - || ( le16_to_cpu( name[len-1] ) == ' ' ) - || ( le16_to_cpu( name[len-1] ) == '.' ); - for ( i = 0; i < len; i++ ) - { - ch = le16_to_cpu( name[i] ); - if ( ( ch < 0x20 ) - || ( ( ch < 0x40 ) - && ( ( 1L << ( ch - 0x20 ) ) & mainset ) ) - || ( ch == '\\' ) - || ( ch == '|' ) ) - forbidden = TRUE; - } - if ( forbidden ) - errno = EINVAL; - return ( forbidden ); + forbidden = (len == 0) + || (le16_to_cpu(name[len-1]) == ' ') + || (le16_to_cpu(name[len-1]) == '.'); + for (i=0; i= vol->upcase_len ) - || ( ( shortname[i] != longname[i] ) - && ( shortname[i] != vol->upcase[ch] ) ) ) - collapsible = FALSE; - } - return ( collapsible ); + collapsible = shortlen == longlen; + if (collapsible) + for (i=0; i= vol->upcase_len) + || ((shortname[i] != longname[i]) + && (shortname[i] != vol->upcase[ch]))) + collapsible = FALSE; + } + return (collapsible); } /* @@ -1441,106 +1329,97 @@ BOOL ntfs_collapsible_chars( ntfs_volume *vol, * Use UTF-8 unless specified otherwise. */ -int ntfs_set_char_encoding( const char *locale ) +int ntfs_set_char_encoding(const char *locale) { - use_utf8 = 0; - if ( !locale || strstr( locale, "utf8" ) || strstr( locale, "UTF8" ) - || strstr( locale, "utf-8" ) || strstr( locale, "UTF-8" ) ) - use_utf8 = 1; - else if ( setlocale( LC_ALL, locale ) ) - use_utf8 = 0; - else - { - ntfs_log_error( "Invalid locale, encoding to UTF-8\n" ); - use_utf8 = 1; - } - return 0; /* always successful */ + use_utf8 = 0; + if (!locale || strstr(locale,"utf8") || strstr(locale,"UTF8") + || strstr(locale,"utf-8") || strstr(locale,"UTF-8")) + use_utf8 = 1; + else + if (setlocale(LC_ALL, locale)) + use_utf8 = 0; + else { + ntfs_log_error("Invalid locale, encoding to UTF-8\n"); + use_utf8 = 1; + } + return 0; /* always successful */ } #if defined(__APPLE__) || defined(__DARWIN__) -int ntfs_macosx_normalize_filenames( int normalize ) -{ +int ntfs_macosx_normalize_filenames(int normalize) { #ifdef ENABLE_NFCONV - if ( normalize == 0 || normalize == 1 ) - { - nfconvert_utf8 = normalize; - return 0; - } - else - return -1; + if(normalize == 0 || normalize == 1) { + nfconvert_utf8 = normalize; + return 0; + } + else + return -1; #else - return -1; + return -1; #endif /* ENABLE_NFCONV */ -} +} -int ntfs_macosx_normalize_utf8( const char *utf8_string, char **target, - int composed ) -{ +int ntfs_macosx_normalize_utf8(const char *utf8_string, char **target, + int composed) { #ifdef ENABLE_NFCONV - /* For this code to compile, the CoreFoundation framework must be fed to the linker. */ - CFStringRef cfSourceString; - CFMutableStringRef cfMutableString; - CFRange rangeToProcess; - CFIndex requiredBufferLength; - char *result = NULL; - int resultLength = -1; + /* For this code to compile, the CoreFoundation framework must be fed to the linker. */ + CFStringRef cfSourceString; + CFMutableStringRef cfMutableString; + CFRange rangeToProcess; + CFIndex requiredBufferLength; + char *result = NULL; + int resultLength = -1; + + /* Convert the UTF-8 string to a CFString. */ + cfSourceString = CFStringCreateWithCString(kCFAllocatorDefault, utf8_string, kCFStringEncodingUTF8); + if(cfSourceString == NULL) { + ntfs_log_error("CFStringCreateWithCString failed!\n"); + return -2; + } + + /* Create a mutable string from cfSourceString that we are free to modify. */ + cfMutableString = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, cfSourceString); + CFRelease(cfSourceString); /* End-of-life. */ + if(cfMutableString == NULL) { + ntfs_log_error("CFStringCreateMutableCopy failed!\n"); + return -3; + } + + /* Normalize the mutable string to the desired normalization form. */ + CFStringNormalize(cfMutableString, (composed != 0 ? kCFStringNormalizationFormC : kCFStringNormalizationFormD)); + + /* Store the resulting string in a '\0'-terminated UTF-8 encoded char* buffer. */ + rangeToProcess = CFRangeMake(0, CFStringGetLength(cfMutableString)); + if(CFStringGetBytes(cfMutableString, rangeToProcess, kCFStringEncodingUTF8, 0, false, NULL, 0, &requiredBufferLength) > 0) { + resultLength = sizeof(char)*(requiredBufferLength + 1); + result = ntfs_calloc(resultLength); + + if(result != NULL) { + if(CFStringGetBytes(cfMutableString, rangeToProcess, kCFStringEncodingUTF8, + 0, false, (UInt8*)result, resultLength-1, &requiredBufferLength) <= 0) { + ntfs_log_error("Could not perform UTF-8 conversion of normalized CFMutableString.\n"); + free(result); + result = NULL; + } + } + else + ntfs_log_error("Could not perform a ntfs_calloc of %d bytes for char *result.\n", resultLength); + } + else + ntfs_log_error("Could not perform check for required length of UTF-8 conversion of normalized CFMutableString.\n"); - /* Convert the UTF-8 string to a CFString. */ - cfSourceString = CFStringCreateWithCString( kCFAllocatorDefault, utf8_string, kCFStringEncodingUTF8 ); - if ( cfSourceString == NULL ) - { - ntfs_log_error( "CFStringCreateWithCString failed!\n" ); - return -2; - } - - /* Create a mutable string from cfSourceString that we are free to modify. */ - cfMutableString = CFStringCreateMutableCopy( kCFAllocatorDefault, 0, cfSourceString ); - CFRelease( cfSourceString ); /* End-of-life. */ - if ( cfMutableString == NULL ) - { - ntfs_log_error( "CFStringCreateMutableCopy failed!\n" ); - return -3; - } - - /* Normalize the mutable string to the desired normalization form. */ - CFStringNormalize( cfMutableString, ( composed != 0 ? kCFStringNormalizationFormC : kCFStringNormalizationFormD ) ); - - /* Store the resulting string in a '\0'-terminated UTF-8 encoded char* buffer. */ - rangeToProcess = CFRangeMake( 0, CFStringGetLength( cfMutableString ) ); - if ( CFStringGetBytes( cfMutableString, rangeToProcess, kCFStringEncodingUTF8, 0, false, NULL, 0, &requiredBufferLength ) > 0 ) - { - resultLength = sizeof( char ) * ( requiredBufferLength + 1 ); - result = ntfs_calloc( resultLength ); - - if ( result != NULL ) - { - if ( CFStringGetBytes( cfMutableString, rangeToProcess, kCFStringEncodingUTF8, - 0, false, ( UInt8* )result, resultLength - 1, &requiredBufferLength ) <= 0 ) - { - ntfs_log_error( "Could not perform UTF-8 conversion of normalized CFMutableString.\n" ); - free( result ); - result = NULL; - } - } - else - ntfs_log_error( "Could not perform a ntfs_calloc of %d bytes for char *result.\n", resultLength ); - } - else - ntfs_log_error( "Could not perform check for required length of UTF-8 conversion of normalized CFMutableString.\n" ); - - - CFRelease( cfMutableString ); - - if ( result != NULL ) - { - *target = result; - return resultLength - 1; - } - else - return -1; + + CFRelease(cfMutableString); + + if(result != NULL) { + *target = result; + return resultLength - 1; + } + else + return -1; #else - return -1; + return -1; #endif /* ENABLE_NFCONV */ } #endif /* defined(__APPLE__) || defined(__DARWIN__) */ diff --git a/source/libntfs/unistr.h b/source/libntfs/unistr.h index ee7c3f83..639c5033 100644 --- a/source/libntfs/unistr.h +++ b/source/libntfs/unistr.h @@ -1,6 +1,6 @@ /* * unistr.h - Exports for Unicode string handling. Originated from the Linux-NTFS - * project. + * project. * * Copyright (c) 2000-2004 Anton Altaparmakov * @@ -26,53 +26,53 @@ #include "types.h" #include "layout.h" -extern BOOL ntfs_names_are_equal( const ntfschar *s1, size_t s1_len, - const ntfschar *s2, size_t s2_len, const IGNORE_CASE_BOOL ic, - const ntfschar *upcase, const u32 upcase_size ); +extern BOOL ntfs_names_are_equal(const ntfschar *s1, size_t s1_len, + const ntfschar *s2, size_t s2_len, const IGNORE_CASE_BOOL ic, + const ntfschar *upcase, const u32 upcase_size); -extern int ntfs_names_full_collate( const ntfschar *name1, const u32 name1_len, - const ntfschar *name2, const u32 name2_len, - const IGNORE_CASE_BOOL ic, - const ntfschar *upcase, const u32 upcase_len ); +extern int ntfs_names_full_collate(const ntfschar *name1, const u32 name1_len, + const ntfschar *name2, const u32 name2_len, + const IGNORE_CASE_BOOL ic, + const ntfschar *upcase, const u32 upcase_len); -extern int ntfs_ucsncmp( const ntfschar *s1, const ntfschar *s2, size_t n ); +extern int ntfs_ucsncmp(const ntfschar *s1, const ntfschar *s2, size_t n); -extern int ntfs_ucsncasecmp( const ntfschar *s1, const ntfschar *s2, size_t n, - const ntfschar *upcase, const u32 upcase_size ); +extern int ntfs_ucsncasecmp(const ntfschar *s1, const ntfschar *s2, size_t n, + const ntfschar *upcase, const u32 upcase_size); -extern u32 ntfs_ucsnlen( const ntfschar *s, u32 maxlen ); +extern u32 ntfs_ucsnlen(const ntfschar *s, u32 maxlen); -extern ntfschar *ntfs_ucsndup( const ntfschar *s, u32 maxlen ); +extern ntfschar *ntfs_ucsndup(const ntfschar *s, u32 maxlen); -extern void ntfs_name_upcase( ntfschar *name, u32 name_len, - const ntfschar *upcase, const u32 upcase_len ); +extern void ntfs_name_upcase(ntfschar *name, u32 name_len, + const ntfschar *upcase, const u32 upcase_len); -extern void ntfs_name_locase( ntfschar *name, u32 name_len, - const ntfschar *locase, const u32 locase_len ); +extern void ntfs_name_locase(ntfschar *name, u32 name_len, + const ntfschar *locase, const u32 locase_len); -extern void ntfs_file_value_upcase( FILE_NAME_ATTR *file_name_attr, - const ntfschar *upcase, const u32 upcase_len ); +extern void ntfs_file_value_upcase(FILE_NAME_ATTR *file_name_attr, + const ntfschar *upcase, const u32 upcase_len); -extern int ntfs_ucstombs( const ntfschar *ins, const int ins_len, char **outs, - int outs_len ); -extern int ntfs_mbstoucs( const char *ins, ntfschar **outs ); +extern int ntfs_ucstombs(const ntfschar *ins, const int ins_len, char **outs, + int outs_len); +extern int ntfs_mbstoucs(const char *ins, ntfschar **outs); -extern char *ntfs_uppercase_mbs( const char *low, - const ntfschar *upcase, u32 upcase_len ); +extern char *ntfs_uppercase_mbs(const char *low, + const ntfschar *upcase, u32 upcase_len); -extern void ntfs_upcase_table_build( ntfschar *uc, u32 uc_len ); -extern ntfschar *ntfs_locase_table_build( const ntfschar *uc, u32 uc_cnt ); +extern void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len); +extern ntfschar *ntfs_locase_table_build(const ntfschar *uc, u32 uc_cnt); -extern ntfschar *ntfs_str2ucs( const char *s, int *len ); +extern ntfschar *ntfs_str2ucs(const char *s, int *len); -extern void ntfs_ucsfree( ntfschar *ucs ); +extern void ntfs_ucsfree(ntfschar *ucs); -extern BOOL ntfs_forbidden_chars( const ntfschar *name, int len ); -extern BOOL ntfs_collapsible_chars( ntfs_volume *vol, - const ntfschar *shortname, int shortlen, - const ntfschar *longname, int longlen ); +extern BOOL ntfs_forbidden_chars(const ntfschar *name, int len); +extern BOOL ntfs_collapsible_chars(ntfs_volume *vol, + const ntfschar *shortname, int shortlen, + const ntfschar *longname, int longlen); -extern int ntfs_set_char_encoding( const char *locale ); +extern int ntfs_set_char_encoding(const char *locale); #if defined(__APPLE__) || defined(__DARWIN__) /** @@ -86,17 +86,17 @@ extern int ntfs_set_char_encoding( const char *locale ); * normalization form. Since Windows and most other OS:es use the NFC form while Mac OS X * mostly uses NFD, this conversion increases compatibility between Mac applications and * NTFS-3G. - * + * * @param normalize decides whether or not the string functions will do automatic filename * normalization when converting to and from UTF-8. 0 means normalization is disabled, * 1 means it is enabled. * @return -1 if the argument was invalid or an error occurred, 0 if all went well. */ -extern int ntfs_macosx_normalize_filenames( int normalize ); +extern int ntfs_macosx_normalize_filenames(int normalize); /** * Mac OS X only. - * + * * Normalizes the input string "utf8_string" to one of the normalization forms NFD or NFC. * The parameter "composed" decides whether output should be in composed, NFC, form * (composed == 1) or decomposed, NFD, form (composed == 0). @@ -111,7 +111,7 @@ extern int ntfs_macosx_normalize_filenames( int normalize ); * @return -1 if the normalization failed for some reason, otherwise the length of the * normalized string stored in target. */ -extern int ntfs_macosx_normalize_utf8( const char *utf8_string, char **target, int composed ); +extern int ntfs_macosx_normalize_utf8(const char *utf8_string, char **target, int composed); #endif /* defined(__APPLE__) || defined(__DARWIN__) */ #endif /* defined _NTFS_UNISTR_H */ diff --git a/source/libntfs/volume.c b/source/libntfs/volume.c index d10d890f..629ec92c 100644 --- a/source/libntfs/volume.c +++ b/source/libntfs/volume.c @@ -69,51 +69,51 @@ #include "cache.h" #include "misc.h" -const char *ntfs_home = - "Ntfs-3g news, support and information: http://ntfs-3g.org\n"; +const char *ntfs_home = +"Ntfs-3g news, support and information: http://ntfs-3g.org\n"; static const char *invalid_ntfs_msg = - "The device '%s' doesn't seem to have a valid NTFS.\n" - "Maybe the wrong device is used? Or the whole disk instead of a\n" - "partition (e.g. /dev/sda, not /dev/sda1)? Or the other way around?\n"; +"The device '%s' doesn't seem to have a valid NTFS.\n" +"Maybe the wrong device is used? Or the whole disk instead of a\n" +"partition (e.g. /dev/sda, not /dev/sda1)? Or the other way around?\n"; static const char *corrupt_volume_msg = - "NTFS is either inconsistent, or there is a hardware fault, or it's a\n" - "SoftRAID/FakeRAID hardware. In the first case run chkdsk /f on Windows\n" - "then reboot into Windows twice. The usage of the /f parameter is very\n" - "important! If the device is a SoftRAID/FakeRAID then first activate\n" - "it and mount a different device under the /dev/mapper/ directory, (e.g.\n" - "/dev/mapper/nvidia_eahaabcc1). Please see the 'dmraid' documentation\n" - "for more details.\n"; +"NTFS is either inconsistent, or there is a hardware fault, or it's a\n" +"SoftRAID/FakeRAID hardware. In the first case run chkdsk /f on Windows\n" +"then reboot into Windows twice. The usage of the /f parameter is very\n" +"important! If the device is a SoftRAID/FakeRAID then first activate\n" +"it and mount a different device under the /dev/mapper/ directory, (e.g.\n" +"/dev/mapper/nvidia_eahaabcc1). Please see the 'dmraid' documentation\n" +"for more details.\n"; static const char *hibernated_volume_msg = - "The NTFS partition is hibernated. Please resume and shutdown Windows\n" - "properly, or mount the volume read-only with the 'ro' mount option, or\n" - "mount the volume read-write with the 'remove_hiberfile' mount option.\n" - "For example type on the command line:\n" - "\n" - " mount -t ntfs-3g -o remove_hiberfile %s %s\n" - "\n"; +"The NTFS partition is hibernated. Please resume and shutdown Windows\n" +"properly, or mount the volume read-only with the 'ro' mount option, or\n" +"mount the volume read-write with the 'remove_hiberfile' mount option.\n" +"For example type on the command line:\n" +"\n" +" mount -t ntfs-3g -o remove_hiberfile %s %s\n" +"\n"; static const char *unclean_journal_msg = - "Write access is denied because the disk wasn't safely powered\n" - "off and the 'norecover' mount option was specified.\n"; +"Write access is denied because the disk wasn't safely powered\n" +"off and the 'norecover' mount option was specified.\n"; static const char *opened_volume_msg = - "Mount is denied because the NTFS volume is already exclusively opened.\n" - "The volume may be already mounted, or another software may use it which\n" - "could be identified for example by the help of the 'fuser' command.\n"; +"Mount is denied because the NTFS volume is already exclusively opened.\n" +"The volume may be already mounted, or another software may use it which\n" +"could be identified for example by the help of the 'fuser' command.\n"; static const char *fakeraid_msg = - "Either the device is missing or it's powered down, or you have\n" - "SoftRAID hardware and must use an activated, different device under\n" - "/dev/mapper/, (e.g. /dev/mapper/nvidia_eahaabcc1) to mount NTFS.\n" - "Please see the 'dmraid' documentation for help.\n"; +"Either the device is missing or it's powered down, or you have\n" +"SoftRAID hardware and must use an activated, different device under\n" +"/dev/mapper/, (e.g. /dev/mapper/nvidia_eahaabcc1) to mount NTFS.\n" +"Please see the 'dmraid' documentation for help.\n"; static const char *access_denied_msg = - "Please check '%s' and the ntfs-3g binary permissions,\n" - "and the mounting user ID. More explanation is provided at\n" - "http://ntfs-3g.org/support.html#unprivileged\n"; +"Please check '%s' and the ntfs-3g binary permissions,\n" +"and the mounting user ID. More explanation is provided at\n" +"http://ntfs-3g.org/support.html#unprivileged\n"; /** * ntfs_volume_alloc - Create an NTFS volume object and initialise it @@ -122,37 +122,35 @@ static const char *access_denied_msg = * * Returns: */ -ntfs_volume *ntfs_volume_alloc( void ) +ntfs_volume *ntfs_volume_alloc(void) { - return ntfs_calloc( sizeof( ntfs_volume ) ); + return ntfs_calloc(sizeof(ntfs_volume)); } -static void ntfs_attr_free( ntfs_attr **na ) +static void ntfs_attr_free(ntfs_attr **na) { - if ( na && *na ) - { - ntfs_attr_close( *na ); - *na = NULL; - } + if (na && *na) { + ntfs_attr_close(*na); + *na = NULL; + } } -static int ntfs_inode_free( ntfs_inode **ni ) +static int ntfs_inode_free(ntfs_inode **ni) { - int ret = -1; + int ret = -1; - if ( ni && *ni ) - { - ret = ntfs_inode_close( *ni ); - *ni = NULL; - } - - return ret; + if (ni && *ni) { + ret = ntfs_inode_close(*ni); + *ni = NULL; + } + + return ret; } -static void ntfs_error_set( int *err ) +static void ntfs_error_set(int *err) { - if ( !*err ) - *err = errno; + if (!*err) + *err = errno; } /** @@ -163,71 +161,69 @@ static void ntfs_error_set( int *err ) * * Returns: */ -static int __ntfs_volume_release( ntfs_volume *v ) +static int __ntfs_volume_release(ntfs_volume *v) { - int err = 0; + int err = 0; - if ( ntfs_inode_free( &v->vol_ni ) ) - ntfs_error_set( &err ); - /* - * FIXME: Inodes must be synced before closing - * attributes, otherwise unmount could fail. - */ - if ( v->lcnbmp_ni && NInoDirty( v->lcnbmp_ni ) ) - ntfs_inode_sync( v->lcnbmp_ni ); - ntfs_attr_free( &v->lcnbmp_na ); - if ( ntfs_inode_free( &v->lcnbmp_ni ) ) - ntfs_error_set( &err ); + if (ntfs_inode_free(&v->vol_ni)) + ntfs_error_set(&err); + /* + * FIXME: Inodes must be synced before closing + * attributes, otherwise unmount could fail. + */ + if (v->lcnbmp_ni && NInoDirty(v->lcnbmp_ni)) + ntfs_inode_sync(v->lcnbmp_ni); + ntfs_attr_free(&v->lcnbmp_na); + if (ntfs_inode_free(&v->lcnbmp_ni)) + ntfs_error_set(&err); + + if (v->mft_ni && NInoDirty(v->mft_ni)) + ntfs_inode_sync(v->mft_ni); + ntfs_attr_free(&v->mftbmp_na); + ntfs_attr_free(&v->mft_na); + if (ntfs_inode_free(&v->mft_ni)) + ntfs_error_set(&err); + + if (v->mftmirr_ni && NInoDirty(v->mftmirr_ni)) + ntfs_inode_sync(v->mftmirr_ni); + ntfs_attr_free(&v->mftmirr_na); + if (ntfs_inode_free(&v->mftmirr_ni)) + ntfs_error_set(&err); + + if (v->dev) { + struct ntfs_device *dev = v->dev; - if ( v->mft_ni && NInoDirty( v->mft_ni ) ) - ntfs_inode_sync( v->mft_ni ); - ntfs_attr_free( &v->mftbmp_na ); - ntfs_attr_free( &v->mft_na ); - if ( ntfs_inode_free( &v->mft_ni ) ) - ntfs_error_set( &err ); + if (dev->d_ops->sync(dev)) + ntfs_error_set(&err); + if (dev->d_ops->close(dev)) + ntfs_error_set(&err); + } - if ( v->mftmirr_ni && NInoDirty( v->mftmirr_ni ) ) - ntfs_inode_sync( v->mftmirr_ni ); - ntfs_attr_free( &v->mftmirr_na ); - if ( ntfs_inode_free( &v->mftmirr_ni ) ) - ntfs_error_set( &err ); + ntfs_free_lru_caches(v); + free(v->vol_name); + free(v->upcase); + if (v->locase) free(v->locase); + free(v->attrdef); + free(v); - if ( v->dev ) - { - struct ntfs_device *dev = v->dev; - - if ( dev->d_ops->sync( dev ) ) - ntfs_error_set( &err ); - if ( dev->d_ops->close( dev ) ) - ntfs_error_set( &err ); - } - - ntfs_free_lru_caches( v ); - free( v->vol_name ); - free( v->upcase ); - if ( v->locase ) free( v->locase ); - free( v->attrdef ); - free( v ); - - errno = err; - return errno ? -1 : 0; + errno = err; + return errno ? -1 : 0; } -static void ntfs_attr_setup_flag( ntfs_inode *ni ) +static void ntfs_attr_setup_flag(ntfs_inode *ni) { - STANDARD_INFORMATION *si; + STANDARD_INFORMATION *si; - si = ntfs_attr_readall( ni, AT_STANDARD_INFORMATION, AT_UNNAMED, 0, NULL ); - if ( si ) - { - ni->flags = si->file_attributes; - free( si ); - } + si = ntfs_attr_readall(ni, AT_STANDARD_INFORMATION, AT_UNNAMED, 0, NULL); + if (si) { + ni->flags = si->file_attributes; + free(si); + } } /** * ntfs_mft_load - load the $MFT and setup the ntfs volume with it - * @vol: ntfs volume whose $MFT to load + * @vol: ntfs volume whose $MFT to load * * Load $MFT from @vol and setup @vol with it. After calling this function the * volume @vol is ready for use by all read access functions provided by the @@ -235,196 +231,178 @@ static void ntfs_attr_setup_flag( ntfs_inode *ni ) * * Return 0 on success and -1 on error with errno set to the error code. */ -static int ntfs_mft_load( ntfs_volume *vol ) +static int ntfs_mft_load(ntfs_volume *vol) { - VCN next_vcn, last_vcn, highest_vcn; - s64 l; - MFT_RECORD *mb = NULL; - ntfs_attr_search_ctx *ctx = NULL; - ATTR_RECORD *a; - int eo; + VCN next_vcn, last_vcn, highest_vcn; + s64 l; + MFT_RECORD *mb = NULL; + ntfs_attr_search_ctx *ctx = NULL; + ATTR_RECORD *a; + int eo; - /* Manually setup an ntfs_inode. */ - vol->mft_ni = ntfs_inode_allocate( vol ); - mb = ntfs_malloc( vol->mft_record_size ); - if ( !vol->mft_ni || !mb ) - { - ntfs_log_perror( "Error allocating memory for $MFT" ); - goto error_exit; - } - vol->mft_ni->mft_no = 0; - vol->mft_ni->mrec = mb; - /* Can't use any of the higher level functions yet! */ - l = ntfs_mst_pread( vol->dev, vol->mft_lcn << vol->cluster_size_bits, 1, - vol->mft_record_size, mb ); - if ( l != 1 ) - { - if ( l != -1 ) - errno = EIO; - ntfs_log_perror( "Error reading $MFT" ); - goto error_exit; - } + /* Manually setup an ntfs_inode. */ + vol->mft_ni = ntfs_inode_allocate(vol); + mb = ntfs_malloc(vol->mft_record_size); + if (!vol->mft_ni || !mb) { + ntfs_log_perror("Error allocating memory for $MFT"); + goto error_exit; + } + vol->mft_ni->mft_no = 0; + vol->mft_ni->mrec = mb; + /* Can't use any of the higher level functions yet! */ + l = ntfs_mst_pread(vol->dev, vol->mft_lcn << vol->cluster_size_bits, 1, + vol->mft_record_size, mb); + if (l != 1) { + if (l != -1) + errno = EIO; + ntfs_log_perror("Error reading $MFT"); + goto error_exit; + } + + if (ntfs_mft_record_check(vol, 0, mb)) + goto error_exit; + + ctx = ntfs_attr_get_search_ctx(vol->mft_ni, NULL); + if (!ctx) + goto error_exit; - if ( ntfs_mft_record_check( vol, 0, mb ) ) - goto error_exit; - - ctx = ntfs_attr_get_search_ctx( vol->mft_ni, NULL ); - if ( !ctx ) - goto error_exit; - - /* Find the $ATTRIBUTE_LIST attribute in $MFT if present. */ - if ( ntfs_attr_lookup( AT_ATTRIBUTE_LIST, AT_UNNAMED, 0, 0, 0, NULL, 0, - ctx ) ) - { - if ( errno != ENOENT ) - { - ntfs_log_error( "$MFT has corrupt attribute list.\n" ); - goto io_error_exit; - } - goto mft_has_no_attr_list; - } - NInoSetAttrList( vol->mft_ni ); - l = ntfs_get_attribute_value_length( ctx->attr ); - if ( l <= 0 || l > 0x40000 ) - { - ntfs_log_error( "$MFT/$ATTR_LIST invalid length (%lld).\n", - ( long long )l ); - goto io_error_exit; - } - vol->mft_ni->attr_list_size = l; - vol->mft_ni->attr_list = ntfs_malloc( l ); - if ( !vol->mft_ni->attr_list ) - goto error_exit; - - l = ntfs_get_attribute_value( vol, ctx->attr, vol->mft_ni->attr_list ); - if ( !l ) - { - ntfs_log_error( "Failed to get value of $MFT/$ATTR_LIST.\n" ); - goto io_error_exit; - } - if ( l != vol->mft_ni->attr_list_size ) - { - ntfs_log_error( "Partial read of $MFT/$ATTR_LIST (%lld != " - "%u).\n", ( long long )l, - vol->mft_ni->attr_list_size ); - goto io_error_exit; - } + /* Find the $ATTRIBUTE_LIST attribute in $MFT if present. */ + if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, AT_UNNAMED, 0, 0, 0, NULL, 0, + ctx)) { + if (errno != ENOENT) { + ntfs_log_error("$MFT has corrupt attribute list.\n"); + goto io_error_exit; + } + goto mft_has_no_attr_list; + } + NInoSetAttrList(vol->mft_ni); + l = ntfs_get_attribute_value_length(ctx->attr); + if (l <= 0 || l > 0x40000) { + ntfs_log_error("$MFT/$ATTR_LIST invalid length (%lld).\n", + (long long)l); + goto io_error_exit; + } + vol->mft_ni->attr_list_size = l; + vol->mft_ni->attr_list = ntfs_malloc(l); + if (!vol->mft_ni->attr_list) + goto error_exit; + + l = ntfs_get_attribute_value(vol, ctx->attr, vol->mft_ni->attr_list); + if (!l) { + ntfs_log_error("Failed to get value of $MFT/$ATTR_LIST.\n"); + goto io_error_exit; + } + if (l != vol->mft_ni->attr_list_size) { + ntfs_log_error("Partial read of $MFT/$ATTR_LIST (%lld != " + "%u).\n", (long long)l, + vol->mft_ni->attr_list_size); + goto io_error_exit; + } mft_has_no_attr_list: - ntfs_attr_setup_flag( vol->mft_ni ); + ntfs_attr_setup_flag(vol->mft_ni); + + /* We now have a fully setup ntfs inode for $MFT in vol->mft_ni. */ + + /* Get an ntfs attribute for $MFT/$DATA and set it up, too. */ + vol->mft_na = ntfs_attr_open(vol->mft_ni, AT_DATA, AT_UNNAMED, 0); + if (!vol->mft_na) { + ntfs_log_perror("Failed to open ntfs attribute"); + goto error_exit; + } + /* Read all extents from the $DATA attribute in $MFT. */ + ntfs_attr_reinit_search_ctx(ctx); + last_vcn = vol->mft_na->allocated_size >> vol->cluster_size_bits; + highest_vcn = next_vcn = 0; + a = NULL; + while (!ntfs_attr_lookup(AT_DATA, AT_UNNAMED, 0, 0, next_vcn, NULL, 0, + ctx)) { + runlist_element *nrl; - /* We now have a fully setup ntfs inode for $MFT in vol->mft_ni. */ + a = ctx->attr; + /* $MFT must be non-resident. */ + if (!a->non_resident) { + ntfs_log_error("$MFT must be non-resident.\n"); + goto io_error_exit; + } + /* $MFT must be uncompressed and unencrypted. */ + if (a->flags & ATTR_COMPRESSION_MASK || + a->flags & ATTR_IS_ENCRYPTED) { + ntfs_log_error("$MFT must be uncompressed and " + "unencrypted.\n"); + goto io_error_exit; + } + /* + * Decompress the mapping pairs array of this extent and merge + * the result into the existing runlist. No need for locking + * as we have exclusive access to the inode at this time and we + * are a mount in progress task, too. + */ + nrl = ntfs_mapping_pairs_decompress(vol, a, vol->mft_na->rl); + if (!nrl) { + ntfs_log_perror("ntfs_mapping_pairs_decompress() failed"); + goto error_exit; + } + vol->mft_na->rl = nrl; - /* Get an ntfs attribute for $MFT/$DATA and set it up, too. */ - vol->mft_na = ntfs_attr_open( vol->mft_ni, AT_DATA, AT_UNNAMED, 0 ); - if ( !vol->mft_na ) - { - ntfs_log_perror( "Failed to open ntfs attribute" ); - goto error_exit; - } - /* Read all extents from the $DATA attribute in $MFT. */ - ntfs_attr_reinit_search_ctx( ctx ); - last_vcn = vol->mft_na->allocated_size >> vol->cluster_size_bits; - highest_vcn = next_vcn = 0; - a = NULL; - while ( !ntfs_attr_lookup( AT_DATA, AT_UNNAMED, 0, 0, next_vcn, NULL, 0, - ctx ) ) - { - runlist_element *nrl; + /* Get the lowest vcn for the next extent. */ + highest_vcn = sle64_to_cpu(a->highest_vcn); + next_vcn = highest_vcn + 1; - a = ctx->attr; - /* $MFT must be non-resident. */ - if ( !a->non_resident ) - { - ntfs_log_error( "$MFT must be non-resident.\n" ); - goto io_error_exit; - } - /* $MFT must be uncompressed and unencrypted. */ - if ( a->flags & ATTR_COMPRESSION_MASK || - a->flags & ATTR_IS_ENCRYPTED ) - { - ntfs_log_error( "$MFT must be uncompressed and " - "unencrypted.\n" ); - goto io_error_exit; - } - /* - * Decompress the mapping pairs array of this extent and merge - * the result into the existing runlist. No need for locking - * as we have exclusive access to the inode at this time and we - * are a mount in progress task, too. - */ - nrl = ntfs_mapping_pairs_decompress( vol, a, vol->mft_na->rl ); - if ( !nrl ) - { - ntfs_log_perror( "ntfs_mapping_pairs_decompress() failed" ); - goto error_exit; - } - vol->mft_na->rl = nrl; + /* Only one extent or error, which we catch below. */ + if (next_vcn <= 0) + break; - /* 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 ) - break; - - /* Avoid endless loops due to corruption. */ - if ( next_vcn < sle64_to_cpu( a->lowest_vcn ) ) - { - ntfs_log_error( "$MFT has corrupt attribute list.\n" ); - goto io_error_exit; - } - } - if ( !a ) - { - ntfs_log_error( "$MFT/$DATA attribute not found.\n" ); - goto io_error_exit; - } - if ( highest_vcn && highest_vcn != last_vcn - 1 ) - { - ntfs_log_error( "Failed to load runlist for $MFT/$DATA.\n" ); - ntfs_log_error( "highest_vcn = 0x%llx, last_vcn - 1 = 0x%llx\n", - ( long long )highest_vcn, ( long long )last_vcn - 1 ); - goto io_error_exit; - } - /* Done with the $Mft mft record. */ - ntfs_attr_put_search_ctx( ctx ); - ctx = NULL; - /* - * The volume is now setup so we can use all read access functions. - */ - vol->mftbmp_na = ntfs_attr_open( vol->mft_ni, AT_BITMAP, AT_UNNAMED, 0 ); - if ( !vol->mftbmp_na ) - { - ntfs_log_perror( "Failed to open $MFT/$BITMAP" ); - goto error_exit; - } - return 0; + /* Avoid endless loops due to corruption. */ + if (next_vcn < sle64_to_cpu(a->lowest_vcn)) { + ntfs_log_error("$MFT has corrupt attribute list.\n"); + goto io_error_exit; + } + } + if (!a) { + ntfs_log_error("$MFT/$DATA attribute not found.\n"); + goto io_error_exit; + } + if (highest_vcn && highest_vcn != last_vcn - 1) { + ntfs_log_error("Failed to load runlist for $MFT/$DATA.\n"); + ntfs_log_error("highest_vcn = 0x%llx, last_vcn - 1 = 0x%llx\n", + (long long)highest_vcn, (long long)last_vcn - 1); + goto io_error_exit; + } + /* Done with the $Mft mft record. */ + ntfs_attr_put_search_ctx(ctx); + ctx = NULL; + /* + * The volume is now setup so we can use all read access functions. + */ + vol->mftbmp_na = ntfs_attr_open(vol->mft_ni, AT_BITMAP, AT_UNNAMED, 0); + if (!vol->mftbmp_na) { + ntfs_log_perror("Failed to open $MFT/$BITMAP"); + goto error_exit; + } + return 0; io_error_exit: - errno = EIO; + errno = EIO; error_exit: - eo = errno; - if ( ctx ) - ntfs_attr_put_search_ctx( ctx ); - if ( vol->mft_na ) - { - ntfs_attr_close( vol->mft_na ); - vol->mft_na = NULL; - } - if ( vol->mft_ni ) - { - ntfs_inode_close( vol->mft_ni ); - vol->mft_ni = NULL; - } - errno = eo; - return -1; + eo = errno; + if (ctx) + ntfs_attr_put_search_ctx(ctx); + if (vol->mft_na) { + ntfs_attr_close(vol->mft_na); + vol->mft_na = NULL; + } + if (vol->mft_ni) { + ntfs_inode_close(vol->mft_ni); + vol->mft_ni = NULL; + } + errno = eo; + return -1; } /** * ntfs_mftmirr_load - load the $MFTMirr and setup the ntfs volume with it - * @vol: ntfs volume whose $MFTMirr to load + * @vol: ntfs volume whose $MFTMirr to load * * Load $MFTMirr from @vol and setup @vol with it. After calling this function * the volume @vol is ready for use by all write access functions provided by @@ -433,49 +411,45 @@ error_exit: * * Return 0 on success and -1 on error with errno set to the error code. */ -static int ntfs_mftmirr_load( ntfs_volume *vol ) +static int ntfs_mftmirr_load(ntfs_volume *vol) { - int err; + int err; - vol->mftmirr_ni = ntfs_inode_open( vol, FILE_MFTMirr ); - if ( !vol->mftmirr_ni ) - { - ntfs_log_perror( "Failed to open inode $MFTMirr" ); - return -1; - } - - vol->mftmirr_na = ntfs_attr_open( vol->mftmirr_ni, AT_DATA, AT_UNNAMED, 0 ); - if ( !vol->mftmirr_na ) - { - ntfs_log_perror( "Failed to open $MFTMirr/$DATA" ); - goto error_exit; - } - - if ( ntfs_attr_map_runlist( vol->mftmirr_na, 0 ) < 0 ) - { - ntfs_log_perror( "Failed to map runlist of $MFTMirr/$DATA" ); - goto error_exit; - } - - return 0; + vol->mftmirr_ni = ntfs_inode_open(vol, FILE_MFTMirr); + if (!vol->mftmirr_ni) { + ntfs_log_perror("Failed to open inode $MFTMirr"); + return -1; + } + + vol->mftmirr_na = ntfs_attr_open(vol->mftmirr_ni, AT_DATA, AT_UNNAMED, 0); + if (!vol->mftmirr_na) { + ntfs_log_perror("Failed to open $MFTMirr/$DATA"); + goto error_exit; + } + + if (ntfs_attr_map_runlist(vol->mftmirr_na, 0) < 0) { + ntfs_log_perror("Failed to map runlist of $MFTMirr/$DATA"); + goto error_exit; + } + + return 0; error_exit: - err = errno; - if ( vol->mftmirr_na ) - { - ntfs_attr_close( vol->mftmirr_na ); - vol->mftmirr_na = NULL; - } - ntfs_inode_close( vol->mftmirr_ni ); - vol->mftmirr_ni = NULL; - errno = err; - return -1; + err = errno; + if (vol->mftmirr_na) { + ntfs_attr_close(vol->mftmirr_na); + vol->mftmirr_na = NULL; + } + ntfs_inode_close(vol->mftmirr_ni); + vol->mftmirr_ni = NULL; + errno = err; + return -1; } /** * ntfs_volume_startup - allocate and setup an ntfs volume - * @dev: device to open - * @flags: optional mount flags + * @dev: device to open + * @flags: optional mount flags * * Load, verify, and parse bootsector; load and setup $MFT and $MFTMirr. After * calling this function, the volume is setup sufficiently to call all read @@ -484,206 +458,196 @@ error_exit: * Return the allocated volume structure on success and NULL on error with * errno set to the error code. */ -ntfs_volume *ntfs_volume_startup( struct ntfs_device *dev, unsigned long flags ) +ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, unsigned long flags) { - LCN mft_zone_size, mft_lcn; - s64 br; - ntfs_volume *vol; - NTFS_BOOT_SECTOR *bs; - int eo; + LCN mft_zone_size, mft_lcn; + s64 br; + ntfs_volume *vol; + NTFS_BOOT_SECTOR *bs; + int eo; - if ( !dev || !dev->d_ops || !dev->d_name ) - { - errno = EINVAL; - ntfs_log_perror( "%s: dev = %p", __FUNCTION__, dev ); - return NULL; - } + if (!dev || !dev->d_ops || !dev->d_name) { + errno = EINVAL; + ntfs_log_perror("%s: dev = %p", __FUNCTION__, dev); + return NULL; + } - bs = ntfs_malloc( sizeof( NTFS_BOOT_SECTOR ) ); - if ( !bs ) - return NULL; + bs = ntfs_malloc(sizeof(NTFS_BOOT_SECTOR)); + if (!bs) + return NULL; + + /* Allocate the volume structure. */ + vol = ntfs_volume_alloc(); + if (!vol) + goto error_exit; + + /* Create the default upcase table. */ + vol->upcase_len = 65536; + vol->upcase = ntfs_malloc(vol->upcase_len * sizeof(ntfschar)); + if (!vol->upcase) + goto error_exit; + + ntfs_upcase_table_build(vol->upcase, + vol->upcase_len * sizeof(ntfschar)); + /* Default with no locase table and case sensitive file names */ + vol->locase = (ntfschar*)NULL; + NVolSetCaseSensitive(vol); + + /* by default, all files are shown and not marked hidden */ + NVolSetShowSysFiles(vol); + NVolSetShowHidFiles(vol); + NVolClearHideDotFiles(vol); + if (flags & MS_RDONLY) + NVolSetReadOnly(vol); + + /* ...->open needs bracketing to compile with glibc 2.7 */ + if ((dev->d_ops->open)(dev, NVolReadOnly(vol) ? O_RDONLY: O_RDWR)) { + ntfs_log_perror("Error opening '%s'", dev->d_name); + goto error_exit; + } + /* Attach the device to the volume. */ + vol->dev = dev; + + /* Now read the bootsector. */ + br = ntfs_pread(dev, 0, sizeof(NTFS_BOOT_SECTOR), bs); + if (br != sizeof(NTFS_BOOT_SECTOR)) { + if (br != -1) + errno = EINVAL; + if (!br) + ntfs_log_error("Failed to read bootsector (size=0)\n"); + else + ntfs_log_perror("Error reading bootsector"); + goto error_exit; + } + if (!ntfs_boot_sector_is_ntfs(bs)) { + errno = EINVAL; + goto error_exit; + } + if (ntfs_boot_sector_parse(vol, bs) < 0) + goto error_exit; + + free(bs); + bs = NULL; + /* Now set the device block size to the sector size. */ + if (ntfs_device_block_size_set(vol->dev, vol->sector_size)) + ntfs_log_debug("Failed to set the device block size to the " + "sector size. This may affect performance " + "but should be harmless otherwise. Error: " + "%s\n", strerror(errno)); + + /* We now initialize the cluster allocator. */ + vol->full_zones = 0; + mft_zone_size = vol->nr_clusters >> 3; /* 12.5% */ - /* Allocate the volume structure. */ - vol = ntfs_volume_alloc(); - if ( !vol ) - goto error_exit; + /* Setup the mft zone. */ + vol->mft_zone_start = vol->mft_zone_pos = vol->mft_lcn; + ntfs_log_debug("mft_zone_pos = 0x%llx\n", (long long)vol->mft_zone_pos); - /* Create the default upcase table. */ - vol->upcase_len = 65536; - vol->upcase = ntfs_malloc( vol->upcase_len * sizeof( ntfschar ) ); - if ( !vol->upcase ) - goto error_exit; + /* + * Calculate the mft_lcn for an unmodified NTFS volume (see mkntfs + * source) and if the actual mft_lcn is in the expected place or even + * further to the front of the volume, extend the mft_zone to cover the + * beginning of the volume as well. This is in order to protect the + * area reserved for the mft bitmap as well within the mft_zone itself. + * On non-standard volumes we don't protect it as the overhead would be + * higher than the speed increase we would get by doing it. + */ + mft_lcn = (8192 + 2 * vol->cluster_size - 1) / vol->cluster_size; + if (mft_lcn * vol->cluster_size < 16 * 1024) + mft_lcn = (16 * 1024 + vol->cluster_size - 1) / + vol->cluster_size; + if (vol->mft_zone_start <= mft_lcn) + vol->mft_zone_start = 0; + ntfs_log_debug("mft_zone_start = 0x%llx\n", (long long)vol->mft_zone_start); - ntfs_upcase_table_build( vol->upcase, - vol->upcase_len * sizeof( ntfschar ) ); - /* Default with no locase table and case sensitive file names */ - vol->locase = ( ntfschar* )NULL; - NVolSetCaseSensitive( vol ); + /* + * Need to cap the mft zone on non-standard volumes so that it does + * not point outside the boundaries of the volume. We do this by + * halving the zone size until we are inside the volume. + */ + vol->mft_zone_end = vol->mft_lcn + mft_zone_size; + while (vol->mft_zone_end >= vol->nr_clusters) { + mft_zone_size >>= 1; + vol->mft_zone_end = vol->mft_lcn + mft_zone_size; + } + ntfs_log_debug("mft_zone_end = 0x%llx\n", (long long)vol->mft_zone_end); - /* by default, all files are shown and not marked hidden */ - NVolSetShowSysFiles( vol ); - NVolSetShowHidFiles( vol ); - NVolClearHideDotFiles( vol ); - if ( flags & MS_RDONLY ) - NVolSetReadOnly( vol ); + /* + * Set the current position within each data zone to the start of the + * respective zone. + */ + vol->data1_zone_pos = vol->mft_zone_end; + ntfs_log_debug("data1_zone_pos = %lld\n", (long long)vol->data1_zone_pos); + vol->data2_zone_pos = 0; + ntfs_log_debug("data2_zone_pos = %lld\n", (long long)vol->data2_zone_pos); - /* ...->open needs bracketing to compile with glibc 2.7 */ - if ( ( dev->d_ops->open )( dev, NVolReadOnly( vol ) ? O_RDONLY : O_RDWR ) ) - { - ntfs_log_perror( "Error opening '%s'", dev->d_name ); - goto error_exit; - } - /* Attach the device to the volume. */ - vol->dev = dev; + /* Set the mft data allocation position to mft record 24. */ + vol->mft_data_pos = 24; - /* Now read the bootsector. */ - br = ntfs_pread( dev, 0, sizeof( NTFS_BOOT_SECTOR ), bs ); - if ( br != sizeof( NTFS_BOOT_SECTOR ) ) - { - if ( br != -1 ) - errno = EINVAL; - if ( !br ) - ntfs_log_error( "Failed to read bootsector (size=0)\n" ); - else - ntfs_log_perror( "Error reading bootsector" ); - goto error_exit; - } - if ( !ntfs_boot_sector_is_ntfs( bs ) ) - { - errno = EINVAL; - goto error_exit; - } - if ( ntfs_boot_sector_parse( vol, bs ) < 0 ) - goto error_exit; + /* + * The cluster allocator is now fully operational. + */ - free( bs ); - bs = NULL; - /* Now set the device block size to the sector size. */ - if ( ntfs_device_block_size_set( vol->dev, vol->sector_size ) ) - ntfs_log_debug( "Failed to set the device block size to the " - "sector size. This may affect performance " - "but should be harmless otherwise. Error: " - "%s\n", strerror( errno ) ); + /* Need to setup $MFT so we can use the library read functions. */ + if (ntfs_mft_load(vol) < 0) { + ntfs_log_perror("Failed to load $MFT"); + goto error_exit; + } - /* We now initialize the cluster allocator. */ - vol->full_zones = 0; - mft_zone_size = vol->nr_clusters >> 3; /* 12.5% */ - - /* Setup the mft zone. */ - vol->mft_zone_start = vol->mft_zone_pos = vol->mft_lcn; - ntfs_log_debug( "mft_zone_pos = 0x%llx\n", ( long long )vol->mft_zone_pos ); - - /* - * Calculate the mft_lcn for an unmodified NTFS volume (see mkntfs - * source) and if the actual mft_lcn is in the expected place or even - * further to the front of the volume, extend the mft_zone to cover the - * beginning of the volume as well. This is in order to protect the - * area reserved for the mft bitmap as well within the mft_zone itself. - * On non-standard volumes we don't protect it as the overhead would be - * higher than the speed increase we would get by doing it. - */ - mft_lcn = ( 8192 + 2 * vol->cluster_size - 1 ) / vol->cluster_size; - if ( mft_lcn * vol->cluster_size < 16 * 1024 ) - mft_lcn = ( 16 * 1024 + vol->cluster_size - 1 ) / - vol->cluster_size; - if ( vol->mft_zone_start <= mft_lcn ) - vol->mft_zone_start = 0; - ntfs_log_debug( "mft_zone_start = 0x%llx\n", ( long long )vol->mft_zone_start ); - - /* - * Need to cap the mft zone on non-standard volumes so that it does - * not point outside the boundaries of the volume. We do this by - * halving the zone size until we are inside the volume. - */ - vol->mft_zone_end = vol->mft_lcn + mft_zone_size; - while ( vol->mft_zone_end >= vol->nr_clusters ) - { - mft_zone_size >>= 1; - vol->mft_zone_end = vol->mft_lcn + mft_zone_size; - } - ntfs_log_debug( "mft_zone_end = 0x%llx\n", ( long long )vol->mft_zone_end ); - - /* - * Set the current position within each data zone to the start of the - * respective zone. - */ - vol->data1_zone_pos = vol->mft_zone_end; - ntfs_log_debug( "data1_zone_pos = %lld\n", ( long long )vol->data1_zone_pos ); - vol->data2_zone_pos = 0; - ntfs_log_debug( "data2_zone_pos = %lld\n", ( long long )vol->data2_zone_pos ); - - /* Set the mft data allocation position to mft record 24. */ - vol->mft_data_pos = 24; - - /* - * The cluster allocator is now fully operational. - */ - - /* Need to setup $MFT so we can use the library read functions. */ - if ( ntfs_mft_load( vol ) < 0 ) - { - ntfs_log_perror( "Failed to load $MFT" ); - goto error_exit; - } - - /* Need to setup $MFTMirr so we can use the write functions, too. */ - if ( ntfs_mftmirr_load( vol ) < 0 ) - { - ntfs_log_perror( "Failed to load $MFTMirr" ); - goto error_exit; - } - return vol; + /* Need to setup $MFTMirr so we can use the write functions, too. */ + if (ntfs_mftmirr_load(vol) < 0) { + ntfs_log_perror("Failed to load $MFTMirr"); + goto error_exit; + } + return vol; error_exit: - eo = errno; - free( bs ); - if ( vol ) - __ntfs_volume_release( vol ); - errno = eo; - return NULL; + eo = errno; + free(bs); + if (vol) + __ntfs_volume_release(vol); + errno = eo; + return NULL; } /** * ntfs_volume_check_logfile - check logfile on target volume - * @vol: volume on which to check logfile + * @vol: volume on which to check logfile * * Return 0 on success and -1 on error with errno set error code. */ -static int ntfs_volume_check_logfile( ntfs_volume *vol ) +static int ntfs_volume_check_logfile(ntfs_volume *vol) { - ntfs_inode *ni; - ntfs_attr *na = NULL; - RESTART_PAGE_HEADER *rp = NULL; - int err = 0; + ntfs_inode *ni; + ntfs_attr *na = NULL; + RESTART_PAGE_HEADER *rp = NULL; + int err = 0; - ni = ntfs_inode_open( vol, FILE_LogFile ); - if ( !ni ) - { - ntfs_log_perror( "Failed to open inode FILE_LogFile" ); - errno = EIO; - return -1; - } - - na = ntfs_attr_open( ni, AT_DATA, AT_UNNAMED, 0 ); - if ( !na ) - { - ntfs_log_perror( "Failed to open $FILE_LogFile/$DATA" ); - err = EIO; - goto out; - } - - if ( !ntfs_check_logfile( na, &rp ) || !ntfs_is_logfile_clean( na, rp ) ) - err = EOPNOTSUPP; - free( rp ); - ntfs_attr_close( na ); -out: - if ( ntfs_inode_close( ni ) ) - ntfs_error_set( &err ); - if ( err ) - { - errno = err; - return -1; - } - return 0; + ni = ntfs_inode_open(vol, FILE_LogFile); + if (!ni) { + ntfs_log_perror("Failed to open inode FILE_LogFile"); + errno = EIO; + return -1; + } + + na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); + if (!na) { + ntfs_log_perror("Failed to open $FILE_LogFile/$DATA"); + err = EIO; + goto out; + } + + if (!ntfs_check_logfile(na, &rp) || !ntfs_is_logfile_clean(na, rp)) + err = EOPNOTSUPP; + free(rp); + ntfs_attr_close(na); +out: + if (ntfs_inode_close(ni)) + ntfs_error_set(&err); + if (err) { + errno = err; + return -1; + } + return 0; } /** @@ -691,63 +655,57 @@ out: * @vol: An ntfs volume obtained from ntfs_mount * * Return: inode Success, hiberfil.sys is valid - * NULL hiberfil.sys doesn't exist or some other error occurred + * NULL hiberfil.sys doesn't exist or some other error occurred */ -static ntfs_inode *ntfs_hiberfile_open( ntfs_volume *vol ) +static ntfs_inode *ntfs_hiberfile_open(ntfs_volume *vol) { - u64 inode; - ntfs_inode *ni_root; - ntfs_inode *ni_hibr = NULL; - ntfschar *unicode = NULL; - int unicode_len; - const char *hiberfile = "hiberfil.sys"; + u64 inode; + ntfs_inode *ni_root; + ntfs_inode *ni_hibr = NULL; + ntfschar *unicode = NULL; + int unicode_len; + const char *hiberfile = "hiberfil.sys"; - if ( !vol ) - { - errno = EINVAL; - return NULL; - } + if (!vol) { + errno = EINVAL; + return NULL; + } - ni_root = ntfs_inode_open( vol, FILE_root ); - if ( !ni_root ) - { - ntfs_log_debug( "Couldn't open the root directory.\n" ); - return NULL; - } + ni_root = ntfs_inode_open(vol, FILE_root); + if (!ni_root) { + ntfs_log_debug("Couldn't open the root directory.\n"); + return NULL; + } - unicode_len = ntfs_mbstoucs( hiberfile, &unicode ); - if ( unicode_len < 0 ) - { - ntfs_log_perror( "Couldn't convert 'hiberfil.sys' to Unicode" ); - goto out; - } + unicode_len = ntfs_mbstoucs(hiberfile, &unicode); + if (unicode_len < 0) { + ntfs_log_perror("Couldn't convert 'hiberfil.sys' to Unicode"); + goto out; + } - inode = ntfs_inode_lookup_by_name( ni_root, unicode, unicode_len ); - if ( inode == ( u64 ) - 1 ) - { - ntfs_log_debug( "Couldn't find file '%s'.\n", hiberfile ); - goto out; - } + inode = ntfs_inode_lookup_by_name(ni_root, unicode, unicode_len); + if (inode == (u64)-1) { + ntfs_log_debug("Couldn't find file '%s'.\n", hiberfile); + goto out; + } - inode = MREF( inode ); - ni_hibr = ntfs_inode_open( vol, inode ); - if ( !ni_hibr ) - { - ntfs_log_debug( "Couldn't open inode %lld.\n", ( long long )inode ); - goto out; - } + inode = MREF(inode); + ni_hibr = ntfs_inode_open(vol, inode); + if (!ni_hibr) { + ntfs_log_debug("Couldn't open inode %lld.\n", (long long)inode); + goto out; + } out: - if ( ntfs_inode_close( ni_root ) ) - { - ntfs_inode_close( ni_hibr ); - ni_hibr = NULL; - } - free( unicode ); - return ni_hibr; + if (ntfs_inode_close(ni_root)) { + ntfs_inode_close(ni_hibr); + ni_hibr = NULL; + } + free(unicode); + return ni_hibr; } -#define NTFS_HIBERFILE_HEADER_SIZE 4096 +#define NTFS_HIBERFILE_HEADER_SIZE 4096 /** * ntfs_volume_check_hiberfile - check hiberfil.sys whether Windows is @@ -757,141 +715,129 @@ out: * Return: 0 if Windows isn't hibernated for sure * -1 otherwise and errno is set to the appropriate value */ -int ntfs_volume_check_hiberfile( ntfs_volume *vol, int verbose ) +int ntfs_volume_check_hiberfile(ntfs_volume *vol, int verbose) { - ntfs_inode *ni; - ntfs_attr *na = NULL; - int bytes_read, err; - char *buf = NULL; + ntfs_inode *ni; + ntfs_attr *na = NULL; + int bytes_read, err; + char *buf = NULL; - ni = ntfs_hiberfile_open( vol ); - if ( !ni ) - { - if ( errno == ENOENT ) - return 0; - return -1; - } + ni = ntfs_hiberfile_open(vol); + if (!ni) { + if (errno == ENOENT) + return 0; + return -1; + } - buf = ntfs_malloc( NTFS_HIBERFILE_HEADER_SIZE ); - if ( !buf ) - goto out; + buf = ntfs_malloc(NTFS_HIBERFILE_HEADER_SIZE); + if (!buf) + goto out; - na = ntfs_attr_open( ni, AT_DATA, AT_UNNAMED, 0 ); - if ( !na ) - { - ntfs_log_perror( "Failed to open hiberfil.sys data attribute" ); - goto out; - } + na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); + if (!na) { + ntfs_log_perror("Failed to open hiberfil.sys data attribute"); + goto out; + } - bytes_read = ntfs_attr_pread( na, 0, NTFS_HIBERFILE_HEADER_SIZE, buf ); - if ( bytes_read == -1 ) - { - ntfs_log_perror( "Failed to read hiberfil.sys" ); - goto out; - } - if ( bytes_read < NTFS_HIBERFILE_HEADER_SIZE ) - { - if ( verbose ) - ntfs_log_error( "Hibernated non-system partition, " - "refused to mount.\n" ); - errno = EPERM; - goto out; - } - if ( memcmp( buf, "hibr", 4 ) == 0 ) - { - if ( verbose ) - ntfs_log_error( "Windows is hibernated, refused to mount.\n" ); - errno = EPERM; - goto out; - } - /* All right, all header bytes are zero */ - errno = 0; + bytes_read = ntfs_attr_pread(na, 0, NTFS_HIBERFILE_HEADER_SIZE, buf); + if (bytes_read == -1) { + ntfs_log_perror("Failed to read hiberfil.sys"); + goto out; + } + if (bytes_read < NTFS_HIBERFILE_HEADER_SIZE) { + if (verbose) + ntfs_log_error("Hibernated non-system partition, " + "refused to mount.\n"); + errno = EPERM; + goto out; + } + if (memcmp(buf, "hibr", 4) == 0) { + if (verbose) + ntfs_log_error("Windows is hibernated, refused to mount.\n"); + errno = EPERM; + goto out; + } + /* All right, all header bytes are zero */ + errno = 0; out: - if ( na ) - ntfs_attr_close( na ); - free( buf ); - err = errno; - if ( ntfs_inode_close( ni ) ) - ntfs_error_set( &err ); - errno = err; - return errno ? -1 : 0; + if (na) + ntfs_attr_close(na); + free(buf); + err = errno; + if (ntfs_inode_close(ni)) + ntfs_error_set(&err); + errno = err; + return errno ? -1 : 0; } /* - * Make sure a LOGGED_UTILITY_STREAM attribute named "$TXF_DATA" - * on the root directory is resident. - * When it is non-resident, the partition cannot be mounted on Vista - * (see http://support.microsoft.com/kb/974729) + * Make sure a LOGGED_UTILITY_STREAM attribute named "$TXF_DATA" + * on the root directory is resident. + * When it is non-resident, the partition cannot be mounted on Vista + * (see http://support.microsoft.com/kb/974729) * - * We take care to avoid this situation, however this can be a - * consequence of having used an older version (including older - * Windows version), so we had better fix it. + * We take care to avoid this situation, however this can be a + * consequence of having used an older version (including older + * Windows version), so we had better fix it. * - * Returns 0 if unneeded or successful - * -1 if there was an error, explained by errno + * Returns 0 if unneeded or successful + * -1 if there was an error, explained by errno */ -static int fix_txf_data( ntfs_volume *vol ) +static int fix_txf_data(ntfs_volume *vol) { - void *txf_data; - s64 txf_data_size; - ntfs_inode *ni; - ntfs_attr *na; - int res; + void *txf_data; + s64 txf_data_size; + ntfs_inode *ni; + ntfs_attr *na; + int res; - res = 0; - ntfs_log_debug( "Loading root directory\n" ); - ni = ntfs_inode_open( vol, FILE_root ); - if ( !ni ) - { - ntfs_log_perror( "Failed to open root directory" ); - res = -1; - } - else - { - /* Get the $TXF_DATA attribute */ - na = ntfs_attr_open( ni, AT_LOGGED_UTILITY_STREAM, TXF_DATA, 9 ); - if ( na ) - { - if ( NAttrNonResident( na ) ) - { - /* - * Fix the attribute by truncating, then - * rewriting it. - */ - ntfs_log_debug( "Making $TXF_DATA resident\n" ); - txf_data = ntfs_attr_readall( ni, - AT_LOGGED_UTILITY_STREAM, - TXF_DATA, 9, &txf_data_size ); - if ( txf_data ) - { - if ( ntfs_attr_truncate( na, 0 ) - || ( ntfs_attr_pwrite( na, 0, - txf_data_size, txf_data ) - != txf_data_size ) ) - res = -1; - free( txf_data ); - } - if ( res ) - ntfs_log_error( "Failed to make $TXF_DATA resident\n" ); - else - ntfs_log_error( "$TXF_DATA made resident\n" ); - } - ntfs_attr_close( na ); - } - if ( ntfs_inode_close( ni ) ) - { - ntfs_log_perror( "Failed to close root" ); - res = -1; - } - } - return ( res ); + res = 0; + ntfs_log_debug("Loading root directory\n"); + ni = ntfs_inode_open(vol, FILE_root); + if (!ni) { + ntfs_log_perror("Failed to open root directory"); + res = -1; + } else { + /* Get the $TXF_DATA attribute */ + na = ntfs_attr_open(ni, AT_LOGGED_UTILITY_STREAM, TXF_DATA, 9); + if (na) { + if (NAttrNonResident(na)) { + /* + * Fix the attribute by truncating, then + * rewriting it. + */ + ntfs_log_debug("Making $TXF_DATA resident\n"); + txf_data = ntfs_attr_readall(ni, + AT_LOGGED_UTILITY_STREAM, + TXF_DATA, 9, &txf_data_size); + if (txf_data) { + if (ntfs_attr_truncate(na, 0) + || (ntfs_attr_pwrite(na, 0, + txf_data_size, txf_data) + != txf_data_size)) + res = -1; + free(txf_data); + } + if (res) + ntfs_log_error("Failed to make $TXF_DATA resident\n"); + else + ntfs_log_error("$TXF_DATA made resident\n"); + } + ntfs_attr_close(na); + } + if (ntfs_inode_close(ni)) { + ntfs_log_perror("Failed to close root"); + res = -1; + } + } + return (res); } /** * ntfs_device_mount - open ntfs volume - * @dev: device to open - * @flags: optional mount flags + * @dev: device to open + * @flags: optional mount flags * * This function mounts an ntfs volume. @dev should describe the device which * to mount as the ntfs volume. @@ -899,7 +845,7 @@ static int fix_txf_data( ntfs_volume *vol ) * @flags is an optional second parameter. The same flags are used as for * the mount system call (man 2 mount). Currently only the following flag * is implemented: - * MS_RDONLY - mount volume read-only + * MS_RDONLY - mount volume read-only * * The function opens the device @dev and verifies that it contains a valid * bootsector. Then, it allocates an ntfs_volume structure and initializes @@ -910,452 +856,409 @@ static int fix_txf_data( ntfs_volume *vol ) * Return the allocated volume structure on success and NULL on error with * errno set to the error code. */ -ntfs_volume *ntfs_device_mount( struct ntfs_device *dev, unsigned long flags ) +ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) { - s64 l; - ntfs_volume *vol; - u8 *m = NULL, *m2 = NULL; - ntfs_attr_search_ctx *ctx = NULL; - ntfs_inode *ni; - ntfs_attr *na; - ATTR_RECORD *a; - VOLUME_INFORMATION *vinf; - ntfschar *vname; - int i, j, eo; - u32 u; + s64 l; + ntfs_volume *vol; + u8 *m = NULL, *m2 = NULL; + ntfs_attr_search_ctx *ctx = NULL; + ntfs_inode *ni; + ntfs_attr *na; + ATTR_RECORD *a; + VOLUME_INFORMATION *vinf; + ntfschar *vname; + int i, j, eo; + u32 u; - vol = ntfs_volume_startup( dev, flags ); - if ( !vol ) - return NULL; + vol = ntfs_volume_startup(dev, flags); + if (!vol) + return NULL; - /* Load data from $MFT and $MFTMirr and compare the contents. */ - m = ntfs_malloc( vol->mftmirr_size << vol->mft_record_size_bits ); - m2 = ntfs_malloc( vol->mftmirr_size << vol->mft_record_size_bits ); - if ( !m || !m2 ) - goto error_exit; + /* Load data from $MFT and $MFTMirr and compare the contents. */ + m = ntfs_malloc(vol->mftmirr_size << vol->mft_record_size_bits); + m2 = ntfs_malloc(vol->mftmirr_size << vol->mft_record_size_bits); + if (!m || !m2) + goto error_exit; - l = ntfs_attr_mst_pread( vol->mft_na, 0, vol->mftmirr_size, - vol->mft_record_size, m ); - if ( l != vol->mftmirr_size ) - { - if ( l == -1 ) - ntfs_log_perror( "Failed to read $MFT" ); - else - { - ntfs_log_error( "Failed to read $MFT, unexpected length " - "(%lld != %d).\n", ( long long )l, - vol->mftmirr_size ); - errno = EIO; - } - goto error_exit; - } - l = ntfs_attr_mst_pread( vol->mftmirr_na, 0, vol->mftmirr_size, - vol->mft_record_size, m2 ); - if ( l != vol->mftmirr_size ) - { - if ( l == -1 ) - { - ntfs_log_perror( "Failed to read $MFTMirr" ); - goto error_exit; - } - vol->mftmirr_size = l; - } - ntfs_log_debug( "Comparing $MFTMirr to $MFT...\n" ); - for ( i = 0; i < vol->mftmirr_size; ++i ) - { - MFT_RECORD *mrec, *mrec2; - const char *ESTR[12] = { "$MFT", "$MFTMirr", "$LogFile", - "$Volume", "$AttrDef", "root directory", "$Bitmap", - "$Boot", "$BadClus", "$Secure", "$UpCase", "$Extend" - }; - const char *s; + l = ntfs_attr_mst_pread(vol->mft_na, 0, vol->mftmirr_size, + vol->mft_record_size, m); + if (l != vol->mftmirr_size) { + if (l == -1) + ntfs_log_perror("Failed to read $MFT"); + else { + ntfs_log_error("Failed to read $MFT, unexpected length " + "(%lld != %d).\n", (long long)l, + vol->mftmirr_size); + errno = EIO; + } + goto error_exit; + } + l = ntfs_attr_mst_pread(vol->mftmirr_na, 0, vol->mftmirr_size, + vol->mft_record_size, m2); + if (l != vol->mftmirr_size) { + if (l == -1) { + ntfs_log_perror("Failed to read $MFTMirr"); + goto error_exit; + } + vol->mftmirr_size = l; + } + ntfs_log_debug("Comparing $MFTMirr to $MFT...\n"); + for (i = 0; i < vol->mftmirr_size; ++i) { + MFT_RECORD *mrec, *mrec2; + const char *ESTR[12] = { "$MFT", "$MFTMirr", "$LogFile", + "$Volume", "$AttrDef", "root directory", "$Bitmap", + "$Boot", "$BadClus", "$Secure", "$UpCase", "$Extend" }; + const char *s; - if ( i < 12 ) - s = ESTR[i]; - else if ( i < 16 ) - s = "system file"; - else - s = "mft record"; + if (i < 12) + s = ESTR[i]; + else if (i < 16) + s = "system file"; + else + s = "mft record"; - mrec = ( MFT_RECORD* )( m + i * vol->mft_record_size ); - if ( mrec->flags & MFT_RECORD_IN_USE ) - { - if ( ntfs_is_baad_recordp( mrec ) ) - { - ntfs_log_error( "$MFT error: Incomplete multi " - "sector transfer detected in " - "'%s'.\n", s ); - goto io_error_exit; - } - if ( !ntfs_is_mft_recordp( mrec ) ) - { - ntfs_log_error( "$MFT error: Invalid mft " - "record for '%s'.\n", s ); - goto io_error_exit; - } - } - mrec2 = ( MFT_RECORD* )( m2 + i * vol->mft_record_size ); - if ( mrec2->flags & MFT_RECORD_IN_USE ) - { - if ( ntfs_is_baad_recordp( mrec2 ) ) - { - ntfs_log_error( "$MFTMirr error: Incomplete " - "multi sector transfer " - "detected in '%s'.\n", s ); - goto io_error_exit; - } - if ( !ntfs_is_mft_recordp( mrec2 ) ) - { - ntfs_log_error( "$MFTMirr error: Invalid mft " - "record for '%s'.\n", s ); - goto io_error_exit; - } - } - if ( memcmp( mrec, mrec2, ntfs_mft_record_get_data_size( mrec ) ) ) - { - ntfs_log_error( "$MFTMirr does not match $MFT (record " - "%d).\n", i ); - goto io_error_exit; - } - } + mrec = (MFT_RECORD*)(m + i * vol->mft_record_size); + if (mrec->flags & MFT_RECORD_IN_USE) { + if (ntfs_is_baad_recordp(mrec)) { + ntfs_log_error("$MFT error: Incomplete multi " + "sector transfer detected in " + "'%s'.\n", s); + goto io_error_exit; + } + if (!ntfs_is_mft_recordp(mrec)) { + ntfs_log_error("$MFT error: Invalid mft " + "record for '%s'.\n", s); + goto io_error_exit; + } + } + mrec2 = (MFT_RECORD*)(m2 + i * vol->mft_record_size); + if (mrec2->flags & MFT_RECORD_IN_USE) { + if (ntfs_is_baad_recordp(mrec2)) { + ntfs_log_error("$MFTMirr error: Incomplete " + "multi sector transfer " + "detected in '%s'.\n", s); + goto io_error_exit; + } + if (!ntfs_is_mft_recordp(mrec2)) { + ntfs_log_error("$MFTMirr error: Invalid mft " + "record for '%s'.\n", s); + goto io_error_exit; + } + } + if (memcmp(mrec, mrec2, ntfs_mft_record_get_data_size(mrec))) { + ntfs_log_error("$MFTMirr does not match $MFT (record " + "%d).\n", i); + goto io_error_exit; + } + } - free( m2 ); - free( m ); - m = m2 = NULL; + free(m2); + free(m); + m = m2 = NULL; - /* Now load the bitmap from $Bitmap. */ - ntfs_log_debug( "Loading $Bitmap...\n" ); - vol->lcnbmp_ni = ntfs_inode_open( vol, FILE_Bitmap ); - if ( !vol->lcnbmp_ni ) - { - ntfs_log_perror( "Failed to open inode FILE_Bitmap" ); - goto error_exit; - } + /* Now load the bitmap from $Bitmap. */ + ntfs_log_debug("Loading $Bitmap...\n"); + vol->lcnbmp_ni = ntfs_inode_open(vol, FILE_Bitmap); + if (!vol->lcnbmp_ni) { + ntfs_log_perror("Failed to open inode FILE_Bitmap"); + goto error_exit; + } + + vol->lcnbmp_na = ntfs_attr_open(vol->lcnbmp_ni, AT_DATA, AT_UNNAMED, 0); + if (!vol->lcnbmp_na) { + ntfs_log_perror("Failed to open ntfs attribute"); + goto error_exit; + } + + if (vol->lcnbmp_na->data_size > vol->lcnbmp_na->allocated_size) { + ntfs_log_error("Corrupt cluster map size (%lld > %lld)\n", + (long long)vol->lcnbmp_na->data_size, + (long long)vol->lcnbmp_na->allocated_size); + goto io_error_exit; + } - vol->lcnbmp_na = ntfs_attr_open( vol->lcnbmp_ni, AT_DATA, AT_UNNAMED, 0 ); - if ( !vol->lcnbmp_na ) - { - ntfs_log_perror( "Failed to open ntfs attribute" ); - goto error_exit; - } + /* Now load the upcase table from $UpCase. */ + ntfs_log_debug("Loading $UpCase...\n"); + ni = ntfs_inode_open(vol, FILE_UpCase); + if (!ni) { + ntfs_log_perror("Failed to open inode FILE_UpCase"); + goto error_exit; + } + /* Get an ntfs attribute for $UpCase/$DATA. */ + na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); + if (!na) { + ntfs_log_perror("Failed to open ntfs attribute"); + goto error_exit; + } + /* + * Note: Normally, the upcase table has a length equal to 65536 + * 2-byte Unicode characters but allow for different cases, so no + * checks done. Just check we don't overflow 32-bits worth of Unicode + * characters. + */ + if (na->data_size & ~0x1ffffffffULL) { + ntfs_log_error("Error: Upcase table is too big (max 32-bit " + "allowed).\n"); + errno = EINVAL; + goto error_exit; + } + if (vol->upcase_len != na->data_size >> 1) { + vol->upcase_len = na->data_size >> 1; + /* Throw away default table. */ + free(vol->upcase); + vol->upcase = ntfs_malloc(na->data_size); + if (!vol->upcase) + goto error_exit; + } + /* Read in the $DATA attribute value into the buffer. */ + l = ntfs_attr_pread(na, 0, na->data_size, vol->upcase); + if (l != na->data_size) { + ntfs_log_error("Failed to read $UpCase, unexpected length " + "(%lld != %lld).\n", (long long)l, + (long long)na->data_size); + errno = EIO; + goto error_exit; + } + /* Done with the $UpCase mft record. */ + ntfs_attr_close(na); + if (ntfs_inode_close(ni)) { + ntfs_log_perror("Failed to close $UpCase"); + goto error_exit; + } - if ( vol->lcnbmp_na->data_size > vol->lcnbmp_na->allocated_size ) - { - ntfs_log_error( "Corrupt cluster map size (%lld > %lld)\n", - ( long long )vol->lcnbmp_na->data_size, - ( long long )vol->lcnbmp_na->allocated_size ); - goto io_error_exit; - } + /* + * Now load $Volume and set the version information and flags in the + * vol structure accordingly. + */ + ntfs_log_debug("Loading $Volume...\n"); + vol->vol_ni = ntfs_inode_open(vol, FILE_Volume); + if (!vol->vol_ni) { + ntfs_log_perror("Failed to open inode FILE_Volume"); + goto error_exit; + } + /* Get a search context for the $Volume/$VOLUME_INFORMATION lookup. */ + ctx = ntfs_attr_get_search_ctx(vol->vol_ni, NULL); + if (!ctx) + goto error_exit; - /* Now load the upcase table from $UpCase. */ - ntfs_log_debug( "Loading $UpCase...\n" ); - ni = ntfs_inode_open( vol, FILE_UpCase ); - if ( !ni ) - { - ntfs_log_perror( "Failed to open inode FILE_UpCase" ); - goto error_exit; - } - /* Get an ntfs attribute for $UpCase/$DATA. */ - na = ntfs_attr_open( ni, AT_DATA, AT_UNNAMED, 0 ); - if ( !na ) - { - ntfs_log_perror( "Failed to open ntfs attribute" ); - goto error_exit; - } - /* - * Note: Normally, the upcase table has a length equal to 65536 - * 2-byte Unicode characters but allow for different cases, so no - * checks done. Just check we don't overflow 32-bits worth of Unicode - * characters. - */ - if ( na->data_size & ~0x1ffffffffULL ) - { - ntfs_log_error( "Error: Upcase table is too big (max 32-bit " - "allowed).\n" ); - errno = EINVAL; - goto error_exit; - } - if ( vol->upcase_len != na->data_size >> 1 ) - { - vol->upcase_len = na->data_size >> 1; - /* Throw away default table. */ - free( vol->upcase ); - vol->upcase = ntfs_malloc( na->data_size ); - if ( !vol->upcase ) - goto error_exit; - } - /* Read in the $DATA attribute value into the buffer. */ - l = ntfs_attr_pread( na, 0, na->data_size, vol->upcase ); - if ( l != na->data_size ) - { - ntfs_log_error( "Failed to read $UpCase, unexpected length " - "(%lld != %lld).\n", ( long long )l, - ( long long )na->data_size ); - errno = EIO; - goto error_exit; - } - /* Done with the $UpCase mft record. */ - ntfs_attr_close( na ); - if ( ntfs_inode_close( ni ) ) - { - ntfs_log_perror( "Failed to close $UpCase" ); - goto error_exit; - } + /* Find the $VOLUME_INFORMATION attribute. */ + if (ntfs_attr_lookup(AT_VOLUME_INFORMATION, AT_UNNAMED, 0, 0, 0, NULL, + 0, ctx)) { + ntfs_log_perror("$VOLUME_INFORMATION attribute not found in " + "$Volume"); + goto error_exit; + } + a = ctx->attr; + /* Has to be resident. */ + if (a->non_resident) { + ntfs_log_error("Attribute $VOLUME_INFORMATION must be " + "resident but it isn't.\n"); + errno = EIO; + goto error_exit; + } + /* Get a pointer to the value of the attribute. */ + vinf = (VOLUME_INFORMATION*)(le16_to_cpu(a->value_offset) + (char*)a); + /* Sanity checks. */ + if ((char*)vinf + le32_to_cpu(a->value_length) > (char*)ctx->mrec + + le32_to_cpu(ctx->mrec->bytes_in_use) || + le16_to_cpu(a->value_offset) + le32_to_cpu( + a->value_length) > le32_to_cpu(a->length)) { + ntfs_log_error("$VOLUME_INFORMATION in $Volume is corrupt.\n"); + errno = EIO; + goto error_exit; + } + /* Setup vol from the volume information attribute value. */ + vol->major_ver = vinf->major_ver; + vol->minor_ver = vinf->minor_ver; + /* Do not use le16_to_cpu() macro here as our VOLUME_FLAGS are + defined using cpu_to_le16() macro and hence are consistent. */ + vol->flags = vinf->flags; + /* + * Reinitialize the search context for the $Volume/$VOLUME_NAME lookup. + */ + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_lookup(AT_VOLUME_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, + ctx)) { + if (errno != ENOENT) { + ntfs_log_perror("Failed to lookup of $VOLUME_NAME in " + "$Volume failed"); + goto error_exit; + } + /* + * Attribute not present. This has been seen in the field. + * Treat this the same way as if the attribute was present but + * had zero length. + */ + vol->vol_name = ntfs_malloc(1); + if (!vol->vol_name) + goto error_exit; + vol->vol_name[0] = '\0'; + } else { + a = ctx->attr; + /* Has to be resident. */ + if (a->non_resident) { + ntfs_log_error("$VOLUME_NAME must be resident.\n"); + errno = EIO; + goto error_exit; + } + /* Get a pointer to the value of the attribute. */ + vname = (ntfschar*)(le16_to_cpu(a->value_offset) + (char*)a); + u = le32_to_cpu(a->value_length) / 2; + /* + * Convert Unicode volume name to current locale multibyte + * format. + */ + vol->vol_name = NULL; + if (ntfs_ucstombs(vname, u, &vol->vol_name, 0) == -1) { + ntfs_log_perror("Volume name could not be converted " + "to current locale"); + ntfs_log_debug("Forcing name into ASCII by replacing " + "non-ASCII characters with underscores.\n"); + vol->vol_name = ntfs_malloc(u + 1); + if (!vol->vol_name) + goto error_exit; + + for (j = 0; j < (s32)u; j++) { + u16 uc = le16_to_cpu(vname[j]); + if (uc > 0xff) + uc = (u16)'_'; + vol->vol_name[j] = (char)uc; + } + vol->vol_name[u] = '\0'; + } + } + ntfs_attr_put_search_ctx(ctx); + ctx = NULL; + /* Now load the attribute definitions from $AttrDef. */ + ntfs_log_debug("Loading $AttrDef...\n"); + ni = ntfs_inode_open(vol, FILE_AttrDef); + if (!ni) { + ntfs_log_perror("Failed to open $AttrDef"); + goto error_exit; + } + /* Get an ntfs attribute for $AttrDef/$DATA. */ + na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); + if (!na) { + ntfs_log_perror("Failed to open ntfs attribute"); + goto error_exit; + } + /* Check we don't overflow 32-bits. */ + if (na->data_size > 0xffffffffLL) { + ntfs_log_error("Attribute definition table is too big (max " + "32-bit allowed).\n"); + errno = EINVAL; + goto error_exit; + } + vol->attrdef_len = na->data_size; + vol->attrdef = ntfs_malloc(na->data_size); + if (!vol->attrdef) + goto error_exit; + /* Read in the $DATA attribute value into the buffer. */ + l = ntfs_attr_pread(na, 0, na->data_size, vol->attrdef); + if (l != na->data_size) { + ntfs_log_error("Failed to read $AttrDef, unexpected length " + "(%lld != %lld).\n", (long long)l, + (long long)na->data_size); + errno = EIO; + goto error_exit; + } + /* Done with the $AttrDef mft record. */ + ntfs_attr_close(na); + if (ntfs_inode_close(ni)) { + ntfs_log_perror("Failed to close $AttrDef"); + goto error_exit; + } + /* + * Check for dirty logfile and hibernated Windows. + * We care only about read-write mounts. + */ + if (!(flags & MS_RDONLY)) { + if (!(flags & MS_IGNORE_HIBERFILE) && + ntfs_volume_check_hiberfile(vol, 1) < 0) + goto error_exit; + if (ntfs_volume_check_logfile(vol) < 0) { + if (!(flags & MS_RECOVER)) + goto error_exit; + ntfs_log_info("The file system wasn't safely " + "closed on Windows. Fixing.\n"); + if (ntfs_logfile_reset(vol)) + goto error_exit; + } + } + /* make $TXF_DATA resident if present on the root directory */ + if (!NVolReadOnly(vol) && fix_txf_data(vol)) + goto error_exit; - /* - * Now load $Volume and set the version information and flags in the - * vol structure accordingly. - */ - ntfs_log_debug( "Loading $Volume...\n" ); - vol->vol_ni = ntfs_inode_open( vol, FILE_Volume ); - if ( !vol->vol_ni ) - { - ntfs_log_perror( "Failed to open inode FILE_Volume" ); - goto error_exit; - } - /* Get a search context for the $Volume/$VOLUME_INFORMATION lookup. */ - ctx = ntfs_attr_get_search_ctx( vol->vol_ni, NULL ); - if ( !ctx ) - goto error_exit; - - /* Find the $VOLUME_INFORMATION attribute. */ - if ( ntfs_attr_lookup( AT_VOLUME_INFORMATION, AT_UNNAMED, 0, 0, 0, NULL, - 0, ctx ) ) - { - ntfs_log_perror( "$VOLUME_INFORMATION attribute not found in " - "$Volume" ); - goto error_exit; - } - a = ctx->attr; - /* Has to be resident. */ - if ( a->non_resident ) - { - ntfs_log_error( "Attribute $VOLUME_INFORMATION must be " - "resident but it isn't.\n" ); - errno = EIO; - goto error_exit; - } - /* Get a pointer to the value of the attribute. */ - vinf = ( VOLUME_INFORMATION* )( le16_to_cpu( a->value_offset ) + ( char* )a ); - /* Sanity checks. */ - if ( ( char* )vinf + le32_to_cpu( a->value_length ) > ( char* )ctx->mrec + - le32_to_cpu( ctx->mrec->bytes_in_use ) || - le16_to_cpu( a->value_offset ) + le32_to_cpu( - a->value_length ) > le32_to_cpu( a->length ) ) - { - ntfs_log_error( "$VOLUME_INFORMATION in $Volume is corrupt.\n" ); - errno = EIO; - goto error_exit; - } - /* Setup vol from the volume information attribute value. */ - vol->major_ver = vinf->major_ver; - vol->minor_ver = vinf->minor_ver; - /* Do not use le16_to_cpu() macro here as our VOLUME_FLAGS are - defined using cpu_to_le16() macro and hence are consistent. */ - vol->flags = vinf->flags; - /* - * Reinitialize the search context for the $Volume/$VOLUME_NAME lookup. - */ - ntfs_attr_reinit_search_ctx( ctx ); - if ( ntfs_attr_lookup( AT_VOLUME_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, - ctx ) ) - { - if ( errno != ENOENT ) - { - ntfs_log_perror( "Failed to lookup of $VOLUME_NAME in " - "$Volume failed" ); - goto error_exit; - } - /* - * Attribute not present. This has been seen in the field. - * Treat this the same way as if the attribute was present but - * had zero length. - */ - vol->vol_name = ntfs_malloc( 1 ); - if ( !vol->vol_name ) - goto error_exit; - vol->vol_name[0] = '\0'; - } - else - { - a = ctx->attr; - /* Has to be resident. */ - if ( a->non_resident ) - { - ntfs_log_error( "$VOLUME_NAME must be resident.\n" ); - errno = EIO; - goto error_exit; - } - /* Get a pointer to the value of the attribute. */ - vname = ( ntfschar* )( le16_to_cpu( a->value_offset ) + ( char* )a ); - u = le32_to_cpu( a->value_length ) / 2; - /* - * Convert Unicode volume name to current locale multibyte - * format. - */ - vol->vol_name = NULL; - if ( ntfs_ucstombs( vname, u, &vol->vol_name, 0 ) == -1 ) - { - ntfs_log_perror( "Volume name could not be converted " - "to current locale" ); - ntfs_log_debug( "Forcing name into ASCII by replacing " - "non-ASCII characters with underscores.\n" ); - vol->vol_name = ntfs_malloc( u + 1 ); - if ( !vol->vol_name ) - goto error_exit; - - for ( j = 0; j < ( s32 )u; j++ ) - { - u16 uc = le16_to_cpu( vname[j] ); - if ( uc > 0xff ) - uc = ( u16 )'_'; - vol->vol_name[j] = ( char )uc; - } - vol->vol_name[u] = '\0'; - } - } - ntfs_attr_put_search_ctx( ctx ); - ctx = NULL; - /* Now load the attribute definitions from $AttrDef. */ - ntfs_log_debug( "Loading $AttrDef...\n" ); - ni = ntfs_inode_open( vol, FILE_AttrDef ); - if ( !ni ) - { - ntfs_log_perror( "Failed to open $AttrDef" ); - goto error_exit; - } - /* Get an ntfs attribute for $AttrDef/$DATA. */ - na = ntfs_attr_open( ni, AT_DATA, AT_UNNAMED, 0 ); - if ( !na ) - { - ntfs_log_perror( "Failed to open ntfs attribute" ); - goto error_exit; - } - /* Check we don't overflow 32-bits. */ - if ( na->data_size > 0xffffffffLL ) - { - ntfs_log_error( "Attribute definition table is too big (max " - "32-bit allowed).\n" ); - errno = EINVAL; - goto error_exit; - } - vol->attrdef_len = na->data_size; - vol->attrdef = ntfs_malloc( na->data_size ); - if ( !vol->attrdef ) - goto error_exit; - /* Read in the $DATA attribute value into the buffer. */ - l = ntfs_attr_pread( na, 0, na->data_size, vol->attrdef ); - if ( l != na->data_size ) - { - ntfs_log_error( "Failed to read $AttrDef, unexpected length " - "(%lld != %lld).\n", ( long long )l, - ( long long )na->data_size ); - errno = EIO; - goto error_exit; - } - /* Done with the $AttrDef mft record. */ - ntfs_attr_close( na ); - if ( ntfs_inode_close( ni ) ) - { - ntfs_log_perror( "Failed to close $AttrDef" ); - goto error_exit; - } - /* - * Check for dirty logfile and hibernated Windows. - * We care only about read-write mounts. - */ - if ( !( flags & MS_RDONLY ) ) - { - if ( !( flags & MS_IGNORE_HIBERFILE ) && - ntfs_volume_check_hiberfile( vol, 1 ) < 0 ) - goto error_exit; - if ( ntfs_volume_check_logfile( vol ) < 0 ) - { - if ( !( flags & MS_RECOVER ) ) - goto error_exit; - ntfs_log_info( "The file system wasn't safely " - "closed on Windows. Fixing.\n" ); - if ( ntfs_logfile_reset( vol ) ) - goto error_exit; - } - } - /* make $TXF_DATA resident if present on the root directory */ - if ( !NVolReadOnly( vol ) && fix_txf_data( vol ) ) - goto error_exit; - - return vol; + return vol; io_error_exit: - errno = EIO; + errno = EIO; error_exit: - eo = errno; - if ( ctx ) - ntfs_attr_put_search_ctx( ctx ); - free( m ); - free( m2 ); - __ntfs_volume_release( vol ); - errno = eo; - return NULL; + eo = errno; + if (ctx) + ntfs_attr_put_search_ctx(ctx); + free(m); + free(m2); + __ntfs_volume_release(vol); + errno = eo; + return NULL; } /* - * Set appropriate flags for showing NTFS metafiles - * or files marked as hidden. - * Not set in ntfs_mount() to avoid breaking existing tools. + * Set appropriate flags for showing NTFS metafiles + * or files marked as hidden. + * Not set in ntfs_mount() to avoid breaking existing tools. */ -int ntfs_set_shown_files( ntfs_volume *vol, - BOOL show_sys_files, BOOL show_hid_files, - BOOL hide_dot_files ) +int ntfs_set_shown_files(ntfs_volume *vol, + BOOL show_sys_files, BOOL show_hid_files, + BOOL hide_dot_files) { - int res; + int res; - res = -1; - if ( vol ) - { - NVolClearShowSysFiles( vol ); - NVolClearShowHidFiles( vol ); - NVolClearHideDotFiles( vol ); - if ( show_sys_files ) - NVolSetShowSysFiles( vol ); - if ( show_hid_files ) - NVolSetShowHidFiles( vol ); - if ( hide_dot_files ) - NVolSetHideDotFiles( vol ); - res = 0; - } - if ( res ) - ntfs_log_error( "Failed to set file visibility\n" ); - return ( res ); + res = -1; + if (vol) { + NVolClearShowSysFiles(vol); + NVolClearShowHidFiles(vol); + NVolClearHideDotFiles(vol); + if (show_sys_files) + NVolSetShowSysFiles(vol); + if (show_hid_files) + NVolSetShowHidFiles(vol); + if (hide_dot_files) + NVolSetHideDotFiles(vol); + res = 0; + } + if (res) + ntfs_log_error("Failed to set file visibility\n"); + return (res); } /* - * Set ignore case mode + * Set ignore case mode */ -int ntfs_set_ignore_case( ntfs_volume *vol ) +int ntfs_set_ignore_case(ntfs_volume *vol) { - int res; + int res; - res = -1; - if ( vol && vol->upcase ) - { - vol->locase = ntfs_locase_table_build( vol->upcase, - vol->upcase_len ); - if ( vol->locase ) - { - NVolClearCaseSensitive( vol ); - res = 0; - } - } - if ( res ) - ntfs_log_error( "Failed to set ignore_case mode\n" ); - return ( res ); + res = -1; + if (vol && vol->upcase) { + vol->locase = ntfs_locase_table_build(vol->upcase, + vol->upcase_len); + if (vol->locase) { + NVolClearCaseSensitive(vol); + res = 0; + } + } + if (res) + ntfs_log_error("Failed to set ignore_case mode\n"); + return (res); } /** * ntfs_mount - open ntfs volume - * @name: name of device/file to open - * @flags: optional mount flags + * @name: name of device/file to open + * @flags: optional mount flags * * This function mounts an ntfs volume. @name should contain the name of the * device/file to mount as the ntfs volume. @@ -1363,7 +1266,7 @@ int ntfs_set_ignore_case( ntfs_volume *vol ) * @flags is an optional second parameter. The same flags are used as for * the mount system call (man 2 mount). Currently only the following flags * is implemented: - * MS_RDONLY - mount volume read-only + * MS_RDONLY - mount volume read-only * * The function opens the device or file @name and verifies that it contains a * valid bootsector. Then, it allocates an ntfs_volume structure and initializes @@ -1377,36 +1280,34 @@ int ntfs_set_ignore_case( ntfs_volume *vol ) * Note, that a copy is made of @name, and hence it can be discarded as * soon as the function returns. */ -ntfs_volume *ntfs_mount( const char *name __attribute__( ( unused ) ), - unsigned long flags __attribute__( ( unused ) ) ) +ntfs_volume *ntfs_mount(const char *name __attribute__((unused)), + unsigned long flags __attribute__((unused))) { #ifndef NO_NTFS_DEVICE_DEFAULT_IO_OPS - struct ntfs_device *dev; - ntfs_volume *vol; + struct ntfs_device *dev; + ntfs_volume *vol; - /* Allocate an ntfs_device structure. */ - dev = ntfs_device_alloc( name, 0, &ntfs_device_default_io_ops, NULL ); - if ( !dev ) - return NULL; - /* Call ntfs_device_mount() to do the actual mount. */ - vol = ntfs_device_mount( dev, flags ); - if ( !vol ) - { - int eo = errno; - ntfs_device_free( dev ); - errno = eo; - } - else - ntfs_create_lru_caches( vol ); - return vol; + /* Allocate an ntfs_device structure. */ + dev = ntfs_device_alloc(name, 0, &ntfs_device_default_io_ops, NULL); + if (!dev) + return NULL; + /* Call ntfs_device_mount() to do the actual mount. */ + vol = ntfs_device_mount(dev, flags); + if (!vol) { + int eo = errno; + ntfs_device_free(dev); + errno = eo; + } else + ntfs_create_lru_caches(vol); + return vol; #else - /* - * ntfs_mount() makes no sense if NO_NTFS_DEVICE_DEFAULT_IO_OPS is - * defined as there are no device operations available in libntfs in - * this case. - */ - errno = EOPNOTSUPP; - return NULL; + /* + * ntfs_mount() makes no sense if NO_NTFS_DEVICE_DEFAULT_IO_OPS is + * defined as there are no device operations available in libntfs in + * this case. + */ + errno = EOPNOTSUPP; + return NULL; #endif } @@ -1432,20 +1333,19 @@ ntfs_volume *ntfs_mount( const char *name __attribute__( ( unused ) ), * function returns success. If it returns an error then nothing has been done * so it is safe to continue using @vol. */ -int ntfs_umount( ntfs_volume *vol, const BOOL force __attribute__( ( unused ) ) ) +int ntfs_umount(ntfs_volume *vol, const BOOL force __attribute__((unused))) { - struct ntfs_device *dev; - int ret; + struct ntfs_device *dev; + int ret; - if ( !vol ) - { - errno = EINVAL; - return -1; - } - dev = vol->dev; - ret = __ntfs_volume_release( vol ); - ntfs_device_free( dev ); - return ret; + if (!vol) { + errno = EINVAL; + return -1; + } + dev = vol->dev; + ret = __ntfs_volume_release(vol); + ntfs_device_free(dev); + return ret; } #ifdef HAVE_MNTENT_H @@ -1454,11 +1354,11 @@ int ntfs_umount( ntfs_volume *vol, const BOOL force __attribute__( ( unused ) ) /** * realpath - If there is no realpath on the system */ -static char *realpath( const char *path, char *resolved_path ) +static char *realpath(const char *path, char *resolved_path) { - strncpy( resolved_path, path, PATH_MAX ); - resolved_path[PATH_MAX] = '\0'; - return resolved_path; + strncpy(resolved_path, path, PATH_MAX); + resolved_path[PATH_MAX] = '\0'; + return resolved_path; } #endif @@ -1470,65 +1370,60 @@ static char *realpath( const char *path, char *resolved_path ) * * See description of ntfs_check_if_mounted(), below. */ -static int ntfs_mntent_check( const char *file, unsigned long *mnt_flags ) +static int ntfs_mntent_check(const char *file, unsigned long *mnt_flags) { - struct mntent *mnt; - char *real_file = NULL, *real_fsname = NULL; - FILE *f; - int err = 0; + struct mntent *mnt; + char *real_file = NULL, *real_fsname = NULL; + FILE *f; + int err = 0; - real_file = ntfs_malloc( PATH_MAX + 1 ); - if ( !real_file ) - return -1; - real_fsname = ntfs_malloc( PATH_MAX + 1 ); - if ( !real_fsname ) - { - err = errno; - goto exit; - } - if ( !realpath( file, real_file ) ) - { - err = errno; - goto exit; - } - if ( !( f = setmntent( MOUNTED, "r" ) ) ) - { - err = errno; - goto exit; - } - while ( ( mnt = getmntent( f ) ) ) - { - if ( !realpath( mnt->mnt_fsname, real_fsname ) ) - continue; - if ( !strcmp( real_file, real_fsname ) ) - break; - } - endmntent( f ); - if ( !mnt ) - goto exit; - *mnt_flags = NTFS_MF_MOUNTED; - if ( !strcmp( mnt->mnt_dir, "/" ) ) - *mnt_flags |= NTFS_MF_ISROOT; + real_file = ntfs_malloc(PATH_MAX + 1); + if (!real_file) + return -1; + real_fsname = ntfs_malloc(PATH_MAX + 1); + if (!real_fsname) { + err = errno; + goto exit; + } + if (!realpath(file, real_file)) { + err = errno; + goto exit; + } + if (!(f = setmntent(MOUNTED, "r"))) { + err = errno; + goto exit; + } + while ((mnt = getmntent(f))) { + if (!realpath(mnt->mnt_fsname, real_fsname)) + continue; + if (!strcmp(real_file, real_fsname)) + break; + } + endmntent(f); + if (!mnt) + goto exit; + *mnt_flags = NTFS_MF_MOUNTED; + if (!strcmp(mnt->mnt_dir, "/")) + *mnt_flags |= NTFS_MF_ISROOT; #ifdef HAVE_HASMNTOPT - if ( hasmntopt( mnt, "ro" ) && !hasmntopt( mnt, "rw" ) ) - *mnt_flags |= NTFS_MF_READONLY; + if (hasmntopt(mnt, "ro") && !hasmntopt(mnt, "rw")) + *mnt_flags |= NTFS_MF_READONLY; #endif exit: - free( real_file ); - free( real_fsname ); - if ( err ) - { - errno = err; - return -1; - } - return 0; + free(real_file); + free(real_fsname); + if (err) { + errno = err; + return -1; + } + return 0; } #endif /* HAVE_MNTENT_H */ /** * ntfs_check_if_mounted - check if an ntfs volume is currently mounted - * @file: device file to check - * @mnt_flags: pointer into which to return the ntfs mount flags (see volume.h) + * @file: device file to check + * @mnt_flags: pointer into which to return the ntfs mount flags (see volume.h) * * If the running system does not support the {set,get,end}mntent() calls, * just return 0 and set *@mnt_flags to zero. @@ -1550,20 +1445,20 @@ exit: * * On error return -1 with errno set to the error code. */ -int ntfs_check_if_mounted( const char *file __attribute__( ( unused ) ), - unsigned long *mnt_flags ) +int ntfs_check_if_mounted(const char *file __attribute__((unused)), + unsigned long *mnt_flags) { - *mnt_flags = 0; + *mnt_flags = 0; #ifdef HAVE_MNTENT_H - return ntfs_mntent_check( file, mnt_flags ); + return ntfs_mntent_check(file, mnt_flags); #else - return 0; + return 0; #endif } /** * ntfs_version_is_supported - check if NTFS version is supported. - * @vol: ntfs volume whose version we're interested in. + * @vol: ntfs volume whose version we're interested in. * * The function checks if the NTFS volume version is known or not. * Version 1.1 and 1.2 are used by Windows NT3.x and NT4. @@ -1574,38 +1469,37 @@ int ntfs_check_if_mounted( const char *file __attribute__( ( unused ) ), * Return 0 if NTFS version is supported otherwise -1 with errno set. * * The following error codes are defined: - * EOPNOTSUPP - Unknown NTFS version - * EINVAL - Invalid argument + * EOPNOTSUPP - Unknown NTFS version + * EINVAL - Invalid argument */ -int ntfs_version_is_supported( ntfs_volume *vol ) +int ntfs_version_is_supported(ntfs_volume *vol) { - u8 major, minor; + u8 major, minor; - if ( !vol ) - { - errno = EINVAL; - return -1; - } + if (!vol) { + errno = EINVAL; + return -1; + } - major = vol->major_ver; - minor = vol->minor_ver; + major = vol->major_ver; + minor = vol->minor_ver; - if ( NTFS_V1_1( major, minor ) || NTFS_V1_2( major, minor ) ) - return 0; + if (NTFS_V1_1(major, minor) || NTFS_V1_2(major, minor)) + return 0; - if ( NTFS_V2_X( major, minor ) ) - return 0; + if (NTFS_V2_X(major, minor)) + return 0; - if ( NTFS_V3_0( major, minor ) || NTFS_V3_1( major, minor ) ) - return 0; + if (NTFS_V3_0(major, minor) || NTFS_V3_1(major, minor)) + return 0; - errno = EOPNOTSUPP; - return -1; + errno = EOPNOTSUPP; + return -1; } /** * ntfs_logfile_reset - "empty" $LogFile data attribute value - * @vol: ntfs volume whose $LogFile we intend to reset. + * @vol: ntfs volume whose $LogFile we intend to reset. * * Fill the value of the $LogFile data attribute, i.e. the contents of * the file, with 0xff's, thus marking the journal as empty. @@ -1618,226 +1512,212 @@ int ntfs_version_is_supported( ntfs_volume *vol ) * * On error return -1 with errno set to the error code. */ -int ntfs_logfile_reset( ntfs_volume *vol ) +int ntfs_logfile_reset(ntfs_volume *vol) { - ntfs_inode *ni; - ntfs_attr *na; - int eo; + ntfs_inode *ni; + ntfs_attr *na; + int eo; - if ( !vol ) - { - errno = EINVAL; - return -1; - } + if (!vol) { + errno = EINVAL; + return -1; + } - ni = ntfs_inode_open( vol, FILE_LogFile ); - if ( !ni ) - { - ntfs_log_perror( "Failed to open inode FILE_LogFile" ); - return -1; - } + ni = ntfs_inode_open(vol, FILE_LogFile); + if (!ni) { + ntfs_log_perror("Failed to open inode FILE_LogFile"); + return -1; + } - na = ntfs_attr_open( ni, AT_DATA, AT_UNNAMED, 0 ); - if ( !na ) - { - eo = errno; - ntfs_log_perror( "Failed to open $FILE_LogFile/$DATA" ); - goto error_exit; - } + na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); + if (!na) { + eo = errno; + ntfs_log_perror("Failed to open $FILE_LogFile/$DATA"); + goto error_exit; + } - if ( ntfs_empty_logfile( na ) ) - { - eo = errno; - ntfs_attr_close( na ); - goto error_exit; - } - - ntfs_attr_close( na ); - return ntfs_inode_close( ni ); + if (ntfs_empty_logfile(na)) { + eo = errno; + ntfs_attr_close(na); + goto error_exit; + } + + ntfs_attr_close(na); + return ntfs_inode_close(ni); error_exit: - ntfs_inode_close( ni ); - errno = eo; - return -1; + ntfs_inode_close(ni); + errno = eo; + return -1; } /** * ntfs_volume_write_flags - set the flags of an ntfs volume - * @vol: ntfs volume where we set the volume flags - * @flags: new flags + * @vol: ntfs volume where we set the volume flags + * @flags: new flags * * Set the on-disk volume flags in the mft record of $Volume and * on volume @vol to @flags. * * Return 0 if successful and -1 if not with errno set to the error code. */ -int ntfs_volume_write_flags( ntfs_volume *vol, const le16 flags ) +int ntfs_volume_write_flags(ntfs_volume *vol, const le16 flags) { - ATTR_RECORD *a; - VOLUME_INFORMATION *c; - ntfs_attr_search_ctx *ctx; - int ret = -1; /* failure */ + ATTR_RECORD *a; + VOLUME_INFORMATION *c; + ntfs_attr_search_ctx *ctx; + int ret = -1; /* failure */ - if ( !vol || !vol->vol_ni ) - { - errno = EINVAL; - return -1; - } - /* Get a pointer to the volume information attribute. */ - ctx = ntfs_attr_get_search_ctx( vol->vol_ni, NULL ); - if ( !ctx ) - return -1; + if (!vol || !vol->vol_ni) { + errno = EINVAL; + return -1; + } + /* Get a pointer to the volume information attribute. */ + ctx = ntfs_attr_get_search_ctx(vol->vol_ni, NULL); + if (!ctx) + return -1; - if ( ntfs_attr_lookup( AT_VOLUME_INFORMATION, AT_UNNAMED, 0, 0, 0, NULL, - 0, ctx ) ) - { - ntfs_log_error( "Attribute $VOLUME_INFORMATION was not found " - "in $Volume!\n" ); - goto err_out; - } - a = ctx->attr; - /* Sanity check. */ - if ( a->non_resident ) - { - ntfs_log_error( "Attribute $VOLUME_INFORMATION must be resident " - "but it isn't.\n" ); - errno = EIO; - goto err_out; - } - /* Get a pointer to the value of the attribute. */ - c = ( VOLUME_INFORMATION* )( le16_to_cpu( a->value_offset ) + ( char* )a ); - /* Sanity checks. */ - if ( ( char* )c + le32_to_cpu( a->value_length ) > ( char* )ctx->mrec + - le32_to_cpu( ctx->mrec->bytes_in_use ) || - le16_to_cpu( a->value_offset ) + - le32_to_cpu( a->value_length ) > le32_to_cpu( a->length ) ) - { - ntfs_log_error( "Attribute $VOLUME_INFORMATION in $Volume is " - "corrupt!\n" ); - errno = EIO; - goto err_out; - } - /* Set the volume flags. */ - vol->flags = c->flags = flags & VOLUME_FLAGS_MASK; - /* Write them to disk. */ - ntfs_inode_mark_dirty( vol->vol_ni ); - if ( ntfs_inode_sync( vol->vol_ni ) ) - goto err_out; + if (ntfs_attr_lookup(AT_VOLUME_INFORMATION, AT_UNNAMED, 0, 0, 0, NULL, + 0, ctx)) { + ntfs_log_error("Attribute $VOLUME_INFORMATION was not found " + "in $Volume!\n"); + goto err_out; + } + a = ctx->attr; + /* Sanity check. */ + if (a->non_resident) { + ntfs_log_error("Attribute $VOLUME_INFORMATION must be resident " + "but it isn't.\n"); + errno = EIO; + goto err_out; + } + /* Get a pointer to the value of the attribute. */ + c = (VOLUME_INFORMATION*)(le16_to_cpu(a->value_offset) + (char*)a); + /* Sanity checks. */ + if ((char*)c + le32_to_cpu(a->value_length) > (char*)ctx->mrec + + le32_to_cpu(ctx->mrec->bytes_in_use) || + le16_to_cpu(a->value_offset) + + le32_to_cpu(a->value_length) > le32_to_cpu(a->length)) { + ntfs_log_error("Attribute $VOLUME_INFORMATION in $Volume is " + "corrupt!\n"); + errno = EIO; + goto err_out; + } + /* Set the volume flags. */ + vol->flags = c->flags = flags & VOLUME_FLAGS_MASK; + /* Write them to disk. */ + ntfs_inode_mark_dirty(vol->vol_ni); + if (ntfs_inode_sync(vol->vol_ni)) + goto err_out; - ret = 0; /* success */ + ret = 0; /* success */ err_out: - ntfs_attr_put_search_ctx( ctx ); - return ret; + ntfs_attr_put_search_ctx(ctx); + return ret; } -int ntfs_volume_error( int err ) +int ntfs_volume_error(int err) { - int ret; + int ret; - switch ( err ) - { - case 0: - ret = NTFS_VOLUME_OK; - break; - case EINVAL: - ret = NTFS_VOLUME_NOT_NTFS; - break; - case EIO: - ret = NTFS_VOLUME_CORRUPT; - break; - case EPERM: - ret = NTFS_VOLUME_HIBERNATED; - break; - case EOPNOTSUPP: - ret = NTFS_VOLUME_UNCLEAN_UNMOUNT; - break; - case EBUSY: - ret = NTFS_VOLUME_LOCKED; - break; - case ENXIO: - ret = NTFS_VOLUME_RAID; - break; - case EACCES: - ret = NTFS_VOLUME_NO_PRIVILEGE; - break; - default: - ret = NTFS_VOLUME_UNKNOWN_REASON; - break; - } - return ret; + switch (err) { + case 0: + ret = NTFS_VOLUME_OK; + break; + case EINVAL: + ret = NTFS_VOLUME_NOT_NTFS; + break; + case EIO: + ret = NTFS_VOLUME_CORRUPT; + break; + case EPERM: + ret = NTFS_VOLUME_HIBERNATED; + break; + case EOPNOTSUPP: + ret = NTFS_VOLUME_UNCLEAN_UNMOUNT; + break; + case EBUSY: + ret = NTFS_VOLUME_LOCKED; + break; + case ENXIO: + ret = NTFS_VOLUME_RAID; + break; + case EACCES: + ret = NTFS_VOLUME_NO_PRIVILEGE; + break; + default: + ret = NTFS_VOLUME_UNKNOWN_REASON; + break; + } + return ret; } -void ntfs_mount_error( const char *volume, const char *mntpoint, int err ) +void ntfs_mount_error(const char *volume, const char *mntpoint, int err) { - switch ( err ) - { - case NTFS_VOLUME_NOT_NTFS: - ntfs_log_error( invalid_ntfs_msg, volume ); - break; - case NTFS_VOLUME_CORRUPT: - ntfs_log_error( "%s", corrupt_volume_msg ); - break; - case NTFS_VOLUME_HIBERNATED: - ntfs_log_error( hibernated_volume_msg, volume, mntpoint ); - break; - case NTFS_VOLUME_UNCLEAN_UNMOUNT: - ntfs_log_error( "%s", unclean_journal_msg ); - break; - case NTFS_VOLUME_LOCKED: - ntfs_log_error( "%s", opened_volume_msg ); - break; - case NTFS_VOLUME_RAID: - ntfs_log_error( "%s", fakeraid_msg ); - break; - case NTFS_VOLUME_NO_PRIVILEGE: - ntfs_log_error( access_denied_msg, volume ); - break; - } + switch (err) { + case NTFS_VOLUME_NOT_NTFS: + ntfs_log_error(invalid_ntfs_msg, volume); + break; + case NTFS_VOLUME_CORRUPT: + ntfs_log_error("%s", corrupt_volume_msg); + break; + case NTFS_VOLUME_HIBERNATED: + ntfs_log_error(hibernated_volume_msg, volume, mntpoint); + break; + case NTFS_VOLUME_UNCLEAN_UNMOUNT: + ntfs_log_error("%s", unclean_journal_msg); + break; + case NTFS_VOLUME_LOCKED: + ntfs_log_error("%s", opened_volume_msg); + break; + case NTFS_VOLUME_RAID: + ntfs_log_error("%s", fakeraid_msg); + break; + case NTFS_VOLUME_NO_PRIVILEGE: + ntfs_log_error(access_denied_msg, volume); + break; + } } -int ntfs_set_locale( void ) +int ntfs_set_locale(void) { - const char *locale; + const char *locale; - locale = setlocale( LC_ALL, "" ); - if ( !locale ) - { - locale = setlocale( LC_ALL, NULL ); - ntfs_log_error( "Couldn't set local environment, using default " - "'%s'.\n", locale ); - return 1; - } - return 0; + locale = setlocale(LC_ALL, ""); + if (!locale) { + locale = setlocale(LC_ALL, NULL); + ntfs_log_error("Couldn't set local environment, using default " + "'%s'.\n", locale); + return 1; + } + return 0; } /* - * Feed the counts of free clusters and free mft records + * Feed the counts of free clusters and free mft records */ -int ntfs_volume_get_free_space( ntfs_volume *vol ) +int ntfs_volume_get_free_space(ntfs_volume *vol) { - ntfs_attr *na; - int ret; + ntfs_attr *na; + int ret; - ret = -1; /* default return */ - vol->free_clusters = ntfs_attr_get_free_bits( vol->lcnbmp_na ); - if ( vol->free_clusters < 0 ) - { - ntfs_log_perror( "Failed to read NTFS $Bitmap" ); - } - else - { - na = vol->mftbmp_na; - vol->free_mft_records = ntfs_attr_get_free_bits( na ); + ret = -1; /* default return */ + vol->free_clusters = ntfs_attr_get_free_bits(vol->lcnbmp_na); + if (vol->free_clusters < 0) { + ntfs_log_perror("Failed to read NTFS $Bitmap"); + } else { + na = vol->mftbmp_na; + vol->free_mft_records = ntfs_attr_get_free_bits(na); - if ( vol->free_mft_records >= 0 ) - vol->free_mft_records += ( na->allocated_size - na->data_size ) << 3; + if (vol->free_mft_records >= 0) + vol->free_mft_records += (na->allocated_size - na->data_size) << 3; - if ( vol->free_mft_records < 0 ) - ntfs_log_perror( "Failed to calculate free MFT records" ); - else - ret = 0; - } - return ( ret ); + if (vol->free_mft_records < 0) + ntfs_log_perror("Failed to calculate free MFT records"); + else + ret = 0; + } + return (ret); } diff --git a/source/libntfs/volume.h b/source/libntfs/volume.h index 9e4dd108..79193c53 100644 --- a/source/libntfs/volume.h +++ b/source/libntfs/volume.h @@ -74,30 +74,28 @@ typedef struct _ntfs_volume ntfs_volume; * * Flags returned by the ntfs_check_if_mounted() function. */ -typedef enum -{ - NTFS_MF_MOUNTED = 1, /* Device is mounted. */ - NTFS_MF_ISROOT = 2, /* Device is mounted as system root. */ - NTFS_MF_READONLY = 4, /* Device is mounted read-only. */ +typedef enum { + NTFS_MF_MOUNTED = 1, /* Device is mounted. */ + NTFS_MF_ISROOT = 2, /* Device is mounted as system root. */ + NTFS_MF_READONLY = 4, /* Device is mounted read-only. */ } ntfs_mount_flags; -extern int ntfs_check_if_mounted( const char *file, unsigned long *mnt_flags ); +extern int ntfs_check_if_mounted(const char *file, unsigned long *mnt_flags); -typedef enum -{ - NTFS_VOLUME_OK = 0, - NTFS_VOLUME_SYNTAX_ERROR = 11, - NTFS_VOLUME_NOT_NTFS = 12, - NTFS_VOLUME_CORRUPT = 13, - NTFS_VOLUME_HIBERNATED = 14, - NTFS_VOLUME_UNCLEAN_UNMOUNT = 15, - NTFS_VOLUME_LOCKED = 16, - NTFS_VOLUME_RAID = 17, - NTFS_VOLUME_UNKNOWN_REASON = 18, - NTFS_VOLUME_NO_PRIVILEGE = 19, - NTFS_VOLUME_OUT_OF_MEMORY = 20, - NTFS_VOLUME_FUSE_ERROR = 21, - NTFS_VOLUME_INSECURE = 22 +typedef enum { + NTFS_VOLUME_OK = 0, + NTFS_VOLUME_SYNTAX_ERROR = 11, + NTFS_VOLUME_NOT_NTFS = 12, + NTFS_VOLUME_CORRUPT = 13, + NTFS_VOLUME_HIBERNATED = 14, + NTFS_VOLUME_UNCLEAN_UNMOUNT = 15, + NTFS_VOLUME_LOCKED = 16, + NTFS_VOLUME_RAID = 17, + NTFS_VOLUME_UNKNOWN_REASON = 18, + NTFS_VOLUME_NO_PRIVILEGE = 19, + NTFS_VOLUME_OUT_OF_MEMORY = 20, + NTFS_VOLUME_FUSE_ERROR = 21, + NTFS_VOLUME_INSECURE = 22 } ntfs_volume_status; /** @@ -105,48 +103,47 @@ typedef enum * * Defined bits for the state field in the ntfs_volume structure. */ -typedef enum -{ - NV_ReadOnly, /* 1: Volume is read-only. */ - NV_CaseSensitive, /* 1: Volume is mounted case-sensitive. */ - NV_LogFileEmpty, /* 1: $logFile journal is empty. */ - NV_ShowSysFiles, /* 1: Show NTFS metafiles. */ - NV_ShowHidFiles, /* 1: Show files marked hidden. */ - NV_HideDotFiles, /* 1: Set hidden flag on dot files */ - NV_Compression, /* 1: allow compression */ +typedef enum { + NV_ReadOnly, /* 1: Volume is read-only. */ + NV_CaseSensitive, /* 1: Volume is mounted case-sensitive. */ + NV_LogFileEmpty, /* 1: $logFile journal is empty. */ + NV_ShowSysFiles, /* 1: Show NTFS metafiles. */ + NV_ShowHidFiles, /* 1: Show files marked hidden. */ + NV_HideDotFiles, /* 1: Set hidden flag on dot files */ + NV_Compression, /* 1: allow compression */ } ntfs_volume_state_bits; -#define test_nvol_flag(nv, flag) test_bit(NV_##flag, (nv)->state) -#define set_nvol_flag(nv, flag) set_bit(NV_##flag, (nv)->state) -#define clear_nvol_flag(nv, flag) clear_bit(NV_##flag, (nv)->state) +#define test_nvol_flag(nv, flag) test_bit(NV_##flag, (nv)->state) +#define set_nvol_flag(nv, flag) set_bit(NV_##flag, (nv)->state) +#define clear_nvol_flag(nv, flag) clear_bit(NV_##flag, (nv)->state) -#define NVolReadOnly(nv) test_nvol_flag(nv, ReadOnly) -#define NVolSetReadOnly(nv) set_nvol_flag(nv, ReadOnly) -#define NVolClearReadOnly(nv) clear_nvol_flag(nv, ReadOnly) +#define NVolReadOnly(nv) test_nvol_flag(nv, ReadOnly) +#define NVolSetReadOnly(nv) set_nvol_flag(nv, ReadOnly) +#define NVolClearReadOnly(nv) clear_nvol_flag(nv, ReadOnly) -#define NVolCaseSensitive(nv) test_nvol_flag(nv, CaseSensitive) -#define NVolSetCaseSensitive(nv) set_nvol_flag(nv, CaseSensitive) -#define NVolClearCaseSensitive(nv) clear_nvol_flag(nv, CaseSensitive) +#define NVolCaseSensitive(nv) test_nvol_flag(nv, CaseSensitive) +#define NVolSetCaseSensitive(nv) set_nvol_flag(nv, CaseSensitive) +#define NVolClearCaseSensitive(nv) clear_nvol_flag(nv, CaseSensitive) -#define NVolLogFileEmpty(nv) test_nvol_flag(nv, LogFileEmpty) -#define NVolSetLogFileEmpty(nv) set_nvol_flag(nv, LogFileEmpty) -#define NVolClearLogFileEmpty(nv) clear_nvol_flag(nv, LogFileEmpty) +#define NVolLogFileEmpty(nv) test_nvol_flag(nv, LogFileEmpty) +#define NVolSetLogFileEmpty(nv) set_nvol_flag(nv, LogFileEmpty) +#define NVolClearLogFileEmpty(nv) clear_nvol_flag(nv, LogFileEmpty) -#define NVolShowSysFiles(nv) test_nvol_flag(nv, ShowSysFiles) -#define NVolSetShowSysFiles(nv) set_nvol_flag(nv, ShowSysFiles) -#define NVolClearShowSysFiles(nv) clear_nvol_flag(nv, ShowSysFiles) +#define NVolShowSysFiles(nv) test_nvol_flag(nv, ShowSysFiles) +#define NVolSetShowSysFiles(nv) set_nvol_flag(nv, ShowSysFiles) +#define NVolClearShowSysFiles(nv) clear_nvol_flag(nv, ShowSysFiles) -#define NVolShowHidFiles(nv) test_nvol_flag(nv, ShowHidFiles) -#define NVolSetShowHidFiles(nv) set_nvol_flag(nv, ShowHidFiles) -#define NVolClearShowHidFiles(nv) clear_nvol_flag(nv, ShowHidFiles) +#define NVolShowHidFiles(nv) test_nvol_flag(nv, ShowHidFiles) +#define NVolSetShowHidFiles(nv) set_nvol_flag(nv, ShowHidFiles) +#define NVolClearShowHidFiles(nv) clear_nvol_flag(nv, ShowHidFiles) -#define NVolHideDotFiles(nv) test_nvol_flag(nv, HideDotFiles) -#define NVolSetHideDotFiles(nv) set_nvol_flag(nv, HideDotFiles) -#define NVolClearHideDotFiles(nv) clear_nvol_flag(nv, HideDotFiles) +#define NVolHideDotFiles(nv) test_nvol_flag(nv, HideDotFiles) +#define NVolSetHideDotFiles(nv) set_nvol_flag(nv, HideDotFiles) +#define NVolClearHideDotFiles(nv) clear_nvol_flag(nv, HideDotFiles) -#define NVolCompression(nv) test_nvol_flag(nv, Compression) -#define NVolSetCompression(nv) set_nvol_flag(nv, Compression) -#define NVolClearCompression(nv) clear_nvol_flag(nv, Compression) +#define NVolCompression(nv) test_nvol_flag(nv, Compression) +#define NVolSetCompression(nv) set_nvol_flag(nv, Compression) +#define NVolClearCompression(nv) clear_nvol_flag(nv, Compression) /* * NTFS version 1.1 and 1.2 are used by Windows NT4. @@ -166,143 +163,141 @@ typedef enum /** * struct _ntfs_volume - structure describing an open volume in memory. */ -struct _ntfs_volume -{ - union - { - struct ntfs_device *dev; /* NTFS device associated with - the volume. */ - void *sb; /* For kernel porting compatibility. */ - }; - char *vol_name; /* Name of the volume. */ - unsigned long state; /* NTFS specific flags describing this volume. - See ntfs_volume_state_bits above. */ +struct _ntfs_volume { + union { + struct ntfs_device *dev; /* NTFS device associated with + the volume. */ + void *sb; /* For kernel porting compatibility. */ + }; + char *vol_name; /* Name of the volume. */ + unsigned long state; /* NTFS specific flags describing this volume. + See ntfs_volume_state_bits above. */ - ntfs_inode *vol_ni; /* ntfs_inode structure for FILE_Volume. */ - u8 major_ver; /* Ntfs major version of volume. */ - u8 minor_ver; /* Ntfs minor version of volume. */ - le16 flags; /* Bit array of VOLUME_* flags. */ + ntfs_inode *vol_ni; /* ntfs_inode structure for FILE_Volume. */ + u8 major_ver; /* Ntfs major version of volume. */ + u8 minor_ver; /* Ntfs minor version of volume. */ + le16 flags; /* Bit array of VOLUME_* flags. */ - u16 sector_size; /* Byte size of a sector. */ - u8 sector_size_bits; /* Log(2) of the byte size of a sector. */ - u32 cluster_size; /* Byte size of a cluster. */ - u32 mft_record_size; /* Byte size of a mft record. */ - u32 indx_record_size; /* Byte size of a INDX record. */ - u8 cluster_size_bits; /* Log(2) of the byte size of a cluster. */ - u8 mft_record_size_bits;/* Log(2) of the byte size of a mft record. */ - u8 indx_record_size_bits;/* Log(2) of the byte size of a INDX record. */ + u16 sector_size; /* Byte size of a sector. */ + u8 sector_size_bits; /* Log(2) of the byte size of a sector. */ + u32 cluster_size; /* Byte size of a cluster. */ + u32 mft_record_size; /* Byte size of a mft record. */ + u32 indx_record_size; /* Byte size of a INDX record. */ + u8 cluster_size_bits; /* Log(2) of the byte size of a cluster. */ + u8 mft_record_size_bits;/* Log(2) of the byte size of a mft record. */ + u8 indx_record_size_bits;/* Log(2) of the byte size of a INDX record. */ - /* Variables used by the cluster and mft allocators. */ - u8 mft_zone_multiplier; /* Initial mft zone multiplier. */ - u8 full_zones; /* cluster zones which are full */ - s64 mft_data_pos; /* Mft record number at which to allocate the - next mft record. */ - LCN mft_zone_start; /* First cluster of the mft zone. */ - LCN mft_zone_end; /* First cluster beyond the mft zone. */ - LCN mft_zone_pos; /* Current position in the mft zone. */ - LCN data1_zone_pos; /* Current position in the first data zone. */ - LCN data2_zone_pos; /* Current position in the second data zone. */ + /* Variables used by the cluster and mft allocators. */ + u8 mft_zone_multiplier; /* Initial mft zone multiplier. */ + u8 full_zones; /* cluster zones which are full */ + s64 mft_data_pos; /* Mft record number at which to allocate the + next mft record. */ + LCN mft_zone_start; /* First cluster of the mft zone. */ + LCN mft_zone_end; /* First cluster beyond the mft zone. */ + LCN mft_zone_pos; /* Current position in the mft zone. */ + LCN data1_zone_pos; /* Current position in the first data zone. */ + LCN data2_zone_pos; /* Current position in the second data zone. */ - s64 nr_clusters; /* Volume size in clusters, hence also the - number of bits in lcn_bitmap. */ - ntfs_inode *lcnbmp_ni; /* ntfs_inode structure for FILE_Bitmap. */ - ntfs_attr *lcnbmp_na; /* ntfs_attr structure for the data attribute - of FILE_Bitmap. Each bit represents a - cluster on the volume, bit 0 representing - lcn 0 and so on. A set bit means that the - cluster and vice versa. */ + s64 nr_clusters; /* Volume size in clusters, hence also the + number of bits in lcn_bitmap. */ + ntfs_inode *lcnbmp_ni; /* ntfs_inode structure for FILE_Bitmap. */ + ntfs_attr *lcnbmp_na; /* ntfs_attr structure for the data attribute + of FILE_Bitmap. Each bit represents a + cluster on the volume, bit 0 representing + lcn 0 and so on. A set bit means that the + cluster and vice versa. */ - LCN mft_lcn; /* Logical cluster number of the data attribute - for FILE_MFT. */ - ntfs_inode *mft_ni; /* ntfs_inode structure for FILE_MFT. */ - ntfs_attr *mft_na; /* ntfs_attr structure for the data attribute - of FILE_MFT. */ - ntfs_attr *mftbmp_na; /* ntfs_attr structure for the bitmap attribute - of FILE_MFT. Each bit represents an mft - record in the $DATA attribute, bit 0 - representing mft record 0 and so on. A set - bit means that the mft record is in use and - vice versa. */ + LCN mft_lcn; /* Logical cluster number of the data attribute + for FILE_MFT. */ + ntfs_inode *mft_ni; /* ntfs_inode structure for FILE_MFT. */ + ntfs_attr *mft_na; /* ntfs_attr structure for the data attribute + of FILE_MFT. */ + ntfs_attr *mftbmp_na; /* ntfs_attr structure for the bitmap attribute + of FILE_MFT. Each bit represents an mft + record in the $DATA attribute, bit 0 + representing mft record 0 and so on. A set + bit means that the mft record is in use and + vice versa. */ - ntfs_inode *secure_ni; /* ntfs_inode structure for FILE $Secure */ - ntfs_index_context *secure_xsii; /* index for using $Secure:$SII */ - ntfs_index_context *secure_xsdh; /* index for using $Secure:$SDH */ - int secure_reentry; /* check for non-rentries */ - unsigned int secure_flags; /* flags, see security.h for values */ + ntfs_inode *secure_ni; /* ntfs_inode structure for FILE $Secure */ + ntfs_index_context *secure_xsii; /* index for using $Secure:$SII */ + ntfs_index_context *secure_xsdh; /* index for using $Secure:$SDH */ + int secure_reentry; /* check for non-rentries */ + unsigned int secure_flags; /* flags, see security.h for values */ - int mftmirr_size; /* Size of the FILE_MFTMirr in mft records. */ - LCN mftmirr_lcn; /* Logical cluster number of the data attribute - for FILE_MFTMirr. */ - ntfs_inode *mftmirr_ni; /* ntfs_inode structure for FILE_MFTMirr. */ - ntfs_attr *mftmirr_na; /* ntfs_attr structure for the data attribute - of FILE_MFTMirr. */ + int mftmirr_size; /* Size of the FILE_MFTMirr in mft records. */ + LCN mftmirr_lcn; /* Logical cluster number of the data attribute + for FILE_MFTMirr. */ + ntfs_inode *mftmirr_ni; /* ntfs_inode structure for FILE_MFTMirr. */ + ntfs_attr *mftmirr_na; /* ntfs_attr structure for the data attribute + of FILE_MFTMirr. */ - ntfschar *upcase; /* Upper case equivalents of all 65536 2-byte - Unicode characters. Obtained from - FILE_UpCase. */ - u32 upcase_len; /* Length in Unicode characters of the upcase - table. */ - ntfschar *locase; /* Lower case equivalents of all 65536 2-byte - Unicode characters. Only if option - case_ignore is set. */ + ntfschar *upcase; /* Upper case equivalents of all 65536 2-byte + Unicode characters. Obtained from + FILE_UpCase. */ + u32 upcase_len; /* Length in Unicode characters of the upcase + table. */ + ntfschar *locase; /* Lower case equivalents of all 65536 2-byte + Unicode characters. Only if option + case_ignore is set. */ - ATTR_DEF *attrdef; /* Attribute definitions. Obtained from - FILE_AttrDef. */ - s32 attrdef_len; /* Size of the attribute definition table in - bytes. */ + ATTR_DEF *attrdef; /* Attribute definitions. Obtained from + FILE_AttrDef. */ + s32 attrdef_len; /* Size of the attribute definition table in + bytes. */ - s64 free_clusters; /* Track the number of free clusters which - greatly improves statfs() performance */ - s64 free_mft_records; /* Same for free mft records (see above) */ - BOOL efs_raw; /* volume is mounted for raw access to - efs-encrypted files */ + s64 free_clusters; /* Track the number of free clusters which + greatly improves statfs() performance */ + s64 free_mft_records; /* Same for free mft records (see above) */ + BOOL efs_raw; /* volume is mounted for raw access to + efs-encrypted files */ #if CACHE_INODE_SIZE - struct CACHE_HEADER *xinode_cache; + struct CACHE_HEADER *xinode_cache; #endif #if CACHE_NIDATA_SIZE - struct CACHE_HEADER *nidata_cache; + struct CACHE_HEADER *nidata_cache; #endif #if CACHE_LOOKUP_SIZE - struct CACHE_HEADER *lookup_cache; + struct CACHE_HEADER *lookup_cache; #endif #if CACHE_SECURID_SIZE - struct CACHE_HEADER *securid_cache; + struct CACHE_HEADER *securid_cache; #endif #if CACHE_LEGACY_SIZE - struct CACHE_HEADER *legacy_cache; + struct CACHE_HEADER *legacy_cache; #endif }; extern const char *ntfs_home; -extern ntfs_volume *ntfs_volume_alloc( void ); +extern ntfs_volume *ntfs_volume_alloc(void); -extern ntfs_volume *ntfs_volume_startup( struct ntfs_device *dev, - unsigned long flags ); +extern ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, + unsigned long flags); -extern ntfs_volume *ntfs_device_mount( struct ntfs_device *dev, - unsigned long flags ); +extern ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, + unsigned long flags); -extern ntfs_volume *ntfs_mount( const char *name, unsigned long flags ); -extern int ntfs_umount( ntfs_volume *vol, const BOOL force ); +extern ntfs_volume *ntfs_mount(const char *name, unsigned long flags); +extern int ntfs_umount(ntfs_volume *vol, const BOOL force); -extern int ntfs_version_is_supported( ntfs_volume *vol ); -extern int ntfs_volume_check_hiberfile( ntfs_volume *vol, int verbose ); -extern int ntfs_logfile_reset( ntfs_volume *vol ); +extern int ntfs_version_is_supported(ntfs_volume *vol); +extern int ntfs_volume_check_hiberfile(ntfs_volume *vol, int verbose); +extern int ntfs_logfile_reset(ntfs_volume *vol); -extern int ntfs_volume_write_flags( ntfs_volume *vol, const le16 flags ); +extern int ntfs_volume_write_flags(ntfs_volume *vol, const le16 flags); -extern int ntfs_volume_error( int err ); -extern void ntfs_mount_error( const char *vol, const char *mntpoint, int err ); +extern int ntfs_volume_error(int err); +extern void ntfs_mount_error(const char *vol, const char *mntpoint, int err); -extern int ntfs_volume_get_free_space( ntfs_volume *vol ); +extern int ntfs_volume_get_free_space(ntfs_volume *vol); -extern int ntfs_set_shown_files( ntfs_volume *vol, - BOOL show_sys_files, BOOL show_hid_files, BOOL hide_dot_files ); -extern int ntfs_set_locale( void ); -extern int ntfs_set_ignore_case( ntfs_volume *vol ); +extern int ntfs_set_shown_files(ntfs_volume *vol, + BOOL show_sys_files, BOOL show_hid_files, BOOL hide_dot_files); +extern int ntfs_set_locale(void); +extern int ntfs_set_ignore_case(ntfs_volume *vol); #endif /* defined _NTFS_VOLUME_H */ diff --git a/source/prompts/TitleBrowser.cpp b/source/prompts/TitleBrowser.cpp index e1709101..58dff465 100644 --- a/source/prompts/TitleBrowser.cpp +++ b/source/prompts/TitleBrowser.cpp @@ -56,7 +56,7 @@ bool TitleSelector( char output[] ) { gprintf("TitleSelector()\n"); - u32 num_titles; + s32 num_titles; s32 r = -1; bool ret = false; u64 *titleList = NULL; @@ -93,7 +93,7 @@ bool TitleSelector( char output[] ) customOptionList options4( num_titles + 1 ); //write the titles on the option browser - u32 i = 0; + s32 i = 0; titles.SetType( 0x10001 ); while ( i < num_titles ) { diff --git a/source/usbloader/GameList.cpp b/source/usbloader/GameList.cpp index 7a61db92..6a91c17c 100644 --- a/source/usbloader/GameList.cpp +++ b/source/usbloader/GameList.cpp @@ -96,10 +96,13 @@ int GameList::ReadGameList() return LoadUnfiltered(); } -static bool WCharSortCallback( const wchar_t char1, const wchar_t char2 ) +static bool WCharSortCallback(const wchar_t char1, const wchar_t char2) { - if( char2 == 0 )return true; - if( char1 == 0 )return false; + if(char2 == 0) + return true; + if(char1 == 0) + return false; + return char2 > char1; } @@ -243,41 +246,41 @@ void GameList::SortList() } -bool GameList::NameSortCallback( const struct discHdr *a, const struct discHdr *b ) +bool GameList::NameSortCallback(const struct discHdr *a, const struct discHdr *b) { - return ( strcasecmp( get_title( ( struct discHdr * ) a ), get_title( ( struct discHdr * ) b ) ) < 0 ); + return (strcasecmp(get_title((struct discHdr *) a), get_title((struct discHdr *) b)) < 0); } -bool GameList::PlaycountSortCallback( const struct discHdr *a, const struct discHdr *b ) +bool GameList::PlaycountSortCallback(const struct discHdr *a, const struct discHdr *b) { struct Game_NUM* game_num1 = CFG_get_game_num( a->id ); struct Game_NUM* game_num2 = CFG_get_game_num( b->id ); int count1 = 0, count2 = 0; - if ( game_num1 ) + if (game_num1) count1 = game_num1->count; - if ( game_num2 ) + if (game_num2) count2 = game_num2->count; - if ( count1 == count2 ) - return NameSortCallback( a, b ); + if (count1 == count2) + return NameSortCallback(a, b); - return ( count1 > count2 ); + return (count1 > count2); } -bool GameList::FavoriteSortCallback( const struct discHdr *a, const struct discHdr *b ) +bool GameList::FavoriteSortCallback(const struct discHdr *a, const struct discHdr *b) { struct Game_NUM* game_num1 = CFG_get_game_num( a->id ); struct Game_NUM* game_num2 = CFG_get_game_num( b->id ); int fav1 = 0, fav2 = 0; - if ( game_num1 ) + if (game_num1) fav1 = game_num1->favorite; - if ( game_num2 ) + if (game_num2) fav2 = game_num2->favorite; - if ( fav1 == fav2 ); - return NameSortCallback( a, b ); + if (fav1 == fav2) + return NameSortCallback(a, b); - return ( fav1 > fav2 ); + return (fav1 > fav2); }