diff --git a/src/add-ons/kernel/file_systems/ntfs/Jamfile b/src/add-ons/kernel/file_systems/ntfs/Jamfile index 315370dd5a..aa82fec037 100644 --- a/src/add-ons/kernel/file_systems/ntfs/Jamfile +++ b/src/add-ons/kernel/file_systems/ntfs/Jamfile @@ -27,12 +27,7 @@ KernelAddon ntfs : libntfs.a ; -SEARCH on [ FGristFiles - kernel_cpp.cpp - ] = [ FDirName $(HAIKU_TOP) src system kernel util ] ; - SEARCH on [ FGristFiles mime_ext_table.c ] += [ FDirName $(HAIKU_TOP) src add-ons kernel file_systems shared ] ; SubInclude HAIKU_TOP src add-ons kernel file_systems ntfs libntfs ; - diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/Jamfile b/src/add-ons/kernel/file_systems/ntfs/libntfs/Jamfile index 3cc7cd8413..423a451a1f 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/Jamfile +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/Jamfile @@ -15,6 +15,7 @@ KernelStaticLibrary libntfs.a : debug.c device.c dir.c + ea.c efs.c index.c inode.c diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/acls.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/acls.c index 76599b4ada..98d8067e57 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/acls.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/acls.c @@ -4,7 +4,7 @@ * This module is part of ntfs-3g library, but may also be * integrated in tools running over Linux or Windows * - * Copyright (c) 2007-2012 Jean-Pierre Andre + * Copyright (c) 2007-2016 Jean-Pierre Andre * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -22,10 +22,6 @@ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifdef HAVE_CONFIG_H - /* - * integration into ntfs-3g - */ #include "config.h" #ifdef HAVE_STDIO_H @@ -53,65 +49,18 @@ #include #include +#ifdef __HAIKU__ +#define getgrgid(a) NULL +#define getpwuid(a) NULL +#define getgrnam(x) NULL +#define getpwnam(x) NULL +#endif + #include "types.h" #include "layout.h" #include "security.h" #include "acls.h" #include "misc.h" -#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) -#define WIN32 1 -#endif -#endif - -#include -#include -#include -#include -#include -#include -#include - - /* - * integration into secaudit/Win32 - */ -#ifdef WIN32 -#include -#include -#define __LITTLE_ENDIAN 1234 -#define __BYTE_ORDER __LITTLE_ENDIAN -#else - /* - * integration into secaudit/STSC - */ -#ifdef STSC -#include -#undef __BYTE_ORDER -#define __BYTE_ORDER __BIG_ENDIAN -#else - /* - * integration into secaudit/Linux - */ -#include -#include -#include -#include -#endif /* STSC */ -#endif /* WIN32 */ -#include "secaudit.h" -#endif /* HAVE_CONFIG_H */ - -#ifdef __HAIKU__ -#define getgrnam(x) NULL -#define getpwnam(x) NULL -#endif /* * A few useful constants @@ -141,6 +90,8 @@ static const char worldsidbytes[] = { 0, 0, 0, 0 /* 1st level */ } ; +const SID *worldsid = (const SID*)worldsidbytes; + /* * SID for authenticated user (S-1-5-11) */ @@ -149,12 +100,10 @@ static const char authsidbytes[] = { 1, /* revision */ 1, /* auth count */ 0, 0, 0, 0, 0, 5, /* base */ - 11, 0, 0, 0 /* 1st level */ + 11, 0, 0, 0 /* 1st level */ }; - -static const SID *authsid = (const SID*)authsidbytes; -const SID *worldsid = (const SID*)worldsidbytes; +static const SID *authsid = (const SID*)authsidbytes; /* * SID for administrator @@ -235,7 +184,11 @@ BOOL ntfs_same_sid(const SID *first, const SID *second) /* * Test whether a SID means "world user" - * Local users group also recognized as world + * Local users group recognized as world + * Also interactive users so that /Users/Public is world accessible, + * but only if Posix ACLs are not enabled (if Posix ACLs are enabled, + * access to /Users/Public should be done by defining interactive users + * as a mapped group.) */ static int is_world_sid(const SID * usid) @@ -259,6 +212,14 @@ static int is_world_sid(const SID * usid) && (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(11))) + +#if !POSIXACLS + /* check whether S-1-5-4 : interactive user */ + || ((usid->sub_authority_count == 1) + && (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(4))) +#endif /* !POSIXACLS */ ); } @@ -276,6 +237,25 @@ BOOL ntfs_is_user_sid(const SID *usid) && (usid->sub_authority[0] == const_cpu_to_le32(21))); } +/* + * Test whether a SID means "some special group" + * Currently we only check for a few S-1-5-n but we should + * probably test for other configurations. + * + * This is useful for granting access to /Users/Public for + * specific users when the Posix ACLs are enabled. + */ + +static BOOL ntfs_known_group_sid(const SID *usid) +{ + /* count == 1 excludes S-1-5-5-X-Y (logon) */ + return ((usid->sub_authority_count == 1) + && (usid->identifier_authority.high_part == const_cpu_to_be16(0)) + && (usid->identifier_authority.low_part == const_cpu_to_be32(5)) + && (le32_to_cpu(usid->sub_authority[0]) >= 1) + && (le32_to_cpu(usid->sub_authority[0]) <= 6)); +} + /* * Determine the size of a security attribute * whatever the order of fields @@ -336,16 +316,18 @@ unsigned int ntfs_attr_size(const char *attr) return (attrsz); } -/* - * Do sanity checks on a SID read from storage - * (just check revision and number of authorities) +/** + * ntfs_valid_sid - determine if a SID 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. */ - BOOL ntfs_valid_sid(const SID *sid) { - return ((sid->revision == SID_REVISION) - && (sid->sub_authority_count >= 1) - && (sid->sub_authority_count <= 8)); + return sid && sid->revision == SID_REVISION && + sid->sub_authority_count <= SID_MAX_SUB_AUTHORITIES; } /* @@ -571,6 +553,7 @@ static BOOL valid_acl(const ACL *pacl, unsigned int end) unsigned int acecnt; unsigned int acesz; unsigned int nace; + unsigned int wantsz; BOOL ok; ok = TRUE; @@ -585,9 +568,16 @@ static BOOL valid_acl(const ACL *pacl, unsigned int end) &((const char*)pacl)[offace]; acesz = le16_to_cpu(pace->size); if (((offace + acesz) > end) - || !ntfs_valid_sid(&pace->sid) - || ((ntfs_sid_size(&pace->sid) + 8) != (int)acesz)) + || !ntfs_valid_sid(&pace->sid)) ok = FALSE; + else { + /* Win10 may insert garbage in the last ACE */ + wantsz = ntfs_sid_size(&pace->sid) + 8; + if (((nace < (acecnt - 1)) + && (wantsz != acesz)) + || (wantsz > acesz)) + ok = FALSE; + } offace += acesz; } } @@ -703,8 +693,10 @@ int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl, const ACCESS_ALLOWED_ACE *poldace; ACCESS_ALLOWED_ACE *pnewace; ACCESS_ALLOWED_ACE *pauthace; + ACCESS_ALLOWED_ACE *pownerace; pauthace = (ACCESS_ALLOWED_ACE*)NULL; + pownerace = (ACCESS_ALLOWED_ACE*)NULL; usidsz = ntfs_sid_size(usid); gsidsz = ntfs_sid_size(gsid); @@ -722,12 +714,24 @@ int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl, poldace = (const ACCESS_ALLOWED_ACE*)((const char*)oldacl + src); acesz = le16_to_cpu(poldace->size); src += acesz; - /* - * Inheritance for access, unless this is inheriting - * an inherited ACL to a directory. - */ + /* + * Extract inheritance for access, including inheritance for + * access from an ACE with is both applied and inheritable. + * + * must not output OBJECT_INHERIT_ACE or CONTAINER_INHERIT_ACE + * + * According to MSDN : + * "For a case in which a container object inherits an ACE + * "that is both effective on the container and inheritable + * "by its descendants, the container may inherit two ACEs. + * "This occurs if the inheritable ACE contains generic + * "information." + */ if ((poldace->flags & selection) - && !(fordir && inherited) + && (!fordir + || (poldace->flags & NO_PROPAGATE_INHERIT_ACE) + || (poldace->mask & (GENERIC_ALL | GENERIC_READ + | GENERIC_WRITE | GENERIC_EXECUTE))) && !ntfs_same_sid(&poldace->sid, ownersid) && !ntfs_same_sid(&poldace->sid, groupsid)) { pnewace = (ACCESS_ALLOWED_ACE*) @@ -750,7 +754,7 @@ int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl, | FILE_READ | FILE_WRITE | FILE_EXEC - | cpu_to_le32(0x40); + | const_cpu_to_le32(0x40); } /* reencode GENERIC_READ (+ EXECUTE) */ if (pnewace->mask & GENERIC_READ) { @@ -790,8 +794,7 @@ int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl, * Group similar ACE for authenticated users * (should probably be done for other SIDs) */ - if (!fordir - && (poldace->type == ACCESS_ALLOWED_ACE_TYPE) + if ((poldace->type == ACCESS_ALLOWED_ACE_TYPE) && ntfs_same_sid(&poldace->sid, authsid)) { if (pauthace) { pauthace->flags |= pnewace->flags; @@ -834,8 +837,14 @@ int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl, | INHERIT_ONLY_ACE); if (inherited) pnewace->flags |= INHERITED_ACE; - dst += usidsz + 8; - newcnt++; + if ((pnewace->type == ACCESS_ALLOWED_ACE_TYPE) + && pownerace + && !(pnewace->flags & ~pownerace->flags)) { + pownerace->mask |= pnewace->mask; + } else { + dst += usidsz + 8; + newcnt++; + } } if (ntfs_same_sid(&pnewace->sid, groupsid)) { memcpy(&pnewace->sid, gsid, gsidsz); @@ -851,15 +860,52 @@ int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl, } } - /* inheritance for further inheritance */ + /* + * inheritance for further inheritance + * + * Situations leading to output CONTAINER_INHERIT_ACE + * or OBJECT_INHERIT_ACE + */ if (fordir && (poldace->flags & (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE))) { pnewace = (ACCESS_ALLOWED_ACE*) ((char*)newacl + dst); memcpy(pnewace,poldace,acesz); + if ((poldace->flags & OBJECT_INHERIT_ACE) + && !(poldace->flags & (CONTAINER_INHERIT_ACE + | NO_PROPAGATE_INHERIT_ACE))) + pnewace->flags |= INHERIT_ONLY_ACE; + if ((poldace->flags & CONTAINER_INHERIT_ACE) + && !(poldace->flags & NO_PROPAGATE_INHERIT_ACE) + && !ntfs_same_sid(&poldace->sid, ownersid) + && !ntfs_same_sid(&poldace->sid, groupsid)) { + if ((poldace->mask & (GENERIC_ALL | GENERIC_READ + | GENERIC_WRITE | GENERIC_EXECUTE))) + pnewace->flags |= INHERIT_ONLY_ACE; + else + pnewace->flags &= ~INHERIT_ONLY_ACE; + } if (inherited) pnewace->flags |= INHERITED_ACE; + /* + * Prepare grouping similar ACE for authenticated users + */ + if ((poldace->type == ACCESS_ALLOWED_ACE_TYPE) + && !pauthace + && !(pnewace->flags & INHERIT_ONLY_ACE) + && ntfs_same_sid(&poldace->sid, authsid)) { + pauthace = pnewace; + } + /* + * Prepare grouping similar ACE for owner + */ + if ((poldace->type == ACCESS_ALLOWED_ACE_TYPE) + && !pownerace + && !(pnewace->flags & INHERIT_ONLY_ACE) + && ntfs_same_sid(&poldace->sid, usid)) { + pownerace = pnewace; + } dst += acesz; newcnt++; } @@ -980,7 +1026,7 @@ BOOL ntfs_valid_posix(const struct POSIX_SECURITY *pxdesc) } } if ((pxdesc->acccnt > 0) - && ((checks[0].owners != 1) || (checks[0].groups != 1) + && ((checks[0].owners != 1) || (checks[0].groups != 1) || (checks[0].others != 1))) ok = FALSE; /* do not check owner, group or other are present in */ @@ -1221,6 +1267,45 @@ struct POSIX_SECURITY *ntfs_replace_acl(const struct POSIX_SECURITY *oldpxdesc, return (newpxdesc); } +/* + * Build a basic Posix ACL from a mode and umask, + * ignoring inheritance from the parent directory + */ + +struct POSIX_SECURITY *ntfs_build_basic_posix( + const struct POSIX_SECURITY *pxdesc __attribute__((unused)), + mode_t mode, mode_t mask, BOOL isdir __attribute__((unused))) +{ + struct POSIX_SECURITY *pydesc; + struct POSIX_ACE *pyace; + + pydesc = (struct POSIX_SECURITY*)malloc( + sizeof(struct POSIX_SECURITY) + 3*sizeof(struct POSIX_ACE)); + if (pydesc) { + pyace = &pydesc->acl.ace[0]; + pyace->tag = POSIX_ACL_USER_OBJ; + pyace->perms = ((mode & ~mask) >> 6) & 7; + pyace->id = -1; + pyace = &pydesc->acl.ace[1]; + pyace->tag = POSIX_ACL_GROUP_OBJ; + pyace->perms = ((mode & ~mask) >> 3) & 7; + pyace->id = -1; + pyace = &pydesc->acl.ace[2]; + pyace->tag = POSIX_ACL_OTHER; + pyace->perms = (mode & ~mask) & 7; + pyace->id = -1; + pydesc->mode = mode; + pydesc->tagsset = POSIX_ACL_USER_OBJ + | POSIX_ACL_GROUP_OBJ + | POSIX_ACL_OTHER; + pydesc->acccnt = 3; + pydesc->defcnt = 0; + pydesc->firstdef = 6; + } else + errno = ENOMEM; + return (pydesc); +} + /* * Build an inherited Posix descriptor from parent * descriptor (if any) restricted to creation mode @@ -1515,7 +1600,7 @@ static BOOL build_user_denials(ACL *pacl, grants = OWNER_RIGHTS; } else { if (pxace->id) { - sid = NTFS_FIND_USID(mapping[MAPUSERS], + sid = ntfs_find_usid(mapping[MAPUSERS], pxace->id, (SID*)&defsid); grants = WORLD_RIGHTS; } else { @@ -1671,7 +1756,7 @@ static BOOL build_user_grants(ACL *pacl, grants = OWNER_RIGHTS; } else { if (pxace->id) { - sid = NTFS_FIND_USID(mapping[MAPUSERS], + sid = ntfs_find_usid(mapping[MAPUSERS], pxace->id, (SID*)&defsid); if (sid) sidsz = ntfs_sid_size(sid); @@ -1757,7 +1842,7 @@ static BOOL build_group_denials_grant(ACL *pacl, sid = gsid; else if (pxace->id) - sid = NTFS_FIND_GSID(mapping[MAPGROUPS], + sid = ntfs_find_gsid(mapping[MAPGROUPS], pxace->id, (SID*)&defsid); else { sid = adminsid; @@ -1906,7 +1991,7 @@ static BOOL build_group_denials_grant(ACL *pacl, * - 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) + * - 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 @@ -2062,7 +2147,7 @@ static int buildacls_posix(struct MAPPING* const mapping[], case POSIX_ACL_USER : pset->designates++; if (pxace->id) { - sid = NTFS_FIND_USID(mapping[MAPUSERS], + sid = ntfs_find_usid(mapping[MAPUSERS], pxace->id, (SID*)&defsid); if (sid && ntfs_same_sid(sid,usid)) pset->selfuserperms |= pxace->perms; @@ -2073,7 +2158,7 @@ static int buildacls_posix(struct MAPPING* const mapping[], case POSIX_ACL_GROUP : pset->designates++; if (pxace->id) { - sid = NTFS_FIND_GSID(mapping[MAPUSERS], + sid = ntfs_find_gsid(mapping[MAPUSERS], pxace->id, (SID*)&defsid); if (sid && ntfs_same_sid(sid,gsid)) pset->selfgrpperms |= pxace->perms; @@ -2193,10 +2278,21 @@ return (0); mapping,flags,pxace,pset); break; - case POSIX_ACL_GROUP : case POSIX_ACL_GROUP_OBJ : + /* denials and grants for group when needed */ + if (pset->groupowns && !pset->adminowns + && (pset->grpperms == pset->othperms) + && !pset->designates && !pset->withmask) { + ok = TRUE; + } else { + ok = build_group_denials_grant(pacl,gsid, + mapping,flags,pxace,pset); + } + break; - /* denials and grants for groups */ + case POSIX_ACL_GROUP : + + /* denials and grants for designated groups */ ok = build_group_denials_grant(pacl,gsid, mapping,flags,pxace,pset); @@ -2453,7 +2549,6 @@ static int buildacls(char *secattr, int offs, mode_t mode, int isdir, /* this ACE will be inserted after denials for group */ if (adminowns - || groupowns || (((mode >> 3) ^ mode) & 7)) { grants = WORLD_RIGHTS; if (isdir) { @@ -2657,10 +2752,10 @@ char *ntfs_build_descr_posix(struct MAPPING* const mapping[], 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 */ + newattrsz += 3*MAX_SID_SIZE; } /* account for default ACE's */ - newattrsz += 2*40*pxdesc->defcnt; /* fixme : maximum size */ + newattrsz += 2*MAX_SID_SIZE*pxdesc->defcnt; newattr = (char*)ntfs_malloc(newattrsz); if (newattr) { /* build the main header part */ @@ -3514,7 +3609,7 @@ static uid_t find_tenant(struct MAPPING *const mapping[], 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); + xid = ntfs_find_user(mapping[MAPUSERS], &pace->sid); if (xid) tid = xid; } offace += le16_to_cpu(pace->size); @@ -3649,13 +3744,13 @@ struct POSIX_SECURITY *ntfs_build_permissions_posix( } else { if (ntfs_same_sid(&pace->sid,usid)) groupowns = TRUE; - gid = NTFS_FIND_GROUP(mapping[MAPGROUPS],&pace->sid); + 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); + uid = ntfs_find_user(mapping[MAPUSERS],&pace->sid); if (uid) { pxace->tag = POSIX_ACL_USER; pxace->id = uid; @@ -3678,7 +3773,7 @@ struct POSIX_SECURITY *ntfs_build_permissions_posix( if (pace->type == ACCESS_ALLOWED_ACE_TYPE) pctx->gotowner = TRUE; if (pctx->gotownermask && !pctx->gotowner) { - uid = NTFS_FIND_USER(mapping[MAPUSERS],&pace->sid); + uid = ntfs_find_user(mapping[MAPUSERS],&pace->sid); pxace->id = uid; pxace->tag = POSIX_ACL_USER; } else @@ -3710,7 +3805,7 @@ struct POSIX_SECURITY *ntfs_build_permissions_posix( pctx->groupmasks++; } else { if (pctx->gotgroup || (pctx->groupmasks > 1)) { - gid = NTFS_FIND_GROUP(mapping[MAPGROUPS],&pace->sid); + gid = ntfs_find_group(mapping[MAPGROUPS],&pace->sid); if (gid) { pxace->id = gid; pxace->tag = POSIX_ACL_GROUP; @@ -3744,7 +3839,7 @@ struct POSIX_SECURITY *ntfs_build_permissions_posix( pxace->id = -1; pxace->tag = POSIX_ACL_SPECIAL; } else { - uid = NTFS_FIND_USER(mapping[MAPUSERS],&pace->sid); + uid = ntfs_find_user(mapping[MAPUSERS],&pace->sid); if (uid) { if ((pace->type == ACCESS_DENIED_ACE_TYPE) && (pace->mask & WRITE_OWNER) @@ -3757,7 +3852,7 @@ struct POSIX_SECURITY *ntfs_build_permissions_posix( } pctx->prevuid = uid; } else { - gid = NTFS_FIND_GROUP(mapping[MAPGROUPS],&pace->sid); + gid = ntfs_find_group(mapping[MAPGROUPS],&pace->sid); if (gid) { if ((pace->type == ACCESS_DENIED_ACE_TYPE) && (pace->mask & WRITE_OWNER) @@ -4005,7 +4100,7 @@ int ntfs_build_permissions(const char *securattr, /* * The following must be in some library... */ -#ifndef __HAIKU__ + static unsigned long atoul(const char *p) { /* must be somewhere ! */ unsigned long v; @@ -4015,7 +4110,6 @@ static unsigned long atoul(const char *p) v = v * 10 + (*p++) - '0'; return (v); } -#endif /* * Build an internal representation of a SID @@ -4050,7 +4144,8 @@ static SID *encodesid(const char *sidstr) cnt++; } bsid->sub_authority_count = cnt; - if ((cnt > 0) && ntfs_valid_sid(bsid) && ntfs_is_user_sid(bsid)) { + if ((cnt > 0) && ntfs_valid_sid(bsid) + && (ntfs_is_user_sid(bsid) || ntfs_known_group_sid(bsid))) { sid = (SID*) ntfs_malloc(4 * cnt + 8); if (sid) memcpy(sid, bsid, 4 * cnt + 8); @@ -4259,6 +4354,12 @@ struct MAPPING *ntfs_do_user_mapping(struct MAPLIST *firstitem) if (uid || (!item->uidstr[0] && !item->gidstr[0])) { sid = encodesid(item->sidstr); + if (sid && ntfs_known_group_sid(sid)) { + ntfs_log_error("Bad user SID %s\n", + item->sidstr); + free(sid); + sid = (SID*)NULL; + } if (sid && !item->uidstr[0] && !item->gidstr[0] && !ntfs_valid_pattern(sid)) { ntfs_log_error("Bad implicit SID pattern %s\n", @@ -4351,7 +4452,15 @@ struct MAPPING *ntfs_do_group_mapping(struct MAPLIST *firstitem) if (mapping) { mapping->sid = sid; mapping->xid = gid; - mapping->grcnt = 0; + /* special groups point to themselves */ + if (ntfs_known_group_sid(sid)) { + mapping->groups = + (gid_t*)&mapping->xid; + mapping->grcnt = 1; + } else + mapping->grcnt = 0; + + mapping->next = (struct MAPPING*)NULL; if (lastmapping) lastmapping->next = mapping; diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/acls.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/acls.h index 4b0838867d..932791e972 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/acls.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/acls.h @@ -24,6 +24,8 @@ #ifndef ACLS_H #define ACLS_H +#include "endians.h" + /* * JPA configuration modes for security.c / acls.c * should be moved to some config file @@ -35,28 +37,6 @@ #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 - */ - -#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)) - -#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 - */ - -#define NTFS_FIND_USID(map,uid,buf) ntfs_find_usid(map,uid,buf) -#define NTFS_FIND_GSID(map,gid,buf) ntfs_find_gsid(map,gid,buf) -#define NTFS_FIND_USER(map,usid) ntfs_find_user(map,usid) -#define NTFS_FIND_GROUP(map,gsid) ntfs_find_group(map,gsid) - - /* * Matching of ntfs permissions to Linux permissions * these constants are adapted to endianness @@ -112,10 +92,11 @@ #define ROOT_GROUP_UNMARK FILE_READ_EA /* ACL granted to root as group */ /* - * A type large enough to hold any SID + * Maximum SID size and a type large enough to hold it */ -typedef char BIGSID[40]; +#define MAX_SID_SIZE (8 + SID_MAX_SUB_AUTHORITIES*4) +typedef char BIGSID[MAX_SID_SIZE]; /* * Struct to hold the input mapping file @@ -170,6 +151,9 @@ 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_build_basic_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); struct POSIX_SECURITY *ntfs_build_permissions_posix( diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/attrib.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/attrib.c index 1a9ef372be..30a8e9cf8a 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/attrib.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/attrib.c @@ -5,7 +5,7 @@ * Copyright (c) 2002-2005 Richard Russon * Copyright (c) 2002-2008 Szabolcs Szakacsits * Copyright (c) 2004-2007 Yura Pakhuchiy - * Copyright (c) 2007-2013 Jean-Pierre Andre + * Copyright (c) 2007-2015 Jean-Pierre Andre * Copyright (c) 2010 Erik Larsson * * This program/include file is free software; you can redistribute it and/or @@ -96,7 +96,7 @@ static void NAttrSetFlag(ntfs_attr *na, FILE_ATTR_FLAGS flag) na->ni->flags |= flag; else ntfs_log_trace("Denied setting flag %d for not unnamed data " - "attribute\n", flag); + "attribute\n", le32_to_cpu(flag)); } static void NAttrClearFlag(ntfs_attr *na, FILE_ATTR_FLAGS flag) @@ -405,7 +405,7 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, le16 cs; ntfs_log_enter("Entering for inode %lld, attr 0x%x.\n", - (unsigned long long)ni->mft_no, type); + (unsigned long long)ni->mft_no, le32_to_cpu(type)); if (!ni || !ni->vol || !ni->mrec) { errno = EINVAL; @@ -452,7 +452,7 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, * does not detect or fix them so we need to cope with it, too. */ if (type == AT_ATTRIBUTE_LIST) - a->flags = 0; + a->flags = const_cpu_to_le16(0); if ((type == AT_DATA) && (a->non_resident ? !a->initialized_size : !a->value_length)) { @@ -484,7 +484,7 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, 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); + le16_to_cpu(a->flags), le32_to_cpu(na->ni->flags)); goto put_err_out; } @@ -494,7 +494,7 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, errno = EIO; ntfs_log_perror("Compressed inode %lld attr 0x%x has " "no compression unit", - (unsigned long long)ni->mft_no, type); + (unsigned long long)ni->mft_no, le32_to_cpu(type)); goto put_err_out; } ntfs_attr_init(na, TRUE, a->flags, @@ -561,7 +561,7 @@ int ntfs_attr_map_runlist(ntfs_attr *na, VCN vcn) 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); + (unsigned long long)na->ni->mft_no, le32_to_cpu(na->type), (long long)vcn); lcn = ntfs_rl_vcn_to_lcn(na->rl, vcn); if (lcn >= 0 || lcn == LCN_HOLE || lcn == LCN_ENOENT) @@ -601,22 +601,19 @@ int ntfs_attr_map_runlist(ntfs_attr *na, VCN vcn) static int ntfs_attr_map_partial_runlist(ntfs_attr *na, VCN vcn) { - LCN lcn; VCN last_vcn; VCN highest_vcn; VCN needed; - VCN existing_vcn; runlist_element *rl; ATTR_RECORD *a; BOOL startseen; ntfs_attr_search_ctx *ctx; + BOOL done; + BOOL newrunlist; - lcn = ntfs_rl_vcn_to_lcn(na->rl, vcn); - if (lcn >= 0 || lcn == LCN_HOLE || lcn == LCN_ENOENT) + if (NAttrFullyMapped(na)) return 0; - existing_vcn = (na->rl ? na->rl->vcn : -1); - ctx = ntfs_attr_get_search_ctx(na->ni, NULL); if (!ctx) return -1; @@ -627,37 +624,56 @@ static int ntfs_attr_map_partial_runlist(ntfs_attr *na, VCN vcn) needed = vcn; highest_vcn = 0; startseen = FALSE; + done = FALSE; + rl = (runlist_element*)NULL; do { + newrunlist = FALSE; /* Find the attribute in the mft record. */ if (!ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE, needed, NULL, 0, ctx)) { a = ctx->attr; - /* Decode and merge the runlist. */ - rl = ntfs_mapping_pairs_decompress(na->ni->vol, a, - na->rl); + /* Decode and merge the runlist. */ + if (ntfs_rl_vcn_to_lcn(na->rl, needed) + == LCN_RL_NOT_MAPPED) { + rl = ntfs_mapping_pairs_decompress(na->ni->vol, + a, na->rl); + newrunlist = TRUE; + } else + rl = na->rl; if (rl) { na->rl = rl; - highest_vcn = le64_to_cpu(a->highest_vcn); - /* corruption detection */ - if (((highest_vcn + 1) < last_vcn) - && ((highest_vcn + 1) <= needed)) { - ntfs_log_error("Corrupt attribute list\n"); - rl = (runlist_element*)NULL; + highest_vcn = sle64_to_cpu(a->highest_vcn); + if (highest_vcn < needed) { + /* corruption detection on unchanged runlists */ + if (newrunlist + && ((highest_vcn + 1) < last_vcn)) { + ntfs_log_error("Corrupt attribute list\n"); + rl = (runlist_element*)NULL; + errno = EIO; + } + done = TRUE; } needed = highest_vcn + 1; if (!a->lowest_vcn) startseen = TRUE; - /* reaching a previously allocated part ? */ - if ((existing_vcn >= 0) - && (needed >= existing_vcn)) { - needed = last_vcn; - } } - } else - rl = (runlist_element*)NULL; - } while (rl && (needed < last_vcn)); + } else { + done = TRUE; + } + } while (rl && !done && (needed < last_vcn)); ntfs_attr_put_search_ctx(ctx); + /* + * Make sure we reached the end, unless the last + * runlist was modified earlier (using HOLES_DELAY + * leads to have a visibility over attributes which + * have not yet been fully updated) + */ + if (done && newrunlist && (needed < last_vcn)) { + ntfs_log_error("End of runlist not reached\n"); + rl = (runlist_element*)NULL; + errno = EIO; + } /* mark fully mapped if we did so */ if (rl && startseen) NAttrSetFullyMapped(na); @@ -685,9 +701,10 @@ int ntfs_attr_map_whole_runlist(ntfs_attr *na) ntfs_volume *vol = na->ni->vol; ATTR_RECORD *a; int ret = -1; + int not_mapped; ntfs_log_enter("Entering for inode %llu, attr 0x%x.\n", - (unsigned long long)na->ni->mft_no, na->type); + (unsigned long long)na->ni->mft_no, le32_to_cpu(na->type)); /* avoid multiple full runlist mappings */ if (NAttrFullyMapped(na)) { @@ -704,7 +721,7 @@ int ntfs_attr_map_whole_runlist(ntfs_attr *na) while (1) { runlist_element *rl; - int not_mapped = 0; + not_mapped = 0; if (ntfs_rl_vcn_to_lcn(na->rl, next_vcn) == LCN_RL_NOT_MAPPED) not_mapped = 1; @@ -759,7 +776,13 @@ int ntfs_attr_map_whole_runlist(ntfs_attr *na) ntfs_log_perror("Couldn't find attribute for runlist mapping"); goto err_out; } - if (highest_vcn && highest_vcn != last_vcn - 1) { + /* + * Cannot check highest_vcn when the last runlist has + * been modified earlier, as runlists and sizes may be + * updated without highest_vcn being in sync, when + * HOLES_DELAY is used + */ + if (not_mapped && 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", @@ -808,7 +831,7 @@ LCN ntfs_attr_vcn_to_lcn(ntfs_attr *na, const VCN vcn) 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); + long)na->ni->mft_no, le32_to_cpu(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); @@ -860,7 +883,7 @@ runlist_element *ntfs_attr_find_vcn(ntfs_attr *na, const VCN vcn) } ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, vcn %llx\n", - (unsigned long long)na->ni->mft_no, na->type, + (unsigned long long)na->ni->mft_no, le32_to_cpu(na->type), (long long)vcn); retry: rl = na->rl; @@ -1017,7 +1040,7 @@ res_err_out: count--; total2++; } else { - *(u16*)((u8*)b+count-2) = cpu_to_le16(efs_padding_length); + *(le16*)((u8*)b+count-2) = cpu_to_le16(efs_padding_length); count -= 2; total2 +=2; } @@ -1153,7 +1176,7 @@ s64 ntfs_attr_pread(ntfs_attr *na, const s64 pos, s64 count, void *b) 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); + le32_to_cpu(na->type), (long long)pos, (long long)count); ret = ntfs_attr_pread_i(na, pos, count, b); @@ -1244,19 +1267,15 @@ static int ntfs_attr_fill_hole(ntfs_attr *na, s64 count, s64 *ofs, /* Map the runlist to be able to update mapping pairs later. */ #if PARTIAL_RUNLIST_UPDATING - if ((!na->rl - || !NAttrDataAppending(na))) { + if (!na->rl) { if (ntfs_attr_map_whole_runlist(na)) goto err_out; } else { - /* make sure the previous non-hole is mapped */ - rlc = *rl; - rlc--; - if (((*rl)->lcn == LCN_HOLE) - && cur_vcn - && (rlc->vcn < 0)) { - if (ntfs_attr_map_partial_runlist(na, cur_vcn - 1)) - goto err_out; + /* make sure the run ahead of hole is mapped */ + if ((*rl)->lcn == LCN_HOLE) { + if (ntfs_attr_map_partial_runlist(na, + (cur_vcn ? cur_vcn - 1 : cur_vcn))) + goto err_out; } } #else @@ -1343,6 +1362,7 @@ static int ntfs_attr_fill_hole(ntfs_attr *na, s64 count, s64 *ofs, na->compressed_size += need << vol->cluster_size_bits; *rl = ntfs_runlists_merge(na->rl, rlc); + NAttrSetRunlistDirty(na); /* * For a compressed attribute, we must be sure there are two * available entries, so reserve them before it gets too late. @@ -1572,6 +1592,7 @@ ntfs_log_error("No free run, case 4\n"); rl = ++(*prl); } } + NAttrSetRunlistDirty(na); if ((*update_from == -1) || ((*prl)->vcn < *update_from)) *update_from = (*prl)->vcn; } @@ -1708,6 +1729,7 @@ static int borrow_from_hole(ntfs_attr *na, runlist_element **prl, zrl->length = endblock - allocated; zrl[1].length -= zrl->length; zrl[1].vcn = zrl->vcn + zrl->length; + NAttrSetRunlistDirty(na); } } if (*prl) { @@ -1758,7 +1780,8 @@ static int ntfs_attr_truncate_i(ntfs_attr *na, const s64 newsize, * 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) +static s64 ntfs_attr_pwrite_i(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; @@ -1776,17 +1799,7 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) } 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); - - 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); @@ -1931,8 +1944,17 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) * However, for compressed file, we need the full compression * block, which may be split in several extents. */ - if (NAttrDataAppending(na)) { - VCN block_begin = pos >> vol->cluster_size_bits; + if (compressed && !NAttrDataAppending(na)) { + if (ntfs_attr_map_whole_runlist(na)) + goto err_out; + } else { + VCN block_begin; + + if (NAttrDataAppending(na) + || (pos < na->initialized_size)) + block_begin = pos >> vol->cluster_size_bits; + else + block_begin = na->initialized_size >> vol->cluster_size_bits; if (compressed) block_begin &= -na->compression_block_clusters; @@ -1942,10 +1964,11 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) goto err_out; if ((update_from == -1) || (block_begin < update_from)) update_from = block_begin; - } else -#endif + } +#else if (ntfs_attr_map_whole_runlist(na)) goto err_out; +#endif /* * For a compressed attribute, we must be sure there is an * available entry, and, when reopening a compressed file, @@ -2192,7 +2215,7 @@ retry: } else written = ntfs_pwrite(vol->dev, wpos, to_write, b); - } + } } else written = to_write; /* If everything ok, update progress counters and continue. */ @@ -2222,13 +2245,7 @@ done: * of the mapping list. This makes a difference only if * inode extents were needed. */ -#if PARTIAL_RUNLIST_UPDATING - updatemap = NAttrFullyMapped(na) || NAttrDataAppending(na); -#else - updatemap = (compressed - ? NAttrFullyMapped(na) != 0 : update_from != -1); -#endif - if (updatemap) { + if (NAttrRunlistDirty(na)) { if (ntfs_attr_update_mapping_pairs(na, (update_from < 0 ? 0 : update_from))) { /* @@ -2243,7 +2260,6 @@ done: NAttrClearDataAppending(na); } out: - ntfs_log_leave("\n"); return total; rl_err_out: eo = errno; @@ -2298,9 +2314,7 @@ err_out: if (ctx) ntfs_attr_put_search_ctx(ctx); /* Update mapping pairs if needed. */ - updatemap = (compressed - ? NAttrFullyMapped(na) != 0 : update_from != -1); - if (updatemap) + if (NAttrRunlistDirty(na)) ntfs_attr_update_mapping_pairs(na, 0); /* Restore original data_size if needed. */ if (need_to.undo_data_size @@ -2312,6 +2326,39 @@ errno_set: goto out; } +s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) +{ + s64 total; + s64 written; + + ntfs_log_enter("Entering for inode %lld, attr 0x%x, pos 0x%llx, count " + "0x%llx.\n", (long long)na->ni->mft_no, le32_to_cpu(na->type), + (long long)pos, (long long)count); + + total = 0; + if (!na || !na->ni || !na->ni->vol || !b || pos < 0 || count < 0) { + errno = EINVAL; + written = -1; + ntfs_log_perror("%s", __FUNCTION__); + goto out; + } + + /* + * Compressed attributes may be written partially, so + * we may have to iterate. + */ + do { + written = ntfs_attr_pwrite_i(na, pos + total, + count - total, (const u8*)b + total); + if (written > 0) + total += written; + } while ((written > 0) && (total < count)); +out : + ntfs_log_leave("\n"); + return (total > 0 ? total : written); +} + + int ntfs_attr_pclose(ntfs_attr *na) { s64 ofs; @@ -2326,7 +2373,8 @@ int ntfs_attr_pclose(ntfs_attr *na) BOOL compressed; ntfs_log_enter("Entering for inode 0x%llx, attr 0x%x.\n", - na->ni->mft_no, na->type); + (unsigned long long)na->ni->mft_no, + le32_to_cpu(na->type)); if (!na || !na->ni || !na->ni->vol) { errno = EINVAL; @@ -2545,7 +2593,7 @@ s64 ntfs_attr_mst_pread(ntfs_attr *na, const s64 pos, const s64 bk_cnt, BOOL warn; 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, + (unsigned long long)na->ni->mft_no, le32_to_cpu(na->type), (long long)pos); if (bk_cnt < 0 || bk_size % NTFS_BLOCK_SIZE) { errno = EINVAL; @@ -2601,7 +2649,7 @@ s64 ntfs_attr_mst_pwrite(ntfs_attr *na, const s64 pos, s64 bk_cnt, 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, + (unsigned long long)na->ni->mft_no, le32_to_cpu(na->type), (long long)pos); if (bk_cnt < 0 || bk_size % NTFS_BLOCK_SIZE) { errno = EINVAL; @@ -2719,7 +2767,7 @@ static int ntfs_attr_find(const ATTR_TYPES type, const ntfschar *name, ntfschar *upcase; u32 upcase_len; - ntfs_log_trace("attribute type 0x%x.\n", type); + ntfs_log_trace("attribute type 0x%x.\n", le32_to_cpu(type)); if (ctx->ntfs_ino) { vol = ctx->ntfs_ino->vol; @@ -2944,7 +2992,7 @@ static int ntfs_external_attr_find(ATTR_TYPES type, const ntfschar *name, 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); + (unsigned long long)ni->mft_no, le32_to_cpu(type)); if (!base_ni) { /* First call happens with the base mft record. */ base_ni = ctx->base_ntfs_ino = ctx->ntfs_ino; @@ -3327,7 +3375,7 @@ int ntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name, ntfs_inode *base_ni; int ret = -1; - ntfs_log_enter("Entering for attribute type 0x%x\n", type); + ntfs_log_enter("Entering for attribute type 0x%x\n", le32_to_cpu(type)); if (!ctx || !ctx->mrec || !ctx->attr || (name && name != AT_UNNAMED && (!ctx->ntfs_ino || !(vol = ctx->ntfs_ino->vol) || @@ -3495,7 +3543,7 @@ ATTR_DEF *ntfs_attr_find_in_attrdef(const ntfs_volume *vol, if (!vol || !vol->attrdef || !type) { errno = EINVAL; - ntfs_log_perror("%s: type=%d", __FUNCTION__, type); + ntfs_log_perror("%s: type=%d", __FUNCTION__, le32_to_cpu(type)); return NULL; } for (ad = vol->attrdef; (u8*)ad - (u8*)vol->attrdef < @@ -3510,7 +3558,7 @@ ATTR_DEF *ntfs_attr_find_in_attrdef(const ntfs_volume *vol, break; } errno = ENOENT; - ntfs_log_perror("%s: type=%d", __FUNCTION__, type); + ntfs_log_perror("%s: type=%d", __FUNCTION__, le32_to_cpu(type)); return NULL; } @@ -3571,7 +3619,7 @@ int ntfs_attr_size_bounds_check(const ntfs_volume *vol, const ATTR_TYPES type, ((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, + "%lld,%lld,%lld)", le32_to_cpu(type), (long long)min_size, (long long)size, (long long)max_size); return -1; } @@ -3751,7 +3799,7 @@ int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, 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); + (long long) ni->mft_no, (unsigned) le32_to_cpu(type), (unsigned) le16_to_cpu(data_flags)); if (!ni || (!name && name_len)) { errno = EINVAL; @@ -3805,7 +3853,7 @@ int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, 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(offsetof(ATTR_RECORD, resident_end)) : const_cpu_to_le16(0)); a->flags = data_flags; a->instance = m->next_attr_instance; @@ -3884,8 +3932,8 @@ int ntfs_non_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, 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); + (long long) ni->mft_no, (unsigned) le32_to_cpu(type), + (long long) lowest_vcn, dataruns_size, (unsigned) le16_to_cpu(flags)); if (!ni || dataruns_size <= 0 || (!name && name_len)) { errno = EINVAL; @@ -3912,7 +3960,7 @@ int ntfs_non_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, if (!ntfs_attr_find(type, name, name_len, CASE_SENSITIVE, NULL, 0, ctx)) { err = EEXIST; - ntfs_log_perror("Attribute 0x%x already present", type); + ntfs_log_perror("Attribute 0x%x already present", le32_to_cpu(type)); goto put_err_out; } if (errno != ENOENT) { @@ -3951,10 +3999,10 @@ int ntfs_non_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, ? 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; + a->highest_vcn = const_cpu_to_sle64(-1); + a->allocated_size = const_cpu_to_sle64(0); + a->data_size = const_cpu_to_sle64(0); + a->initialized_size = const_cpu_to_sle64(0); /* Set empty mapping pairs. */ *((u8*)a + le16_to_cpu(a->mapping_pairs_offset)) = 0; } @@ -4173,7 +4221,7 @@ int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type, } ntfs_log_trace("Entering for inode %lld, attr %x, size %lld.\n", - (long long)ni->mft_no, type, (long long)size); + (long long)ni->mft_no, le32_to_cpu(type), (long long)size); if (ni->nr_extents == -1) ni = ni->base_ni; @@ -4395,7 +4443,7 @@ int ntfs_attr_rm(ntfs_attr *na) } ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", - (long long) na->ni->mft_no, na->type); + (long long) na->ni->mft_no, le32_to_cpu(na->type)); /* Free cluster allocation. */ if (NAttrNonResident(na)) { @@ -4743,7 +4791,7 @@ int ntfs_attr_make_non_resident(ntfs_attr *na, 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); + long)na->ni->mft_no, le32_to_cpu(na->type)); /* Some preliminary sanity checking. */ if (NAttrNonResident(na)) { @@ -4851,7 +4899,7 @@ int ntfs_attr_make_non_resident(ntfs_attr *na, a->name_offset = cpu_to_le16(name_ofs); /* Setup the fields specific to non-resident attributes. */ - a->lowest_vcn = cpu_to_sle64(0); + a->lowest_vcn = const_cpu_to_sle64(0); a->highest_vcn = cpu_to_sle64((new_allocated_size - 1) >> vol->cluster_size_bits); @@ -4868,7 +4916,7 @@ int ntfs_attr_make_non_resident(ntfs_attr *na, 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); + a->compressed_size = const_cpu_to_sle64(0); } else { a->compression_unit = 0; a->flags &= ~ATTR_COMPRESSION_MASK; @@ -4937,7 +4985,7 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize, 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, + (unsigned long long)na->ni->mft_no, le32_to_cpu(na->type), (long long)newsize); /* Get the attribute record that needs modification. */ @@ -5094,6 +5142,10 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize, */ if (na->type==AT_STANDARD_INFORMATION || na->type==AT_ATTRIBUTE_LIST) { ntfs_attr_put_search_ctx(ctx); + if (!NInoAttrList(na->ni) && ntfs_inode_add_attrlist(na->ni)) { + ntfs_log_perror("Could not add attribute list"); + return -1; + } if (ntfs_inode_free_space(na->ni, offsetof(ATTR_RECORD, non_resident_end) + 8)) { ntfs_log_perror("Could not free space in MFT record"); @@ -5240,7 +5292,7 @@ static int ntfs_attr_make_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx) 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); + long)na->ni->mft_no, le32_to_cpu(na->type)); /* Should be called for the first extent of the attribute. */ if (sle64_to_cpu(a->lowest_vcn)) { @@ -5314,7 +5366,7 @@ static int ntfs_attr_make_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx) /* Convert the attribute record to describe a resident attribute. */ a->non_resident = 0; - a->flags = 0; + a->flags = const_cpu_to_le16(0); a->value_length = cpu_to_le32(na->data_size); a->value_offset = cpu_to_le16(val_ofs); /* @@ -5402,7 +5454,7 @@ static int ntfs_attr_update_meta(ATTR_RECORD *a, ntfs_attr *na, MFT_RECORD *m, 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); + (unsigned long long)na->ni->mft_no, le32_to_cpu(na->type)); if (a->lowest_vcn) goto out; @@ -5545,7 +5597,7 @@ retry: } ntfs_log_trace("Entering for inode %llu, attr 0x%x\n", - (unsigned long long)na->ni->mft_no, na->type); + (unsigned long long)na->ni->mft_no, le32_to_cpu(na->type)); if (!NAttrNonResident(na)) { errno = EINVAL; @@ -5847,8 +5899,12 @@ retry: 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); + /* Allocate new mft record, with special case for mft itself */ + if (!na->ni->mft_no) + ni = ntfs_mft_rec_alloc(na->ni->vol, + na->type == AT_DATA); + else + 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; @@ -5903,6 +5959,7 @@ retry: break; } ok: + NAttrClearRunlistDirty(na); ret = 0; out: return ret; @@ -5969,7 +6026,7 @@ static int ntfs_non_resident_attr_shrink(ntfs_attr *na, const s64 newsize) 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); + na->ni->mft_no, le32_to_cpu(na->type), (long long)newsize); vol = na->ni->vol; @@ -6030,6 +6087,7 @@ static int ntfs_non_resident_attr_shrink(ntfs_attr *na, const s64 newsize) ntfs_log_trace("Eeek! Run list truncation failed.\n"); return -1; } + NAttrSetRunlistDirty(na); /* Prepare to mapping pairs update. */ na->allocated_size = first_free_vcn << vol->cluster_size_bits; @@ -6080,7 +6138,8 @@ static int ntfs_non_resident_attr_shrink(ntfs_attr *na, const s64 newsize) /* If the attribute now has zero size, make it resident. */ if (!newsize) { - if (ntfs_attr_make_resident(na, ctx)) { + if (!(na->data_flags & ATTR_IS_ENCRYPTED) + && ntfs_attr_make_resident(na, ctx)) { /* If couldn't make resident, just continue. */ if (errno != EPERM) ntfs_log_error("Failed to make attribute " @@ -6125,7 +6184,7 @@ static int ntfs_non_resident_attr_expand_i(ntfs_attr *na, const s64 newsize, 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, + (unsigned long long)na->ni->mft_no, le32_to_cpu(na->type), (long long)newsize, (long long)na->data_size); vol = na->ni->vol; @@ -6245,6 +6304,7 @@ static int ntfs_non_resident_attr_expand_i(ntfs_attr *na, const s64 newsize, return -1; } na->rl = rln; + NAttrSetRunlistDirty(na); /* Prepare to mapping pairs update. */ na->allocated_size = first_free_vcn << vol->cluster_size_bits; @@ -6330,6 +6390,7 @@ rollback: na->rl = NULL; ntfs_log_perror("Couldn't truncate runlist. Rollback failed"); } else { + NAttrSetRunlistDirty(na); /* Prepare to mapping pairs update. */ na->allocated_size = org_alloc_size; /* Restore mapping pairs. */ @@ -6394,7 +6455,7 @@ static int ntfs_attr_truncate_i(ntfs_attr *na, const s64 newsize, } ntfs_log_enter("Entering for inode %lld, attr 0x%x, size %lld\n", - (unsigned long long)na->ni->mft_no, na->type, + (unsigned long long)na->ni->mft_no, le32_to_cpu(na->type), (long long)newsize); if (na->data_size == newsize) { @@ -6406,7 +6467,7 @@ static int ntfs_attr_truncate_i(ntfs_attr *na, const s64 newsize, * Encrypted attributes are not supported. We return access denied, * which is what Windows NT4 does, too. */ - if (na->data_flags & ATTR_IS_ENCRYPTED) { + if ((na->data_flags & ATTR_IS_ENCRYPTED) && !na->ni->vol->efs_raw) { errno = EACCES; ntfs_log_trace("Cannot truncate encrypted attribute\n"); goto out; @@ -6703,6 +6764,42 @@ exit: return res; } +/* + * Shrink the size of a data attribute if needed + * + * For non-resident attributes only. + * The space remains allocated. + * + * Returns 0 if successful + * -1 if failed, with errno telling why + */ + + +int ntfs_attr_shrink_size(ntfs_inode *ni, ntfschar *stream_name, + int stream_name_len, off_t offset) +{ + ntfs_attr_search_ctx *ctx; + ATTR_RECORD *a; + int res; + + res = -1; + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (ctx) { + if (!ntfs_attr_lookup(AT_DATA, stream_name, stream_name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + a = ctx->attr; + + if (a->non_resident + && (sle64_to_cpu(a->initialized_size) > offset)) { + a->initialized_size = cpu_to_sle64(offset); + a->data_size = a->initialized_size; + } + res = 0; + } + ntfs_attr_put_search_ctx(ctx); + } + return (res); +} int ntfs_attr_exist(ntfs_inode *ni, const ATTR_TYPES type, const ntfschar *name, u32 name_len) @@ -6743,7 +6840,7 @@ int ntfs_attr_remove(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name, /* 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); + "0x%llx", le32_to_cpu(type), (unsigned long long)ni->mft_no); } return -1; } @@ -6751,7 +6848,7 @@ int ntfs_attr_remove(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name, 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); + "0x%llx", le32_to_cpu(type), (unsigned long long)ni->mft_no); ntfs_attr_close(na); return ret; diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/attrib.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/attrib.h index a0bee2652a..b3752a608b 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/attrib.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/attrib.h @@ -211,6 +211,7 @@ typedef enum { NA_FullyMapped, /* 1: Attribute has been fully mapped */ NA_DataAppending, /* 1: Attribute is being appended to */ NA_ComprClosing, /* 1: Compressed attribute is being closed */ + NA_RunlistDirty, /* 1: Runlist has been updated */ } ntfs_attr_state_bits; #define test_nattr_flag(na, flag) test_bit(NA_##flag, (na)->state) @@ -237,6 +238,10 @@ typedef enum { #define NAttrSetDataAppending(na) set_nattr_flag(na, DataAppending) #define NAttrClearDataAppending(na) clear_nattr_flag(na, DataAppending) +#define NAttrRunlistDirty(na) test_nattr_flag(na, RunlistDirty) +#define NAttrSetRunlistDirty(na) set_nattr_flag(na, RunlistDirty) +#define NAttrClearRunlistDirty(na) clear_nattr_flag(na, RunlistDirty) + #define NAttrComprClosing(na) test_nattr_flag(na, ComprClosing) #define NAttrSetComprClosing(na) set_nattr_flag(na, ComprClosing) #define NAttrClearComprClosing(na) clear_nattr_flag(na, ComprClosing) @@ -391,6 +396,8 @@ extern int ntfs_attr_data_read(ntfs_inode *ni, extern int ntfs_attr_data_write(ntfs_inode *ni, ntfschar *stream_name, int stream_name_len, const char *buf, size_t size, off_t offset); +extern int ntfs_attr_shrink_size(ntfs_inode *ni, ntfschar *stream_name, + int stream_name_len, off_t offset); #endif /* defined _NTFS_ATTRIB_H */ diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/attrlist.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/attrlist.c index 9c62f31614..29d4085980 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/attrlist.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/attrlist.c @@ -107,7 +107,7 @@ int ntfs_attrlist_need(ntfs_inode *ni) int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr) { ATTR_LIST_ENTRY *ale; - MFT_REF mref; + leMFT_REF mref; ntfs_attr *na = NULL; ntfs_attr_search_ctx *ctx; u8 *new_al; @@ -150,7 +150,7 @@ int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr) 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) : + (attr->non_resident) ? sle64_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)) { @@ -193,7 +193,7 @@ int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr) if (attr->non_resident) ale->lowest_vcn = attr->lowest_vcn; else - ale->lowest_vcn = 0; + ale->lowest_vcn = const_cpu_to_sle64(0); ale->mft_reference = mref; ale->instance = attr->instance; memcpy(ale->name, (u8 *)attr + le16_to_cpu(attr->name_offset), @@ -265,7 +265,7 @@ int ntfs_attrlist_entry_rm(ntfs_attr_search_ctx *ctx) 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)); + (long long) sle64_to_cpu(ctx->al_entry->lowest_vcn)); if (!NInoAttrList(base_ni)) { ntfs_log_trace("Attribute list isn't present.\n"); diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/bootsect.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/bootsect.c index e9be072d0a..e185fe3fec 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/bootsect.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/bootsect.c @@ -65,7 +65,7 @@ BOOL ntfs_boot_sector_is_ntfs(NTFS_BOOT_SECTOR *b) ntfs_log_debug("Beginning bootsector check.\n"); ntfs_log_debug("Checking OEMid, NTFS signature.\n"); - if (b->oem_id != cpu_to_le64(0x202020205346544eULL)) { /* "NTFS " */ + if (b->oem_id != const_cpu_to_le64(0x202020205346544eULL)) { /* "NTFS " */ ntfs_log_error("NTFS signature is missing.\n"); goto not_ntfs; } @@ -140,7 +140,7 @@ BOOL ntfs_boot_sector_is_ntfs(NTFS_BOOT_SECTOR *b) } } - if (b->end_of_sector_marker != cpu_to_le16(0xaa55)) + if (b->end_of_sector_marker != const_cpu_to_le16(0xaa55)) ntfs_log_debug("Warning: Bootsector has invalid end of sector " "marker.\n"); @@ -204,7 +204,7 @@ int ntfs_boot_sector_parse(ntfs_volume *vol, const NTFS_BOOT_SECTOR *bs) (sectors - 1) << vol->sector_size_bits, SEEK_SET) == -1) { ntfs_log_perror("Failed to read last sector (%lld)", - (long long)sectors); + (long long)(sectors - 1)); ntfs_log_error("%s", last_sector_error); return -1; } diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/compat.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/compat.h index cc84670f88..ded6c5f853 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/compat.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/compat.h @@ -37,6 +37,10 @@ #define ENODATA ENOENT #endif +#ifndef ELIBBAD +#define ELIBBAD ENOEXEC +#endif + #ifndef PATH_MAX #define PATH_MAX 4096 #endif @@ -65,11 +69,9 @@ extern char *strsep(char **stringp, const char *delim); #else /* !defined WINDOWS */ -#ifndef __HAIKU__ #ifndef O_BINARY #define O_BINARY 0 /* unix is binary by default */ #endif -#endif #endif /* defined WINDOWS */ diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/compress.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/compress.c index 69b39ed09b..2c0c260a14 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/compress.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/compress.c @@ -5,7 +5,8 @@ * Copyright (c) 2004-2005 Anton Altaparmakov * Copyright (c) 2004-2006 Szabolcs Szakacsits * Copyright (c) 2005 Yura Pakhuchiy - * Copyright (c) 2009-2013 Jean-Pierre Andre + * Copyright (c) 2009-2014 Jean-Pierre Andre + * Copyright (c) 2014 Eric Biggers * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -21,17 +22,6 @@ * along with this program (in the main directory of the NTFS-3G * distribution in the file COPYING); if not, write to the Free Software * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * A part of the compression algorithm is based on lzhuf.c whose header - * describes the roles of the original authors (with no apparent copyright - * notice, and according to http://home.earthlink.net/~neilbawd/pall.html - * this was put into public domain in 1988 by Haruhiko OKUMURA). - * - * LZHUF.C English version 1.0 - * 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 */ #ifdef HAVE_CONFIG_H @@ -81,96 +71,191 @@ typedef enum { NTFS_SB_IS_COMPRESSED = 0x8000, } ntfs_compression_constants; +/* Match length at or above which ntfs_best_match() will stop searching for + * longer matches. */ +#define NICE_MATCH_LEN 18 + +/* Maximum number of potential matches that ntfs_best_match() will consider at + * each position. */ +#define MAX_SEARCH_DEPTH 24 + +/* log base 2 of the number of entries in the hash table for match-finding. */ +#define HASH_SHIFT 14 + +/* Constant for the multiplicative hash function. */ +#define HASH_MULTIPLIER 0x1E35A7BD + struct COMPRESS_CONTEXT { const unsigned char *inbuf; int bufsize; int size; int rel; int mxsz; - s16 head[256]; - s16 lson[NTFS_SB_SIZE]; - s16 rson[NTFS_SB_SIZE]; + s16 head[1 << HASH_SHIFT]; + s16 prev[NTFS_SB_SIZE]; } ; +/* + * Hash the next 3-byte sequence in the input buffer + */ +static inline unsigned int ntfs_hash(const u8 *p) +{ + u32 str; + u32 hash; + +#if defined(__i386__) || defined(__x86_64__) + /* Unaligned access allowed, and little endian CPU. + * Callers ensure that at least 4 (not 3) bytes are remaining. */ + str = *(const u32 *)p & 0xFFFFFF; +#else + str = ((u32)p[0] << 0) | ((u32)p[1] << 8) | ((u32)p[2] << 16); +#endif + + hash = str * HASH_MULTIPLIER; + + /* High bits are more random than the low bits. */ + return hash >> (32 - HASH_SHIFT); +} + /* * Search for the longest sequence matching current position * - * A binary tree is maintained to locate all previously met sequences, - * and this function has to be called for all of them. + * A hash table, each entry of which points to a chain of sequence + * positions sharing the corresponding hash code, is maintained to speed up + * searching for matches. To maintain the hash table, either + * ntfs_best_match() or ntfs_skip_position() has to be called for each + * consecutive position. * - * This function is heavily used, it has to be optimized carefully + * This function is heavily used; it has to be optimized carefully. * - * Returns the size of the longest match, - * zero if no match is found. + * This function sets pctx->size and pctx->rel to the length and offset, + * respectively, of the longest match found. + * + * The minimum match length is assumed to be 3, and the maximum match + * length is assumed to be pctx->mxsz. If this function produces + * pctx->size < 3, then no match was found. + * + * Note: for the following reasons, this function is not guaranteed to find + * *the* longest match up to pctx->mxsz: + * + * (1) If this function finds a match of NICE_MATCH_LEN bytes or greater, + * it ends early because a match this long is good enough and it's not + * worth spending more time searching. + * + * (2) If this function considers MAX_SEARCH_DEPTH matches with a single + * position, it ends early and returns the longest match found so far. + * This saves a lot of time on degenerate inputs. */ - -static int ntfs_best_match(struct COMPRESS_CONTEXT *pctx, int i) +static void ntfs_best_match(struct COMPRESS_CONTEXT *pctx, const int i, + int best_len) { - s16 *prev; - int node; - register long j; - long maxpos; - long startj; - long bestj; - int bufsize; - int bestnode; - register const unsigned char *p1,*p2; + const u8 * const inbuf = pctx->inbuf; + const u8 * const strptr = &inbuf[i]; /* String we're matching against */ + s16 * const prev = pctx->prev; + const int max_len = min(pctx->bufsize - i, pctx->mxsz); + const int nice_len = min(NICE_MATCH_LEN, max_len); + int depth_remaining = MAX_SEARCH_DEPTH; + const u8 *best_matchptr = strptr; + unsigned int hash; + s16 cur_match; + const u8 *matchptr; + int len; - p1 = pctx->inbuf; - node = pctx->head[p1[i] & 255]; - if (node >= 0) { - /* search the best match at current position */ - bestnode = node; - bufsize = pctx->bufsize; - /* restrict matches to the longest allowed sequence */ - maxpos = bufsize; - if ((i + pctx->mxsz) < maxpos) - maxpos = i + pctx->mxsz; - startj = i + 1 - maxpos; - bestj = startj; - /* make indexes relative to end of allowed position */ - p1 = &p1[maxpos]; - if (startj < 0) { - do { - /* indexes are negative */ - p2 = &p1[node - i]; - /* no need to compare the first byte */ - j = startj; - /* the second byte cannot lead to useful compression */ - if (p1[j] == p2[j]) { - j++; - if (j < 0) { - do { - } while ((p1[j] == p2[j]) - && (++j < 0)); - } - /* remember the match, if better */ - if (j > bestj) { - bestj = j; - bestnode = node; - } - } - /* walk in the tree in the right direction */ - if ((j < 0) && (p1[j] < p2[j])) - prev = &pctx->lson[node]; - else - prev = &pctx->rson[node]; - node = *prev; - /* stop if reaching a leaf or maximum length */ - } while ((node >= 0) && (j < 0)); - /* put the node into the tree if we reached a leaf */ - if (node < 0) - *prev = i; - } - /* done, return the best match */ - pctx->size = bestj + maxpos - i; - pctx->rel = bestnode - i; - } else { - pctx->head[p1[i] & 255] = i; - pctx->size = 0; - pctx->rel = 0; + if (max_len < 4) + goto out; + + /* Insert the current sequence into the appropriate hash chain. */ + hash = ntfs_hash(strptr); + cur_match = pctx->head[hash]; + prev[i] = cur_match; + pctx->head[hash] = i; + + if (best_len >= max_len) { + /* Lazy match is being attempted, but there aren't enough length + * bits remaining to code a longer match. */ + goto out; } - return (pctx->size); + + /* Search the appropriate hash chain for matches. */ + + for (; cur_match >= 0 && depth_remaining--; + cur_match = prev[cur_match]) + { + + matchptr = &inbuf[cur_match]; + + /* Considering the potential match at 'matchptr': is it longer + * than 'best_len'? + * + * The bytes at index 'best_len' are the most likely to differ, + * so check them first. + * + * The bytes at indices 'best_len - 1' and '0' are less + * important to check separately. But doing so still gives a + * slight performance improvement, at least on x86_64, probably + * because they create separate branches for the CPU to predict + * independently of the branches in the main comparison loops. + */ + if (matchptr[best_len] != strptr[best_len] || + matchptr[best_len - 1] != strptr[best_len - 1] || + matchptr[0] != strptr[0]) + goto next_match; + + for (len = 1; len < best_len - 1; len++) + if (matchptr[len] != strptr[len]) + goto next_match; + + /* The match is the longest found so far --- + * at least 'best_len' + 1 bytes. Continue extending it. */ + + best_matchptr = matchptr; + + do { + if (++best_len >= nice_len) { + /* 'nice_len' reached; don't waste time + * searching for longer matches. Extend the + * match as far as possible and terminate the + * search. */ + while (best_len < max_len && + (best_matchptr[best_len] == + strptr[best_len])) + { + best_len++; + } + goto out; + } + } while (best_matchptr[best_len] == strptr[best_len]); + + /* Found a longer match, but 'nice_len' not yet reached. */ + + next_match: + /* Continue to next match in the chain. */ + ; + } + + /* Reached end of chain, or ended early due to reaching the maximum + * search depth. */ + +out: + /* Return the longest match we were able to find. */ + pctx->size = best_len; + pctx->rel = best_matchptr - strptr; /* given as a negative number! */ +} + +/* + * Advance the match-finder, but don't search for matches. + */ +static void ntfs_skip_position(struct COMPRESS_CONTEXT *pctx, const int i) +{ + unsigned int hash; + + if (pctx->bufsize - i < 4) + return; + + /* Insert the current sequence into the appropriate hash chain. */ + hash = ntfs_hash(pctx->inbuf + i); + pctx->prev[i] = pctx->head[hash]; + pctx->head[hash] = i; } /* @@ -188,7 +273,7 @@ static int ntfs_best_match(struct COMPRESS_CONTEXT *pctx, int i) * 0 if an error has been met. */ -static unsigned int ntfs_compress_block(const char *inbuf, int bufsize, +static unsigned int ntfs_compress_block(const char *inbuf, const int bufsize, char *outbuf) { struct COMPRESS_CONTEXT *pctx; @@ -196,109 +281,168 @@ static unsigned int ntfs_compress_block(const char *inbuf, int bufsize, int j; /* end of best match from current position */ int k; /* end of best match from next position */ int offs; /* offset to best match */ - int n; int bp; /* bits to store offset */ + int bp_cur; /* saved bits to store offset at current position */ int mxoff; /* max match offset : 1 << bp */ - int mxsz2; unsigned int xout; unsigned int q; /* aggregated offset and size */ - int done; + int have_match; /* do we have a match at the current position? */ char *ptag; /* location reserved for a tag */ int tag; /* current value of tag */ int ntag; /* count of bits still undefined in tag */ - pctx = (struct COMPRESS_CONTEXT*)ntfs_malloc(sizeof(struct COMPRESS_CONTEXT)); - if (pctx) { - for (n=0; nlson[n] = pctx->rson[n] = -1; - for (n=0; n<256; n++) - pctx->head[n] = -1; - pctx->inbuf = (const unsigned char*)inbuf; - pctx->bufsize = bufsize; - xout = 2; - n = 0; - i = 0; - bp = 4; - mxoff = 1 << bp; - pctx->mxsz = (1 << (16 - bp)) + 2; - tag = 0; - done = -1; - ntag = 8; - ptag = &outbuf[xout++]; - while ((i < bufsize) && (xout < (NTFS_SB_SIZE + 2))) { - /* adjust the longest match we can output */ + pctx = ntfs_malloc(sizeof(struct COMPRESS_CONTEXT)); + if (!pctx) { + errno = ENOMEM; + return 0; + } + + /* All hash chains start as empty. The special value '-1' indicates the + * end of each hash chain. */ + memset(pctx->head, 0xFF, sizeof(pctx->head)); + + pctx->inbuf = (const unsigned char*)inbuf; + pctx->bufsize = bufsize; + xout = 2; + i = 0; + bp = 4; + mxoff = 1 << bp; + pctx->mxsz = (1 << (16 - bp)) + 2; + have_match = 0; + tag = 0; + ntag = 8; + ptag = &outbuf[xout++]; + + while ((i < bufsize) && (xout < (NTFS_SB_SIZE + 2))) { + + /* This implementation uses "lazy" parsing: it always chooses + * the longest match, unless the match at the next position is + * longer. This is the same strategy used by the high + * compression modes of zlib. */ + + if (!have_match) { + /* Find the longest match at the current position. But + * first adjust the maximum match length if needed. + * (This loop might need to run more than one time in + * the case that we just output a long match.) */ while (mxoff < i) { bp++; mxoff <<= 1; pctx->mxsz = (pctx->mxsz + 2) >> 1; } - /* search the best match at current position */ - if (done < i) - do { - ntfs_best_match(pctx,++done); - } while (done < i); - j = i + pctx->size; - if ((j - i) > pctx->mxsz) - j = i + pctx->mxsz; + ntfs_best_match(pctx, i, 2); + } - if ((j - i) > 2) { - offs = pctx->rel; - /* check whether there is a better run at i+1 */ - ntfs_best_match(pctx,i+1); - done = i+1; + if (pctx->size >= 3) { + + /* Found a match at the current position. */ + + j = i + pctx->size; + bp_cur = bp; + offs = pctx->rel; + + if (pctx->size >= NICE_MATCH_LEN) { + + /* Choose long matches immediately. */ + + q = (~offs << (16 - bp_cur)) + (j - i - 3); + outbuf[xout++] = q & 255; + outbuf[xout++] = (q >> 8) & 255; + tag |= (1 << (8 - ntag)); + + if (j == bufsize) { + /* Shortcut if the match extends to the + * end of the buffer. */ + i = j; + --ntag; + break; + } + i += 1; + do { + ntfs_skip_position(pctx, i); + } while (++i != j); + have_match = 0; + } else { + /* Check for a longer match at the next + * position. */ + + /* Doesn't need to be while() since we just + * adjusted the maximum match length at the + * previous position. */ + if (mxoff < i + 1) { + bp++; + mxoff <<= 1; + pctx->mxsz = (pctx->mxsz + 2) >> 1; + } + ntfs_best_match(pctx, i + 1, pctx->size); k = i + 1 + pctx->size; - mxsz2 = pctx->mxsz; - if (mxoff <= i) - mxsz2 = (pctx->mxsz + 2) >> 1; - if ((k - i) > mxsz2) - k = i + mxsz2; + if (k > (j + 1)) { - /* issue a single byte */ - outbuf[xout++] = inbuf[i]; - i++; + /* Next match is longer. + * Output a literal. */ + outbuf[xout++] = inbuf[i++]; + have_match = 1; } else { - q = (~offs << (16 - bp)) - + (j - i - 3); + /* Next match isn't longer. + * Output the current match. */ + q = (~offs << (16 - bp_cur)) + + (j - i - 3); outbuf[xout++] = q & 255; outbuf[xout++] = (q >> 8) & 255; tag |= (1 << (8 - ntag)); - i = j; + + /* The minimum match length is 3, and + * we've run two bytes through the + * matchfinder already. So the minimum + * number of positions we need to skip + * is 1. */ + i += 2; + do { + ntfs_skip_position(pctx, i); + } while (++i != j); + have_match = 0; } - } else { - outbuf[xout++] = inbuf[i]; - i++; } - /* store the tag if fully used */ - if (!--ntag) { - *ptag = tag; - ntag = 8; - ptag = &outbuf[xout++]; - tag = 0; - } - } - /* store the last tag, if partially used */ - if (ntag == 8) - xout--; - else - *ptag = tag; - /* uncompressed must be full size, accept if better */ - if ((i >= bufsize) && (xout < (NTFS_SB_SIZE + 2))) { - outbuf[0] = (xout - 3) & 255; - outbuf[1] = 0xb0 + (((xout - 3) >> 8) & 15); } else { - memcpy(&outbuf[2],inbuf,bufsize); - if (bufsize < NTFS_SB_SIZE) - memset(&outbuf[bufsize+2], 0, - NTFS_SB_SIZE - bufsize); - outbuf[0] = 0xff; - outbuf[1] = 0x3f; - xout = NTFS_SB_SIZE + 2; + /* No match at current position. Output a literal. */ + outbuf[xout++] = inbuf[i++]; + have_match = 0; + } + + /* Store the tag if fully used. */ + if (!--ntag) { + *ptag = tag; + ntag = 8; + ptag = &outbuf[xout++]; + tag = 0; } - free(pctx); - } else { - xout = 0; - errno = ENOMEM; } + + /* Store the last tag if partially used. */ + if (ntag == 8) + xout--; + else + *ptag = tag; + + /* Determine whether to store the data compressed or uncompressed. */ + + if ((i >= bufsize) && (xout < (NTFS_SB_SIZE + 2))) { + /* Compressed. */ + outbuf[0] = (xout - 3) & 255; + outbuf[1] = 0xb0 + (((xout - 3) >> 8) & 15); + } else { + /* Uncompressed. */ + memcpy(&outbuf[2], inbuf, bufsize); + if (bufsize < NTFS_SB_SIZE) + memset(&outbuf[bufsize + 2], 0, NTFS_SB_SIZE - bufsize); + outbuf[0] = 0xff; + outbuf[1] = 0x3f; + xout = NTFS_SB_SIZE + 2; + } + + /* Free the compression context and return the total number of bytes + * written to 'outbuf'. */ + free(pctx); return (xout); } @@ -575,7 +719,7 @@ s64 ntfs_compressed_attr_pread(ntfs_attr *na, s64 pos, s64 count, void *b) 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, + (unsigned long long)na->ni->mft_no, le32_to_cpu(na->type), (long long)pos, (long long)count); data_flags = na->data_flags; compression = na->ni->flags & FILE_ATTR_COMPRESSED; @@ -987,6 +1131,7 @@ static s32 ntfs_comp_set(ntfs_attr *na, runlist_element *rl, outbuf[compsz++] = 0; /* write a full cluster, to avoid partial reading */ rounded = ((compsz - 1) | (clsz - 1)) + 1; + memset(&outbuf[compsz], 0, rounded - compsz); written = write_clusters(vol, rl, offs, rounded, outbuf); if (written != rounded) { /* @@ -1417,6 +1562,7 @@ static int ntfs_compress_free(ntfs_attr *na, runlist_element *rl, ntfs_log_error("No cluster to free after compression\n"); errno = EIO; } + NAttrSetRunlistDirty(na); return (res); } diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/config.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/config.h index 6e58ab7283..ada5673c0c 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/config.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/config.h @@ -1,27 +1,70 @@ +/* Haiku-specific stuff */ +#ifdef __HAIKU__ + +/* major part of a device */ +#define major(x) (int)((unsigned)((x)>>8)&0x7F) +#define bmajor(x) (int)((unsigned)((x)>>8)&0x7F) + +/* minor part of a device */ +#define minor(x) (int)((x)&0xFF) +#endif + +/* config.h. Generated from config.h.in by configure. */ /* config.h.in. Generated from configure.ac by autoheader. */ +/* Define if building universal (internal helper macro) */ +/* #undef AC_APPLE_UNIVERSAL_BUILD */ + +/* Define to 1 for disabling reparse plugins */ +/* #undef DISABLE_PLUGINS */ + +/* Define this to 1 if you want to enable support of encrypted files in + libntfs and utilities. */ +/* #undef ENABLE_CRYPTO */ + +/* Define to 1 if debug should be enabled */ +/* #undef ENABLE_DEBUG */ + +/* Define this to 1 if you want to enable use of Windows compliant disk + geometry. */ +/* #undef ENABLE_HD */ + +/* Define to 1 if the nfconv patch should be enabled */ +/* #undef ENABLE_NFCONV */ + +/* Define this to 1 if you want to enable generation of DCE compliant UUIDs. + */ +/* #undef ENABLE_UUID */ + +/* Define to 1 if using internal fuse */ +/* #undef FUSE_INTERNAL */ + /* Define to 1 if you have the `atexit' function. */ -#undef HAVE_ATEXIT +#define HAVE_ATEXIT 1 /* Define to 1 if you have the `basename' function. */ -#undef HAVE_BASENAME +#define HAVE_BASENAME 1 /* Define to 1 if you have the header file. */ -#undef HAVE_BYTESWAP_H +/* #undef HAVE_BYTESWAP_H */ + +/* Define to 1 if you have the `clock_gettime' function. */ +/* #undef HAVE_CLOCK_GETTIME */ /* Define to 1 if you have the header file. */ #define HAVE_CTYPE_H 1 +/* Define to 1 if you have the `daemon' function. */ #define HAVE_DAEMON 1 /* Define to 1 if you have the header file. */ -#undef HAVE_DLFCN_H +#define HAVE_DLFCN_H 1 /* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ -#undef HAVE_DOPRNT +/* #undef HAVE_DOPRNT */ /* Define to 1 if you have the `dup2' function. */ -#undef HAVE_DUP2 +#define HAVE_DUP2 1 /* Define to 1 if you have the header file. */ #define HAVE_ENDIAN_H 1 @@ -33,103 +76,128 @@ #define HAVE_FCNTL_H 1 /* Define to 1 if you have the `fdatasync' function. */ -#undef HAVE_FDATASYNC +/* #undef HAVE_FDATASYNC */ /* Define to 1 if you have the header file. */ -#undef HAVE_FEATURES_H +/* #undef HAVE_FEATURES_H */ -/* Define to 1 if you have the header file. */ -#undef HAVE_GCRYPT_H +/* Define to 1 if you have the `ffs' function. */ +#define HAVE_FFS 1 + +/* Define to 1 if you have the `fork' function. */ +/* #undef HAVE_FORK */ /* Define to 1 if you have the `getmntent' function. */ -#undef HAVE_GETMNTENT +/* #undef HAVE_GETMNTENT */ /* Define to 1 if you have the header file. */ -#undef HAVE_GETOPT_H +#define HAVE_GETOPT_H 1 /* Define to 1 if you have the `getopt_long' function. */ -#undef HAVE_GETOPT_LONG +#define HAVE_GETOPT_LONG 1 -/* Define to 1 if you have the header file. */ -#undef HAVE_GNUTLS_PKCS12_H +/* Define to 1 if you have the `gettimeofday' function. */ +#define HAVE_GETTIMEOFDAY 1 /* Define to 1 if you have the `hasmntopt' function. */ -#undef HAVE_HASMNTOPT +/* #undef HAVE_HASMNTOPT */ /* Define to 1 if you have the header file. */ -#undef HAVE_INTTYPES_H +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `c' library (-lc). */ +/* #undef HAVE_LIBC */ /* Define to 1 if you have the header file. */ -#undef HAVE_LIBGEN_H +#define HAVE_LIBGEN_H 1 /* Define to 1 if you have the header file. */ -#undef HAVE_LIBINTL_H +/* #undef HAVE_LIBINTL_H */ /* Define to 1 if you have the header file. */ -#undef HAVE_LIMITS_H +#define HAVE_LIMITS_H 1 /* Define to 1 if you have the header file. */ -#undef HAVE_LINUX_FD_H +/* #undef HAVE_LINUX_FD_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_LINUX_FS_H */ /* Define to 1 if you have the header file. */ -#undef HAVE_LINUX_HDREG_H +/* #undef HAVE_LINUX_HDREG_H */ /* Define to 1 if you have the header file. */ -#undef HAVE_LINUX_MAJOR_H +/* #undef HAVE_LINUX_MAJOR_H */ /* Define to 1 if you have the header file. */ #define HAVE_LOCALE_H 1 /* Define to 1 if you have the header file. */ -#undef HAVE_MACHINE_ENDIAN_H +/* #undef HAVE_MACHINE_ENDIAN_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_MALLOC_H 1 /* Define to 1 if mbrtowc and mbstate_t are properly declared. */ -#undef HAVE_MBRTOWC #define HAVE_MBRTOWC 1 /* Define to 1 if you have the `mbsinit' function. */ -#undef HAVE_MBSINIT -//#define HAVE_MBSINIT 1 +/* #undef HAVE_MBSINIT */ + +/* Define to 1 if you have the `memcpy' function. */ +#define HAVE_MEMCPY 1 /* Define to 1 if you have the `memmove' function. */ -#undef HAVE_MEMMOVE +#define HAVE_MEMMOVE 1 /* Define to 1 if you have the header file. */ -#undef HAVE_MEMORY_H +#define HAVE_MEMORY_H 1 /* Define to 1 if you have the `memset' function. */ -#undef HAVE_MEMSET +#define HAVE_MEMSET 1 /* Define to 1 if you have the header file. */ -#undef HAVE_MNTENT_H +/* #undef HAVE_MNTENT_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_PWD_H 1 + +/* Define to 1 if you have the `random' function. */ +#define HAVE_RANDOM 1 /* Define to 1 if you have the `realpath' function. */ -#undef HAVE_REALPATH +#define HAVE_REALPATH 1 /* Define to 1 if you have the `regcomp' function. */ -#undef HAVE_REGCOMP +#define HAVE_REGCOMP 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_REGEX_H 1 /* Define to 1 if you have the `setlocale' function. */ -#undef HAVE_SETLOCALE +/* #undef HAVE_SETLOCALE */ /* Define to 1 if you have the `setxattr' function. */ -#undef HAVE_SETXATTR +/* #undef HAVE_SETXATTR */ + +/* Define to 1 if you have the `snprintf' function. */ +#define HAVE_SNPRINTF 1 /* Define to 1 if `stat' has the bug that it succeeds when given the zero-length file name argument. */ -#undef HAVE_STAT_EMPTY_STRING_BUG +/* #undef HAVE_STAT_EMPTY_STRING_BUG */ /* Define to 1 if you have the header file. */ #define HAVE_STDARG_H 1 /* Define to 1 if stdbool.h conforms to C99. */ -#undef HAVE_STDBOOL_H +/* #undef HAVE_STDBOOL_H */ /* Define to 1 if you have the header file. */ -#undef HAVE_STDDEF_H +#define HAVE_STDDEF_H 1 /* Define to 1 if you have the header file. */ -#define HAVE_STDINT_H 1 +#define HAVE_STDINT_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STDIO_H 1 @@ -138,182 +206,254 @@ #define HAVE_STDLIB_H 1 /* Define to 1 if you have the `strcasecmp' function. */ -#undef HAVE_STRCASECMP +#define HAVE_STRCASECMP 1 /* Define to 1 if you have the `strchr' function. */ -#undef HAVE_STRCHR +#define HAVE_STRCHR 1 /* Define to 1 if you have the `strdup' function. */ -#undef HAVE_STRDUP +#define HAVE_STRDUP 1 /* Define to 1 if you have the `strerror' function. */ -#undef HAVE_STRERROR +#define HAVE_STRERROR 1 /* Define to 1 if you have the `strftime' function. */ -#undef HAVE_STRFTIME +#define HAVE_STRFTIME 1 /* Define to 1 if you have the header file. */ -#undef HAVE_STRINGS_H +#define HAVE_STRINGS_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STRING_H 1 /* Define to 1 if you have the `strnlen' function. */ -#undef HAVE_STRNLEN +#define HAVE_STRNLEN 1 + +/* Define to 1 if you have the `strsep' function. */ +/* #undef HAVE_STRSEP */ /* Define to 1 if you have the `strtol' function. */ -#undef HAVE_STRTOL +#define HAVE_STRTOL 1 /* Define to 1 if you have the `strtoul' function. */ -#undef HAVE_STRTOUL +#define HAVE_STRTOUL 1 -/* Define to 1 if `st_blocks' is member of `struct stat'. */ -#undef HAVE_STRUCT_STAT_ST_BLOCKS +/* Define to 1 if `st_atim' is a member of `struct stat'. */ +#define HAVE_STRUCT_STAT_ST_ATIM 1 -/* Define to 1 if `st_rdev' is member of `struct stat'. */ -#undef HAVE_STRUCT_STAT_ST_RDEV +/* Define to 1 if `st_atimensec' is a member of `struct stat'. */ +/* #undef HAVE_STRUCT_STAT_ST_ATIMENSEC */ + +/* Define to 1 if `st_atimespec' is a member of `struct stat'. */ +/* #undef HAVE_STRUCT_STAT_ST_ATIMESPEC */ + +/* Define to 1 if `st_blocks' is a member of `struct stat'. */ +#define HAVE_STRUCT_STAT_ST_BLOCKS 1 + +/* Define to 1 if `st_rdev' is a member of `struct stat'. */ +#define HAVE_STRUCT_STAT_ST_RDEV 1 /* Define to 1 if your `struct stat' has `st_blocks'. Deprecated, use `HAVE_STRUCT_STAT_ST_BLOCKS' instead. */ -#undef HAVE_ST_BLOCKS +#define HAVE_ST_BLOCKS 1 /* Define to 1 if you have the `sysconf' function. */ -#undef HAVE_SYSCONF +#define HAVE_SYSCONF 1 /* Define to 1 if you have the header file. */ -#undef HAVE_SYSLOG_H +/* #undef HAVE_SYSLOG_H */ /* Define to 1 if you have the header file. */ -#undef HAVE_SYS_BYTEORDER_H +/* #undef HAVE_SYS_BYTEORDER_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_DISK_H */ /* Define to 1 if you have the header file. */ -#undef HAVE_SYS_ENDIAN_H +/* #undef HAVE_SYS_ENDIAN_H */ /* Define to 1 if you have the header file. */ -#undef HAVE_SYS_IOCTL_H +#define HAVE_SYS_IOCTL_H 1 /* Define to 1 if you have the header file. */ -#undef HAVE_SYS_MOUNT_H +/* #undef HAVE_SYS_MOUNT_H */ /* Define to 1 if you have the header file. */ -#undef HAVE_SYS_PARAM_H +#define HAVE_SYS_PARAM_H 1 /* Define to 1 if you have the header file. */ -#undef HAVE_SYS_STATVFS_H +#define HAVE_SYS_STATVFS_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_STAT_H 1 -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_SYSMACROS_H - /* Define to 1 if you have the header file. */ #define HAVE_SYS_TYPES_H 1 /* Define to 1 if you have the header file. */ -#undef HAVE_SYS_VFS_H +/* #undef HAVE_SYS_VFS_H */ /* Define to 1 if you have the header file. */ -#undef HAVE_TIME_H #define HAVE_TIME_H 1 /* Define to 1 if you have the header file. */ -#undef HAVE_UNISTD_H #define HAVE_UNISTD_H 1 /* Define to 1 if you have the `utime' function. */ -#undef HAVE_UTIME +#define HAVE_UTIME 1 + +/* Define to 1 if you have the `utimensat' function. */ +#define HAVE_UTIMENSAT 1 /* Define to 1 if you have the header file. */ -#undef HAVE_UTIME_H +#define HAVE_UTIME_H 1 /* Define to 1 if `utime(file, NULL)' sets file's timestamp to the present. */ -#undef HAVE_UTIME_NULL +#define HAVE_UTIME_NULL 1 /* Define to 1 if you have the `vprintf' function. */ -#undef HAVE_VPRINTF #define HAVE_VPRINTF 1 /* Define to 1 if you have the header file. */ -#undef HAVE_WCHAR_H #define HAVE_WCHAR_H 1 /* Define to 1 if you have the header file. */ -#undef HAVE_WINDOWS_H +/* #undef HAVE_WINDOWS_H */ /* Define to 1 if the system has the type `_Bool'. */ -#undef HAVE__BOOL +/* #undef HAVE__BOOL */ + +/* Don't update /etc/mtab */ +/* #undef IGNORE_MTAB */ /* Define to 1 if `lstat' dereferences a symlink specified with a trailing slash. */ -#undef LSTAT_FOLLOWS_SLASHED_SYMLINK +/* #undef LSTAT_FOLLOWS_SLASHED_SYMLINK */ + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define LT_OBJDIR ".libs/" + +/* Define to 1 if `major', `minor', and `makedev' are declared in . + */ +/* #undef MAJOR_IN_MKDEV */ + +/* Define to 1 if `major', `minor', and `makedev' are declared in + . */ +/* #undef MAJOR_IN_SYSMACROS */ + +/* Don't use default IO ops */ +/* #undef NO_NTFS_DEVICE_DEFAULT_IO_OPS */ /* Name of package */ -#undef PACKAGE +#define PACKAGE "ntfs-3g" /* Define to the address where bug reports for this package should be sent. */ -#undef PACKAGE_BUGREPORT +#define PACKAGE_BUGREPORT "" /* Define to the full name of this package. */ -#undef PACKAGE_NAME +#define PACKAGE_NAME "" /* Define to the full name and version of this package. */ -#undef PACKAGE_STRING +#define PACKAGE_STRING "" /* Define to the one symbol short name of this package. */ -#undef PACKAGE_TARNAME +#define PACKAGE_TARNAME "" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" /* Define to the version of this package. */ -#undef PACKAGE_VERSION +#define PACKAGE_VERSION "2017.3.23" + +/* POSIX ACL support */ +/* #undef POSIXACLS */ /* Define to 1 if you have the ANSI C header files. */ -#undef STDC_HEADERS +#define STDC_HEADERS 1 + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# define _ALL_SOURCE 1 +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# define _POSIX_PTHREAD_SEMANTICS 1 +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# define _TANDEM_SOURCE 1 +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# define __EXTENSIONS__ 1 +#endif + /* Version number of package */ -#undef VERSION +#define VERSION "2017.3.23" -/* Define to 1 if your processor stores words with the most significant byte - first (like Motorola and SPARC, unlike Intel and VAX). */ -#undef WORDS_BIGENDIAN +/* Define to 1 if this is a Windows OS */ +/* #undef WINDOWS */ + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +/* # undef WORDS_BIGENDIAN */ +# endif +#endif /* Define to 1 if your processor stores words with the least significant byte first (like Intel and VAX, unlike Motorola and SPARC). */ -#undef WORDS_LITTLEENDIAN +#define WORDS_LITTLEENDIAN 1 -/* Number of bits in a file offset, on hosts where this is settable. */ -#undef _FILE_OFFSET_BITS +/* system extended attributes mappings */ +/* #undef XATTR_MAPPINGS */ -/* Enable GNU extensions on systems that have them. */ -#ifndef _GNU_SOURCE -# undef _GNU_SOURCE +/* Enable large inode numbers on Mac OS X 10.5. */ +#ifndef _DARWIN_USE_64_BIT_INODE +# define _DARWIN_USE_64_BIT_INODE 1 #endif +/* Number of bits in a file offset, on hosts where this is settable. */ +/* #undef _FILE_OFFSET_BITS */ + /* Define for large files, on AIX-style hosts. */ -#undef _LARGE_FILES +/* #undef _LARGE_FILES */ + +/* Define to 1 if on MINIX. */ +/* #undef _MINIX */ + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +/* #undef _POSIX_1_SOURCE */ + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +/* #undef _POSIX_SOURCE */ + +/* Required define if using POSIX threads */ +/* #undef _REENTRANT */ /* Define to empty if `const' does not conform to ANSI C. */ -#undef const +/* #undef const */ /* Define to `__inline__' or `__inline' if that's what the C compiler calls it, or to nothing if 'inline' is not supported under any name. */ #ifndef __cplusplus -#undef inline +/* #undef inline */ #endif -/* Define to `long' if does not define. */ -#undef off_t +/* Define to `long int' if does not define. */ +/* #undef off_t */ -/* Define to `unsigned' if does not define. */ -#undef size_t - -#define LOGGING_DISABLE 1 - -/* major part of a device */ -#define major(x) (int)((unsigned)((x)>>8)&0x7F) -#define bmajor(x) (int)((unsigned)((x)>>8)&0x7F) - -/* minor part of a device */ -#define minor(x) (int)((x)&0xFF) - -/* make a device number */ -#define makedev(x,y) (dev_t)(((x)<<8) | (y)) +/* Define to `unsigned int' if does not define. */ +/* #undef size_t */ diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/device.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/device.c index 12ca14d1cc..b58c8026ad 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/device.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/device.c @@ -211,14 +211,14 @@ s64 ntfs_pread(struct ntfs_device *dev, const s64 pos, s64 count, void *b) struct ntfs_device_operations *dops; 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; for (total = 0; count; count -= br, total += br) { @@ -273,7 +273,7 @@ s64 ntfs_pwrite(struct ntfs_device *dev, const s64 pos, s64 count, errno = EROFS; goto out; } - + dops = dev->d_ops; NDevSetDirty(dev); @@ -296,7 +296,7 @@ s64 ntfs_pwrite(struct ntfs_device *dev, const s64 pos, s64 count, total--; /* on sync error, return partially written */ } ret = total; -out: +out: return ret; } @@ -605,18 +605,19 @@ s64 ntfs_device_size_get(struct ntfs_device *dev, int block_size) partition_info partitionInfo; device_geometry geometry; - + if (dev->d_ops->ioctl(dev, B_GET_PARTITION_INFO, &partitionInfo) == 0) size = partitionInfo.size; else if (dev->d_ops->ioctl(dev, B_GET_GEOMETRY, &geometry) == 0) { size = (off_t)geometry.cylinder_count * geometry.sectors_per_track * geometry.head_count * geometry.bytes_per_sector; } - + if (size > 0) return (s64)size / block_size; } -#endif + #endif + /* * We couldn't figure it out by using a specialized ioctl, * so do binary search to find the size of the device. diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/device_io.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/device_io.h index 24f8d9bfc4..66ad2439fb 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/device_io.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/device_io.h @@ -72,7 +72,7 @@ struct hd_geometry { /* A few useful functions */ int ntfs_win32_set_sparse(int); int ntfs_win32_ftruncate(int fd, s64 size); -int ntfs_win32_device_ftruncate(struct ntfs_device*, s64); +int ntfs_device_win32_ftruncate(struct ntfs_device*, s64); #endif /* HAVE_WINDOWS_H */ diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/dir.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/dir.c index a13b121315..b6c7566ee1 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/dir.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/dir.c @@ -5,7 +5,7 @@ * Copyright (c) 2004-2005 Richard Russon * Copyright (c) 2004-2008 Szabolcs Szakacsits * Copyright (c) 2005-2007 Yura Pakhuchiy - * Copyright (c) 2008-2010 Jean-Pierre Andre + * Copyright (c) 2008-2014 Jean-Pierre Andre * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -40,7 +40,13 @@ #include #endif -#ifdef HAVE_SYS_SYSMACROS_H +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef MAJOR_IN_MKDEV +#include +#endif +#ifdef MAJOR_IN_SYSMACROS #include #endif @@ -61,10 +67,7 @@ #include "security.h" #include "reparse.h" #include "object_id.h" - -#ifdef HAVE_SETXATTR -#include -#endif +#include "xattrs.h" /* * The little endian Unicode strings "$I30", "$SII", "$SDH", "$O" @@ -313,7 +316,7 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, */ 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); @@ -384,7 +387,7 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, } /* Get the starting vcn of the index_block holding the child node. */ - vcn = sle64_to_cpup((u8*)ie + le16_to_cpu(ie->length) - 8); + vcn = sle64_to_cpup((sle64*)((u8*)ie + le16_to_cpu(ie->length) - 8)); descend_into_child_node: @@ -441,7 +444,7 @@ descend_into_child_node: (u8*)ie + le16_to_cpu(ie->key_length) > index_end) { ntfs_log_error("Index entry out of bounds in directory " - "inode %lld.\n", + "inode %lld.\n", (unsigned long long)dir_ni->mft_no); errno = EIO; goto close_err_out; @@ -452,7 +455,7 @@ descend_into_child_node: */ 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" @@ -496,7 +499,7 @@ descend_into_child_node: goto close_err_out; } /* Child node present, descend into it. */ - vcn = sle64_to_cpup((u8*)ie + le16_to_cpu(ie->length) - 8); + vcn = sle64_to_cpup((sle64*)((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 " @@ -681,9 +684,9 @@ ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent, errno = EINVAL; return NULL; } - + ntfs_log_trace("path: '%s'\n", pathname); - + ascii = strdup(pathname); if (!ascii) { ntfs_log_error("Out of memory.\n"); @@ -818,7 +821,7 @@ ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent, err = EIO; goto close; } - + free(unicode); unicode = NULL; @@ -875,7 +878,7 @@ typedef enum { * and most metadata files have such similar patters. */ -static u32 ntfs_interix_types(ntfs_inode *ni) +u32 ntfs_interix_types(ntfs_inode *ni) { ntfs_attr *na; u32 dt_type; @@ -884,8 +887,14 @@ static u32 ntfs_interix_types(ntfs_inode *ni) dt_type = NTFS_DT_UNKNOWN; na = ntfs_attr_open(ni, AT_DATA, NULL, 0); if (na) { - /* Unrecognized patterns (eg HID + SYST) are plain files */ - dt_type = NTFS_DT_REG; + /* + * Unrecognized patterns (eg HID + SYST for metadata) + * are plain files or directories + */ + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + dt_type = NTFS_DT_DIR; + else + dt_type = NTFS_DT_REG; if (na->data_size <= 1) { if (!(ni->flags & FILE_ATTR_HIDDEN)) dt_type = (na->data_size ? @@ -974,7 +983,7 @@ static int ntfs_filldir(ntfs_inode *dir_ni, s64 *pos, u8 ivcn_bits, MFT_REF mref; 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( @@ -1058,7 +1067,7 @@ MFT_REF ntfs_mft_get_parent_ref(ntfs_inode *ni) int eo; ntfs_log_trace("Entering.\n"); - + if (!ni) { errno = EINVAL; return ERR_MREF(-1); @@ -1068,7 +1077,7 @@ MFT_REF ntfs_mft_get_parent_ref(ntfs_inode *ni) 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", + ntfs_log_error("No file name found in inode %lld\n", (unsigned long long)ni->mft_no); goto err_out; } @@ -1129,7 +1138,7 @@ int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, u8 index_block_size_bits, index_vcn_size_bits; ntfs_log_trace("Entering.\n"); - + if (!dir_ni || !pos || !filldir) { errno = EINVAL; return -1; @@ -1249,10 +1258,10 @@ int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, /* 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; @@ -1405,10 +1414,10 @@ find_next_index_buffer: /* 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; @@ -1495,19 +1504,19 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, 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; @@ -1534,7 +1543,7 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, si->last_access_time = ni->last_access_time; if (securid) { set_nino_flag(ni, v3_Extensions); - ni->owner_id = si->owner_id = 0; + ni->owner_id = si->owner_id = const_cpu_to_le32(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); @@ -1599,15 +1608,15 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, ni->vol->indx_record_size >> ni->vol->cluster_size_bits; else - ir->clusters_per_index_block = + ir->clusters_per_index_block = ni->vol->indx_record_size >> NTFS_BLOCK_SIZE_BITS; - ir->index.entries_offset = cpu_to_le32(sizeof(INDEX_HEADER)); + ir->index.entries_offset = const_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->length = const_cpu_to_le16(sizeof(INDEX_ENTRY_HEADER)); + ie->key_length = const_cpu_to_le16(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, @@ -1694,7 +1703,7 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, 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); + fn->data_size = fn->allocated_size = const_cpu_to_sle64(0); else { fn->data_size = cpu_to_sle64(ni->data_size); fn->allocated_size = cpu_to_sle64(ni->allocated_size); @@ -1714,7 +1723,7 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, goto err_out; } /* Set hard links count and directory flag. */ - ni->mrec->link_count = cpu_to_le16(1); + ni->mrec->link_count = const_cpu_to_le16(1); if (S_ISDIR(type)) ni->mrec->flags |= MFT_RECORD_IS_DIRECTORY; ntfs_inode_mark_dirty(ni); @@ -1728,7 +1737,7 @@ err_out: 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); /* @@ -1793,7 +1802,7 @@ int ntfs_check_empty_dir(ntfs_inode *ni) { ntfs_attr *na; int ret = 0; - + if (!(ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) return 0; @@ -1803,7 +1812,7 @@ int ntfs_check_empty_dir(ntfs_inode *ni) 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. */ @@ -1811,7 +1820,7 @@ int ntfs_check_empty_dir(ntfs_inode *ni) ntfs_log_debug("Directory is not empty\n"); ret = -1; } - + ntfs_attr_close(na); return ret; } @@ -1820,23 +1829,23 @@ 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) || + 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: +no_hardlink: return ret; } @@ -1875,7 +1884,7 @@ int ntfs_delete(ntfs_volume *vol, const char *pathname, #endif ntfs_log_trace("Entering.\n"); - + if (!ni || !dir_ni || !name || !name_len) { ntfs_log_error("Invalid arguments.\n"); errno = EINVAL; @@ -1895,20 +1904,23 @@ int ntfs_delete(ntfs_volume *vol, const char *pathname, if (!actx) goto err_out; search: - while (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE, - 0, NULL, 0, actx)) { + while (!(err = ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, + CASE_SENSITIVE, 0, NULL, 0, actx))) { + #ifdef DEBUG char *s; + #endif IGNORE_CASE_BOOL case_sensitive = IGNORE_CASE; - errno = 0; fn = (FILE_NAME_ATTR*)((u8*)actx->attr + le16_to_cpu(actx->attr->value_offset)); + #ifdef DEBUG 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); + #endif if (looking_for_dos_name) { if (fn->file_name_type == FILE_NAME_DOS) break; @@ -1921,12 +1933,12 @@ search: 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, + "(%llu != %llu)\n", + (long long unsigned)dir_ni->mft_no, (long long unsigned)MREF_LE(fn->parent_directory)); continue; } @@ -1934,11 +1946,11 @@ search: || ((fn->file_name_type == FILE_NAME_POSIX) && NVolCaseSensitive(ni->vol))) case_sensitive = CASE_SENSITIVE; - + if (ntfs_names_are_equal(fn->file_name, fn->file_name_length, - name, name_len, case_sensitive, + 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); @@ -1949,7 +1961,7 @@ search: break; } } - if (errno) { + if (err) { /* * If case sensitive search failed, then try once again * ignoring case. @@ -1961,29 +1973,29 @@ 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; - + /* * Keep the last name in place, this is useful for undeletion * (Windows also does so), however delete the name if it were * in an extent, to avoid leaving an attribute list. */ - if ((ni->mrec->link_count == cpu_to_le16(1)) && !actx->base_ntfs_ino) { + if ((ni->mrec->link_count == const_cpu_to_le16(1)) && !actx->base_ntfs_ino) { /* make sure to not loop to another search */ looking_for_dos_name = FALSE; } else { 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; @@ -2109,7 +2121,7 @@ search: "Leaving inconsistent metadata.\n"); } ni = NULL; -ok: +ok: ntfs_inode_update_times(dir_ni, NTFS_UPDATE_MCTIME); out: if (actx) @@ -2152,19 +2164,14 @@ static int ntfs_link_i(ntfs_inode *ni, ntfs_inode *dir_ni, const ntfschar *name, int fn_len, err; ntfs_log_trace("Entering.\n"); - - if (!ni || !dir_ni || !name || !name_len || + + 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; - } + if (NVolHideDotFiles(dir_ni->vol)) { /* Set hidden flag according to the latest name */ if ((name_len > 1) @@ -2174,7 +2181,7 @@ static int ntfs_link_i(ntfs_inode *ni, ntfs_inode *dir_ni, const ntfschar *name, else ni->flags &= ~FILE_ATTR_HIDDEN; } - + /* Create FILE_NAME attribute. */ fn_len = sizeof(FILE_NAME_ATTR) + name_len * sizeof(ntfschar); fn = ntfs_calloc(fn_len); @@ -2189,7 +2196,7 @@ static int ntfs_link_i(ntfs_inode *ni, ntfs_inode *dir_ni, const ntfschar *name, 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); + fn->data_size = fn->allocated_size = const_cpu_to_sle64(0); } else { fn->allocated_size = cpu_to_sle64(ni->allocated_size); fn->data_size = cpu_to_sle64(ni->data_size); @@ -2276,8 +2283,6 @@ ntfs_inode *ntfs_dir_parent_inode(ntfs_inode *ni) return (dir_ni); } -#ifdef HAVE_SETXATTR - #define MAX_DOS_NAME_LENGTH 12 /* @@ -2426,11 +2431,11 @@ int ntfs_get_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, */ ntfs_name_upcase(dosname, doslen, ni->vol->upcase, ni->vol->upcase_len); - if (ntfs_ucstombs(dosname, doslen, &outname, size) < 0) { + outsize = ntfs_ucstombs(dosname, doslen, &outname, 0); + if (outsize < 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 @@ -2562,7 +2567,7 @@ static int set_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, if (set_namespace(ni, dir_ni, longname, longlen, FILE_NAME_WIN32_AND_DOS) >= 0) { if (!ntfs_delete(vol, - (const char*)NULL, ni, dir_ni, + (const char*)NULL, ni, dir_ni, deletename, deletelen)) res = 0; deleted = TRUE; @@ -2583,7 +2588,7 @@ static int set_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, res = -1; } } else { - if (!ntfs_link_i(ni, dir_ni, shortname, shortlen, + 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)) { @@ -2652,8 +2657,12 @@ int ntfs_set_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, shortlen = ntfs_mbstoucs(newname, &shortname); if (shortlen > MAX_DOS_NAME_LENGTH) shortlen = MAX_DOS_NAME_LENGTH; - /* make sure the short name has valid chars */ - if ((shortlen < 0) || ntfs_forbidden_chars(shortname,shortlen)) { + + /* Make sure the short name has valid chars. + * Note: the short name cannot end with dot or space, but the + * corresponding long name can. */ + if ((shortlen < 0) + || ntfs_forbidden_names(ni->vol,shortname,shortlen,TRUE)) { ntfs_inode_close_in_dir(ni,dir_ni); ntfs_inode_close(dir_ni); res = -errno; @@ -2664,7 +2673,8 @@ int ntfs_set_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, if (longlen > 0) { oldlen = get_dos_name(ni, dnum, oldname); if ((oldlen >= 0) - && !ntfs_forbidden_chars(longname, longlen)) { + && !ntfs_forbidden_names(ni->vol, longname, longlen, + FALSE)) { if (oldlen > 0) { if (flags & XATTR_CREATE) { res = -1; @@ -2759,7 +2769,7 @@ int ntfs_remove_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni) } else { /* * DOS name has been found, but cannot - * migrate to Posix : something bad + * migrate to Posix : something bad * has happened */ errno = EIO; @@ -2785,5 +2795,3 @@ int ntfs_remove_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni) } return (res); } - -#endif diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/dir.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/dir.h index 7d196b0198..acf659b16a 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/dir.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/dir.h @@ -29,12 +29,6 @@ #define PATH_SEP '/' -#ifdef __HAIKU__ -#ifndef MAX_PATH -#define MAX_PATH 1024 -#endif -#endif - /* * We do not have these under DJGPP, so define our version that do not conflict * with other S_IFs defined under DJGPP. @@ -115,10 +109,8 @@ 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); - -#ifdef __HAIKU__ MFT_REF ntfs_mft_get_parent_ref(ntfs_inode *ni); -#endif +u32 ntfs_interix_types(ntfs_inode *ni); int ntfs_get_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, char *value, size_t size); diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/ea.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/ea.c new file mode 100644 index 0000000000..0a7b20e97a --- /dev/null +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/ea.c @@ -0,0 +1,395 @@ +/** + * ea.c - Processing of EA's + * + * This module is part of ntfs-3g library + * + * Copyright (c) 2014 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif + +#include "types.h" +#include "param.h" +#include "layout.h" +#include "attrib.h" +#include "index.h" +#include "dir.h" +#include "ea.h" +#include "misc.h" +#include "logging.h" +#include "xattrs.h" + +/* + * Create a needed attribute (EA or EA_INFORMATION) + * + * Returns 0 if successful, + * -1 otherwise, with errno indicating why it failed. + */ + +static int ntfs_need_ea(ntfs_inode *ni, ATTR_TYPES type, int size, int flags) +{ + u8 dummy; + int res; + + res = 0; + if (!ntfs_attr_exist(ni,type, AT_UNNAMED,0)) { + if (!(flags & XATTR_REPLACE)) { + /* + * no needed 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, type, + AT_UNNAMED,0,&dummy,(s64)size); + if (!res) { + NInoFileNameSetDirty(ni); + } + NInoSetDirty(ni); + } else { + errno = EOPNOTSUPP; + res = -1; + } + } else { + errno = ENODATA; + res = -1; + } + } + return (res); +} + +/* + * Restore the old EA_INFORMATION or delete the current one, + * when EA cannot be updated. + * + * As this is used in the context of some other error, the caller + * is responsible for returning the proper error, and errno is + * left unchanged. + * Only double errors are logged here. + */ + +static void restore_ea_info(ntfs_attr *nai, const EA_INFORMATION *old_ea_info) +{ + s64 written; + int olderrno; + + olderrno = errno; + if (old_ea_info) { + written = ntfs_attr_pwrite(nai, 0, sizeof(EA_INFORMATION), + old_ea_info); + if ((size_t)written != sizeof(EA_INFORMATION)) { + ntfs_log_error("Could not restore the EA_INFORMATION," + " possible inconsistency in inode %lld\n", + (long long)nai->ni->mft_no); + } + } else { + if (ntfs_attr_rm(nai)) { + ntfs_log_error("Could not delete the EA_INFORMATION," + " possible inconsistency in inode %lld\n", + (long long)nai->ni->mft_no); + } + } + errno = olderrno; +} + +/* + * Update both EA and EA_INFORMATION + */ + +static int ntfs_update_ea(ntfs_inode *ni, const char *value, size_t size, + const EA_INFORMATION *ea_info, + const EA_INFORMATION *old_ea_info) +{ + ntfs_attr *na; + ntfs_attr *nai; + int res; + + res = 0; + nai = ntfs_attr_open(ni, AT_EA_INFORMATION, AT_UNNAMED, 0); + if (nai) { + na = ntfs_attr_open(ni, AT_EA, AT_UNNAMED, 0); + if (na) { + /* + * Set EA_INFORMATION first, it is easier to + * restore the old value, if setting EA fails. + */ + if (ntfs_attr_pwrite(nai, 0, sizeof(EA_INFORMATION), + ea_info) + != (s64)sizeof(EA_INFORMATION)) { + res = -errno; + } else { + if (((na->data_size > (s64)size) + && ntfs_attr_truncate(na, size)) + || (ntfs_attr_pwrite(na, 0, size, value) + != (s64)size)) { + res = -errno; + if (old_ea_info) + restore_ea_info(nai, + old_ea_info); + } + } + ntfs_attr_close(na); + } + ntfs_attr_close(nai); + } else { + res = -errno; + } + return (res); +} + +/* + * Return the existing EA + * + * The EA_INFORMATION is not examined and the consistency of the + * existing EA is not checked. + * + * If successful, the full attribute is returned unchanged + * and its size is returned. + * If the designated buffer is too small, the needed size is + * returned, and the buffer is left unchanged. + * If there is an error, a negative value is returned and errno + * is set according to the error. + */ + +int ntfs_get_ntfs_ea(ntfs_inode *ni, char *value, size_t size) +{ + s64 ea_size; + void *ea_buf; + int res = 0; + + if (ntfs_attr_exist(ni, AT_EA, AT_UNNAMED, 0)) { + ea_buf = ntfs_attr_readall(ni, AT_EA, (ntfschar*)NULL, 0, + &ea_size); + if (ea_buf) { + if (value && (ea_size <= (s64)size)) + memcpy(value, ea_buf, ea_size); + free(ea_buf); + res = ea_size; + } else { + ntfs_log_error("Failed to read EA from inode %lld\n", + (long long)ni->mft_no); + errno = ENODATA; + res = -errno; + } + } else { + errno = ENODATA; + res = -errno; + } + return (res); +} + +/* + * Set a new EA, and set EA_INFORMATION accordingly + * + * This is roughly the same as ZwSetEaFile() on Windows, however + * the "offset to next" of the last EA should not be cleared. + * + * Consistency of the new EA is first checked. + * + * EA_INFORMATION is set first, and it is restored to its former + * state if setting EA fails. + * + * Returns 0 if successful + * a negative value if an error occurred. + */ + +int ntfs_set_ntfs_ea(ntfs_inode *ni, const char *value, size_t size, int flags) +{ + EA_INFORMATION ea_info; + EA_INFORMATION *old_ea_info; + s64 old_ea_size; + int res; + size_t offs; + size_t nextoffs; + BOOL ok; + int ea_count; + int ea_packed; + const EA_ATTR *p_ea; + + res = -1; + if (value && (size > 0)) { + /* do consistency checks */ + offs = 0; + ok = TRUE; + ea_count = 0; + ea_packed = 0; + nextoffs = 0; + while (ok && (offs < size)) { + p_ea = (const EA_ATTR*)&value[offs]; + nextoffs = offs + le32_to_cpu(p_ea->next_entry_offset); + /* null offset to next not allowed */ + ok = (nextoffs > offs) + && (nextoffs <= size) + && !(nextoffs & 3) + && p_ea->name_length + /* zero sized value are allowed */ + && ((offs + offsetof(EA_ATTR,name) + + p_ea->name_length + 1 + + le16_to_cpu(p_ea->value_length)) + <= nextoffs) + && ((offs + offsetof(EA_ATTR,name) + + p_ea->name_length + 1 + + le16_to_cpu(p_ea->value_length)) + >= (nextoffs - 3)) + && !p_ea->name[p_ea->name_length]; + /* name not checked, as chkdsk accepts any chars */ + if (ok) { + if (p_ea->flags & NEED_EA) + ea_count++; + /* + * Assume ea_packed includes : + * 4 bytes for header (flags and lengths) + * + name length + 1 + * + value length + */ + ea_packed += 5 + p_ea->name_length + + le16_to_cpu(p_ea->value_length); + offs = nextoffs; + } + } + /* + * EA and REPARSE_POINT exclude each other + * see http://msdn.microsoft.com/en-us/library/windows/desktop/aa364404(v=vs.85).aspx + * Also return EINVAL if REPARSE_POINT is present. + */ + if (ok + && !ntfs_attr_exist(ni, AT_REPARSE_POINT, AT_UNNAMED,0)) { + ea_info.ea_length = cpu_to_le16(ea_packed); + ea_info.need_ea_count = cpu_to_le16(ea_count); + ea_info.ea_query_length = cpu_to_le32(nextoffs); + + old_ea_size = 0; + old_ea_info = NULL; + /* Try to save the old EA_INFORMATION */ + if (ntfs_attr_exist(ni, AT_EA_INFORMATION, + AT_UNNAMED, 0)) { + old_ea_info = ntfs_attr_readall(ni, + AT_EA_INFORMATION, + (ntfschar*)NULL, 0, &old_ea_size); + } + /* + * no EA or EA_INFORMATION : add them + */ + if (!ntfs_need_ea(ni, AT_EA_INFORMATION, + sizeof(EA_INFORMATION), flags) + && !ntfs_need_ea(ni, AT_EA, 0, flags)) { + res = ntfs_update_ea(ni, value, size, + &ea_info, old_ea_info); + } else { + res = -errno; + } + if (old_ea_info) + free(old_ea_info); + } else { + errno = EINVAL; + res = -errno; + } + } else { + errno = EINVAL; + res = -errno; + } + return (res); +} + +/* + * Remove the EA (including EA_INFORMATION) + * + * EA_INFORMATION is removed first, and it is restored to its former + * state if removing EA fails. + * + * Returns 0, or -1 if there is a problem + */ + +int ntfs_remove_ntfs_ea(ntfs_inode *ni) +{ + EA_INFORMATION *old_ea_info; + s64 old_ea_size; + int res; + ntfs_attr *na; + ntfs_attr *nai; + + res = 0; + if (ni) { + /* + * open and delete the EA_INFORMATION and the EA + */ + nai = ntfs_attr_open(ni, AT_EA_INFORMATION, AT_UNNAMED, 0); + if (nai) { + na = ntfs_attr_open(ni, AT_EA, AT_UNNAMED, 0); + if (na) { + /* Try to save the old EA_INFORMATION */ + old_ea_info = ntfs_attr_readall(ni, + AT_EA_INFORMATION, + (ntfschar*)NULL, 0, &old_ea_size); + res = ntfs_attr_rm(na); + NInoFileNameSetDirty(ni); + if (!res) { + res = ntfs_attr_rm(nai); + if (res && old_ea_info) { + /* + * Failed to remove the EA, try to + * restore the EA_INFORMATION + */ + restore_ea_info(nai, + old_ea_info); + } + } else { + ntfs_log_error("Failed to remove the" + " EA_INFORMATION from inode %lld\n", + (long long)ni->mft_no); + } + free(old_ea_info); + ntfs_attr_close(na); + } else { + /* EA_INFORMATION present, but no EA */ + res = ntfs_attr_rm(nai); + NInoFileNameSetDirty(ni); + } + ntfs_attr_close(nai); + } else { + errno = ENODATA; + res = -1; + } + NInoSetDirty(ni); + } else { + errno = EINVAL; + res = -1; + } + return (res ? -1 : 0); +} diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/ea.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/ea.h new file mode 100644 index 0000000000..1936aab493 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/ea.h @@ -0,0 +1,33 @@ +/* + * + * Copyright (c) 2014 Jean-Pierre Andre + * + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef EA_H +#define EA_H + +int ntfs_get_ntfs_ea(ntfs_inode *ni, char *value, size_t size); + +int ntfs_set_ntfs_ea(ntfs_inode *ni, const char *value, size_t size, int flags); + +int ntfs_remove_ntfs_ea(ntfs_inode *ni); + +#endif /* EA_H */ diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/efs.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/efs.c index 7957005b6b..7b87cfce48 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/efs.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/efs.c @@ -39,10 +39,6 @@ #include #endif -#ifdef HAVE_SETXATTR -#include -#endif - #ifdef HAVE_SYS_SYSMACROS_H #include #endif @@ -57,8 +53,7 @@ #include "logging.h" #include "misc.h" #include "efs.h" - -#ifdef HAVE_SETXATTR /* extended attributes interface required */ +#include "xattrs.h" static ntfschar logged_utility_stream_name[] = { const_cpu_to_le16('$'), @@ -321,8 +316,8 @@ int ntfs_set_efs_info(ntfs_inode *ni, const char *value, size_t size, int ntfs_efs_fixup_attribute(ntfs_attr_search_ctx *ctx, ntfs_attr *na) { - u64 newsize; - u64 oldsize; + s64 newsize; + s64 oldsize; le16 appended_bytes; u16 padding_length; ntfs_inode *ni; @@ -420,8 +415,8 @@ int ntfs_efs_fixup_attribute(ntfs_attr_search_ctx *ctx, ntfs_attr *na) NInoSetDirty(ni); NInoFileNameSetDirty(ni); - ctx->attr->data_size = cpu_to_le64(newsize); - if (le64_to_cpu(ctx->attr->initialized_size) > newsize) + ctx->attr->data_size = cpu_to_sle64(newsize); + if (sle64_to_cpu(ctx->attr->initialized_size) > newsize) ctx->attr->initialized_size = ctx->attr->data_size; ctx->attr->flags |= ATTR_IS_ENCRYPTED; if (close_ctx) @@ -433,5 +428,3 @@ err_out: ntfs_attr_put_search_ctx(ctx); return (-1); } - -#endif /* HAVE_SETXATTR */ diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/endians.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/endians.h index 397f1c20b9..948295f2ca 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/endians.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/endians.h @@ -61,10 +61,16 @@ # define __BYTE_ORDER BYTE_ORDER # define __LITTLE_ENDIAN LITTLE_ENDIAN # define __BIG_ENDIAN BIG_ENDIAN -# elif defined(__BYTE_ORDER__) +# elif defined(__BYTE_ORDER__) && defined(__LITTLE_ENDIAN__) && \ + defined(__BIG_ENDIAN__) # define __BYTE_ORDER __BYTE_ORDER__ # define __LITTLE_ENDIAN __LITTLE_ENDIAN__ # define __BIG_ENDIAN __BIG_ENDIAN__ +# elif defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \ + defined(__ORDER_BIG_ENDIAN__) +# define __BYTE_ORDER __BYTE_ORDER__ +# define __LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__ +# define __BIG_ENDIAN __ORDER_BIG_ENDIAN__ # elif (defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)) || \ defined(WORDS_LITTLEENDIAN) # define __BYTE_ORDER 1 @@ -126,6 +132,22 @@ #define __constant_cpu_to_le32(x) (x) #define __constant_cpu_to_le64(x) (x) +#define __be16_to_cpu(x) bswap_16(x) +#define __be32_to_cpu(x) bswap_32(x) +#define __be64_to_cpu(x) bswap_64(x) + +#define __cpu_to_be16(x) bswap_16(x) +#define __cpu_to_be32(x) bswap_32(x) +#define __cpu_to_be64(x) bswap_64(x) + +#define __constant_be16_to_cpu(x) __ntfs_bswap_constant_16((u16)(x)) +#define __constant_be32_to_cpu(x) __ntfs_bswap_constant_32((u32)(x)) +#define __constant_be64_to_cpu(x) __ntfs_bswap_constant_64((u64)(x)) + +#define __constant_cpu_to_be16(x) __ntfs_bswap_constant_16((u16)(x)) +#define __constant_cpu_to_be32(x) __ntfs_bswap_constant_32((u32)(x)) +#define __constant_cpu_to_be64(x) __ntfs_bswap_constant_64((u64)(x)) + #elif defined(__BIG_ENDIAN) && (__BYTE_ORDER == __BIG_ENDIAN) #define __le16_to_cpu(x) bswap_16(x) @@ -144,6 +166,22 @@ #define __constant_cpu_to_le32(x) __ntfs_bswap_constant_32((u32)(x)) #define __constant_cpu_to_le64(x) __ntfs_bswap_constant_64((u64)(x)) +#define __be16_to_cpu(x) (x) +#define __be32_to_cpu(x) (x) +#define __be64_to_cpu(x) (x) + +#define __cpu_to_be16(x) (x) +#define __cpu_to_be32(x) (x) +#define __cpu_to_be64(x) (x) + +#define __constant_be16_to_cpu(x) (x) +#define __constant_be32_to_cpu(x) (x) +#define __constant_be64_to_cpu(x) (x) + +#define __constant_cpu_to_be16(x) (x) +#define __constant_cpu_to_be32(x) (x) +#define __constant_cpu_to_be64(x) (x) + #else #error "You must define __BYTE_ORDER to be __LITTLE_ENDIAN or __BIG_ENDIAN." @@ -190,14 +228,78 @@ #define cpu_to_sle32p(x) (s32)__cpu_to_le32(*(s32*)(x)) #define cpu_to_sle64p(x) (s64)__cpu_to_le64(*(s64*)(x)) +/* Unsigned from BE to CPU conversion. */ + +#define be16_to_cpu(x) (u16)__be16_to_cpu((u16)(x)) +#define be32_to_cpu(x) (u32)__be32_to_cpu((u32)(x)) +#define be64_to_cpu(x) (u64)__be64_to_cpu((u64)(x)) + +#define be16_to_cpup(x) (u16)__be16_to_cpu(*(const u16*)(x)) +#define be32_to_cpup(x) (u32)__be32_to_cpu(*(const u32*)(x)) +#define be64_to_cpup(x) (u64)__be64_to_cpu(*(const u64*)(x)) + +/* Signed from BE to CPU conversion. */ + +#define sbe16_to_cpu(x) (s16)__be16_to_cpu((s16)(x)) +#define sbe32_to_cpu(x) (s32)__be32_to_cpu((s32)(x)) +#define sbe64_to_cpu(x) (s64)__be64_to_cpu((s64)(x)) + +#define sbe16_to_cpup(x) (s16)__be16_to_cpu(*(s16*)(x)) +#define sbe32_to_cpup(x) (s32)__be32_to_cpu(*(s32*)(x)) +#define sbe64_to_cpup(x) (s64)__be64_to_cpu(*(s64*)(x)) + +/* Unsigned from CPU to BE conversion. */ + +#define cpu_to_be16(x) (u16)__cpu_to_be16((u16)(x)) +#define cpu_to_be32(x) (u32)__cpu_to_be32((u32)(x)) +#define cpu_to_be64(x) (u64)__cpu_to_be64((u64)(x)) + +#define cpu_to_be16p(x) (u16)__cpu_to_be16(*(u16*)(x)) +#define cpu_to_be32p(x) (u32)__cpu_to_be32(*(u32*)(x)) +#define cpu_to_be64p(x) (u64)__cpu_to_be64(*(u64*)(x)) + +/* Signed from CPU to BE conversion. */ + +#define cpu_to_sbe16(x) (s16)__cpu_to_be16((s16)(x)) +#define cpu_to_sbe32(x) (s32)__cpu_to_be32((s32)(x)) +#define cpu_to_sbe64(x) (s64)__cpu_to_be64((s64)(x)) + +#define cpu_to_sbe16p(x) (s16)__cpu_to_be16(*(s16*)(x)) +#define cpu_to_sbe32p(x) (s32)__cpu_to_be32(*(s32*)(x)) +#define cpu_to_sbe64p(x) (s64)__cpu_to_be64(*(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) ((u16) __constant_le16_to_cpu(x)) +#define const_le32_to_cpu(x) ((u32) __constant_le32_to_cpu(x)) +#define const_le64_to_cpu(x) ((u64) __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) ((le16) __constant_cpu_to_le16(x)) +#define const_cpu_to_le32(x) ((le32) __constant_cpu_to_le32(x)) +#define const_cpu_to_le64(x) ((le64) __constant_cpu_to_le64(x)) + +#define const_sle16_to_cpu(x) ((s16) __constant_le16_to_cpu((le16) x)) +#define const_sle32_to_cpu(x) ((s32) __constant_le32_to_cpu((le32) x)) +#define const_sle64_to_cpu(x) ((s64) __constant_le64_to_cpu((le64) x)) + +#define const_cpu_to_sle16(x) ((sle16) __constant_cpu_to_le16((u16) x)) +#define const_cpu_to_sle32(x) ((sle32) __constant_cpu_to_le32((u32) x)) +#define const_cpu_to_sle64(x) ((sle64) __constant_cpu_to_le64((u64) x)) + +#define const_be16_to_cpu(x) ((u16) __constant_be16_to_cpu(x))) +#define const_be32_to_cpu(x) ((u32) __constant_be32_to_cpu(x))) +#define const_be64_to_cpu(x) ((u64) __constant_be64_to_cpu(x))) + +#define const_cpu_to_be16(x) ((be16) __constant_cpu_to_be16(x)) +#define const_cpu_to_be32(x) ((be32) __constant_cpu_to_be32(x)) +#define const_cpu_to_be64(x) ((be64) __constant_cpu_to_be64(x)) + +#define const_sbe16_to_cpu(x) ((s16) __constant_be16_to_cpu((be16) x)) +#define const_sbe32_to_cpu(x) ((s32) __constant_be32_to_cpu((be32) x)) +#define const_sbe64_to_cpu(x) ((s64) __constant_be64_to_cpu((be64) x)) + +#define const_cpu_to_sbe16(x) ((sbe16) __constant_cpu_to_be16((u16) x)) +#define const_cpu_to_sbe32(x) ((sbe32) __constant_cpu_to_be32((u32) x)) +#define const_cpu_to_sbe64(x) ((sbe64) __constant_cpu_to_be64((u64) x)) #endif /* defined _NTFS_ENDIANS_H */ diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/index.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/index.c index d498dde4b6..58568ded29 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/index.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/index.c @@ -143,7 +143,7 @@ static void ntfs_index_ctx_free(ntfs_index_context *icx) { ntfs_log_trace("Entering\n"); - if (!icx->entry) + if (!icx->bad_index && !icx->entry) return; if (icx->actx) @@ -191,9 +191,9 @@ void ntfs_index_ctx_reinit(ntfs_index_context *icx) }; } -static VCN *ntfs_ie_get_vcn_addr(INDEX_ENTRY *ie) +static leVCN *ntfs_ie_get_vcn_addr(INDEX_ENTRY *ie) { - return (VCN *)((u8 *)ie + le16_to_cpu(ie->length) - sizeof(VCN)); + return (leVCN *)((u8 *)ie + le16_to_cpu(ie->length) - sizeof(leVCN)); } /** @@ -338,7 +338,7 @@ static void ntfs_ie_delete(INDEX_HEADER *ih, INDEX_ENTRY *ie) 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_sle64(vcn); } /** @@ -719,7 +719,7 @@ int ntfs_index_lookup(const void *key, const int key_len, ntfs_index_context *ic ret = ntfs_ie_lookup(key, key_len, icx, &ir->index, &vcn, &ie); if (ret == STATUS_ERROR) { err = errno; - goto err_out; + goto err_lookup; } icx->ir = ir; @@ -780,6 +780,8 @@ descend_into_child_node: goto descend_into_child_node; err_out: + icx->bad_index = TRUE; /* Force icx->* to be freed */ +err_lookup: free(ib); if (!err) err = EIO; @@ -811,17 +813,17 @@ static INDEX_BLOCK *ntfs_ib_alloc(VCN ib_vcn, u32 ib_size, return NULL; ib->magic = magic_INDX; - ib->usa_ofs = cpu_to_le16(sizeof(INDEX_BLOCK)); + ib->usa_ofs = const_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); + *(le16 *)((char *)ib + le16_to_cpu(ib->usa_ofs)) = const_cpu_to_le16(1); + ib->lsn = const_cpu_to_sle64(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.index_length = const_cpu_to_le32(0); ib->index.allocated_size = cpu_to_le32(ib_size - (sizeof(INDEX_BLOCK) - ih_size)); ib->index.ih_flags = node_type; @@ -1160,7 +1162,7 @@ retry : ie = ntfs_ie_get_first(&ir->index); ie->ie_flags |= INDEX_ENTRY_NODE; - ie->length = cpu_to_le16(sizeof(INDEX_ENTRY_HEADER) + sizeof(VCN)); + ie->length = const_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) @@ -1175,8 +1177,7 @@ retry : * index, we may have to move the root to an extent */ if ((errno == ENOSPC) - && !ctx->al_entry - && !ntfs_inode_add_attrlist(icx->ni)) { + && (ctx->al_entry || !ntfs_inode_add_attrlist(icx->ni))) { ntfs_attr_put_search_ctx(ctx); ctx = (ntfs_attr_search_ctx*)NULL; ir = ntfs_ir_lookup(icx->ni, icx->name, icx->name_len, @@ -1842,7 +1843,8 @@ err_out: goto out; } -int ntfs_index_remove(ntfs_inode *dir_ni, ntfs_inode *ni, +int ntfs_index_remove(ntfs_inode *dir_ni, + ntfs_inode *ni __attribute__((unused)), const void *key, const int keylen) { int ret = STATUS_ERROR; @@ -1857,13 +1859,6 @@ int ntfs_index_remove(ntfs_inode *dir_ni, ntfs_inode *ni, 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; - } - ret = ntfs_index_rm(icx); if (ret == STATUS_ERROR) goto err_out; @@ -2042,7 +2037,7 @@ static INDEX_ENTRY *ntfs_index_walk_up(INDEX_ENTRY *ie, INDEX_ENTRY *ntfs_index_next(INDEX_ENTRY *ie, ntfs_index_context *ictx) { INDEX_ENTRY *next; - int flags; + le16 flags; /* * lookup() may have returned an invalid node diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/index.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/index.h index c0e7618038..036b742fb5 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/index.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/index.h @@ -129,6 +129,7 @@ typedef struct { VCN parent_vcn[MAX_PARENT_VCN]; /* entry's parent nodes */ int pindex; /* maximum it's the number of the parent nodes */ BOOL ib_dirty; + BOOL bad_index; u32 block_size; u8 vcn_size_bits; } ntfs_index_context; diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/inode.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/inode.c index a4a01348be..4c364b9ae6 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/inode.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/inode.c @@ -36,9 +36,6 @@ #ifdef HAVE_ERRNO_H #include #endif -#ifdef HAVE_SETXATTR -#include -#endif #include "param.h" #include "compat.h" @@ -57,6 +54,7 @@ #include "ntfstime.h" #include "logging.h" #include "misc.h" +#include "xattrs.h" ntfs_inode *ntfs_inode_base(ntfs_inode *ni) { @@ -570,7 +568,7 @@ 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 leMFT_REF mref) { u64 mft_no = MREF_LE(mref); VCN extent_vcn; @@ -806,7 +804,7 @@ static int ntfs_inode_sync_file_name(ntfs_inode *ni, ntfs_inode *dir_ni) goto err_out; } /* Collect the reparse tag, if any */ - reparse_tag = cpu_to_le32(0); + reparse_tag = const_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)) { @@ -874,7 +872,7 @@ static int ntfs_inode_sync_file_name(ntfs_inode *ni, ntfs_inode *dir_ni) (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); + = const_cpu_to_sle64(0); else { fnx->allocated_size = cpu_to_sle64(ni->allocated_size); fnx->data_size = cpu_to_sle64(ni->data_size); @@ -1168,7 +1166,7 @@ int ntfs_inode_add_attrlist(ntfs_inode *ni) if (ctx->attr->non_resident) ale->lowest_vcn = ctx->attr->lowest_vcn; else - ale->lowest_vcn = 0; + ale->lowest_vcn = const_cpu_to_sle64(0); ale->mft_reference = MK_LE_MREF(ni->mft_no, le16_to_cpu(ni->mrec->sequence_number)); ale->instance = ctx->attr->instance; @@ -1206,7 +1204,7 @@ int ntfs_inode_add_attrlist(ntfs_inode *ni) /* Add $ATTRIBUTE_LIST to mft record. */ if (ntfs_resident_attr_record_add(ni, - AT_ATTRIBUTE_LIST, NULL, 0, NULL, 0, 0) < 0) { + AT_ATTRIBUTE_LIST, NULL, 0, NULL, 0, const_cpu_to_le16(0)) < 0) { err = errno; ntfs_log_perror("Couldn't add $ATTRIBUTE_LIST to MFT"); goto rollback; @@ -1446,8 +1444,6 @@ int ntfs_inode_badclus_bad(u64 mft_no, ATTR_RECORD *attr) return ret; } -#ifdef HAVE_SETXATTR /* extended attributes interface required */ - /* * Get high precision NTFS times * @@ -1477,18 +1473,18 @@ int ntfs_inode_get_times(ntfs_inode *ni, char *value, size_t size) le16_to_cpu(ctx->attr->value_offset)); if (value && (size >= 8)) { times = (u64*)value; - times[0] = le64_to_cpu(std_info->creation_time); + times[0] = sle64_to_cpu(std_info->creation_time); ret = 8; if (size >= 16) { - times[1] = le64_to_cpu(std_info->last_data_change_time); + times[1] = sle64_to_cpu(std_info->last_data_change_time); ret = 16; } if (size >= 24) { - times[2] = le64_to_cpu(std_info->last_access_time); + times[2] = sle64_to_cpu(std_info->last_access_time); ret = 24; } if (size >= 32) { - times[3] = le64_to_cpu(std_info->last_mft_change_time); + times[3] = sle64_to_cpu(std_info->last_mft_change_time); ret = 32; } } else @@ -1551,16 +1547,16 @@ int ntfs_inode_set_times(ntfs_inode *ni, const char *value, size_t size, * return sub-second times in getattr() */ set_nino_flag(ni, TimesSet); - std_info->creation_time = cpu_to_le64(times[0]); + std_info->creation_time = cpu_to_sle64(times[0]); ni->creation_time = std_info->creation_time; if (size >= 16) { - std_info->last_data_change_time = cpu_to_le64(times[1]); + std_info->last_data_change_time = cpu_to_sle64(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]); + std_info->last_access_time = cpu_to_sle64(times[2]); ni->last_access_time = std_info->last_access_time; } @@ -1578,13 +1574,13 @@ int ntfs_inode_set_times(ntfs_inode *ni, const char *value, size_t size, fn = (FILE_NAME_ATTR*)((u8 *)ctx->attr + le16_to_cpu(ctx->attr->value_offset)); fn->creation_time - = cpu_to_le64(times[0]); + = cpu_to_sle64(times[0]); if (size >= 16) fn->last_data_change_time - = cpu_to_le64(times[1]); + = cpu_to_sle64(times[1]); if (size >= 24) fn->last_access_time - = cpu_to_le64(times[2]); + = cpu_to_sle64(times[2]); fn->last_mft_change_time = now; cnt++; } @@ -1604,5 +1600,3 @@ int ntfs_inode_set_times(ntfs_inode *ni, const char *value, size_t size, errno = EEXIST; return (ret); } - -#endif /* HAVE_SETXATTR */ diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/inode.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/inode.h index 5a6f7da66e..8d58d24584 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/inode.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/inode.h @@ -197,7 +197,7 @@ extern int ntfs_inode_nidata_hash(const struct CACHED_GENERIC *item); extern ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni, - const MFT_REF mref); + const leMFT_REF mref); extern int ntfs_inode_attach_all_extents(ntfs_inode *ni); diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/ioctl.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/ioctl.c new file mode 100644 index 0000000000..2eef5a512d --- /dev/null +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/ioctl.c @@ -0,0 +1,388 @@ +/** + * ioctl.c - Processing of ioctls + * + * This module is part of ntfs-3g library + * + * Copyright (c) 2014-2015 Jean-Pierre Andre + * Copyright (c) 2014 Red Hat, Inc. + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_INTTYPES_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_LIMITS_H +#include +#endif +#include +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef MAJOR_IN_MKDEV +#include +#endif +#ifdef MAJOR_IN_SYSMACROS +#include +#endif + +#ifdef HAVE_SYS_STAT_H +#include +#endif + +#ifdef HAVE_LINUX_FS_H +#include +#endif + +#include "compat.h" +#include "debug.h" +#include "bitmap.h" +#include "attrib.h" +#include "inode.h" +#include "layout.h" +#include "volume.h" +#include "index.h" +#include "logging.h" +#include "ntfstime.h" +#include "unistr.h" +#include "dir.h" +#include "security.h" +#include "ioctl.h" +#include "misc.h" + +#if defined(FITRIM) && defined(BLKDISCARD) + +/* Issue a TRIM request to the underlying device for the given clusters. */ +static int fstrim_clusters(ntfs_volume *vol, LCN lcn, s64 length) +{ + struct ntfs_device *dev = vol->dev; + uint64_t range[2]; + + ntfs_log_debug("fstrim_clusters: %lld length %lld\n", + (long long) lcn, (long long) length); + + range[0] = lcn << vol->cluster_size_bits; + range[1] = length << vol->cluster_size_bits; + + if (dev->d_ops->ioctl(dev, BLKDISCARD, range) == -1) { + ntfs_log_debug("fstrim_one_cluster: ioctl failed: %m\n"); + return -errno; + } + return 0; +} + +static int read_line(const char *path, char *line, size_t max_bytes) +{ + FILE *fp; + + fp = fopen(path, "r"); + if (fp == NULL) + return -errno; + if (fgets(line, max_bytes, fp) == NULL) { + int ret = -EIO; /* fgets doesn't set errno */ + fclose(fp); + return ret; + } + fclose (fp); + return 0; +} + +static int read_u64(const char *path, u64 *n) +{ + char line[64]; + int ret; + + ret = read_line(path, line, sizeof line); + if (ret) + return ret; + if (sscanf(line, "%" SCNu64, n) != 1) + return -EINVAL; + return 0; +} + +/* Find discard limits for current backing device. + */ +static int fstrim_limits(ntfs_volume *vol, + u64 *discard_alignment, + u64 *discard_granularity, + u64 *discard_max_bytes) +{ + struct stat statbuf; + char path1[80], path2[80]; + int ret; + + /* Stat the backing device. Caller has ensured it is a block device. */ + if (stat(vol->dev->d_name, &statbuf) == -1) { + ntfs_log_debug("fstrim_limits: could not stat %s\n", + vol->dev->d_name); + return -errno; + } + + /* For whole devices, + * /sys/dev/block/MAJOR:MINOR/discard_alignment + * /sys/dev/block/MAJOR:MINOR/queue/discard_granularity + * /sys/dev/block/MAJOR:MINOR/queue/discard_max_bytes + * will exist. + * For partitions, we also need to check the parent device: + * /sys/dev/block/MAJOR:MINOR/../queue/discard_granularity + * /sys/dev/block/MAJOR:MINOR/../queue/discard_max_bytes + */ + snprintf(path1, sizeof path1, "/sys/dev/block/%d:%d", + major(statbuf.st_rdev), minor(statbuf.st_rdev)); + + snprintf(path2, sizeof path2, "%s/discard_alignment", path1); + ret = read_u64(path2, discard_alignment); + if (ret) { + if (ret != -ENOENT) + return ret; + else + /* We would expect this file to exist on all + * modern kernels. But for the sake of very + * old kernels: + */ + goto not_found; + } + + snprintf(path2, sizeof path2, "%s/queue/discard_granularity", path1); + ret = read_u64(path2, discard_granularity); + if (ret) { + if (ret != -ENOENT) + return ret; + else { + snprintf(path2, sizeof path2, + "%s/../queue/discard_granularity", path1); + ret = read_u64(path2, discard_granularity); + if (ret) { + if (ret != -ENOENT) + return ret; + else + goto not_found; + } + } + } + + snprintf(path2, sizeof path2, "%s/queue/discard_max_bytes", path1); + ret = read_u64(path2, discard_max_bytes); + if (ret) { + if (ret != -ENOENT) + return ret; + else { + snprintf(path2, sizeof path2, + "%s/../queue/discard_max_bytes", path1); + ret = read_u64(path2, discard_max_bytes); + if (ret) { + if (ret != -ENOENT) + return ret; + else + goto not_found; + } + } + } + + return 0; + +not_found: + /* If we reach here then we didn't find the device. This is + * not an error, but set discard_max_bytes = 0 to indicate + * that discard is not available. + */ + *discard_alignment = 0; + *discard_granularity = 0; + *discard_max_bytes = 0; + return 0; +} + +#define FSTRIM_BUFSIZ 4096 + +/* Trim the filesystem. + * + * Free blocks between 'start' and 'start+len-1' (both byte offsets) + * are found and TRIM requests are sent to the block device. 'minlen' + * is the minimum continguous free range to discard. + */ +static int fstrim(ntfs_volume *vol, void *data, u64 *trimmed) +{ + struct fstrim_range *range = data; + u64 start = range->start; + u64 len = range->len; + u64 minlen = range->minlen; + u64 discard_alignment, discard_granularity, discard_max_bytes; + u8 *buf = NULL; + LCN start_buf; + int ret; + + ntfs_log_debug("fstrim: start=%llu len=%llu minlen=%llu\n", + (unsigned long long) start, + (unsigned long long) len, + (unsigned long long) minlen); + + *trimmed = 0; + + /* Fail if user tries to use the fstrim -o/-l/-m options. + * XXX We could fix these limitations in future. + */ + if (start != 0 || len != (uint64_t)-1) { + ntfs_log_debug("fstrim: setting start or length is not supported\n"); + return -EINVAL; + } + if (minlen > vol->cluster_size) { + ntfs_log_debug("fstrim: minlen > cluster size is not supported\n"); + return -EINVAL; + } + + /* Only block devices are supported. It would be possible to + * support backing files (ie. without using loop) but the + * ioctls used to punch holes in files are completely + * different. + */ + if (!NDevBlock(vol->dev)) { + ntfs_log_debug("fstrim: not supported for non-block-device\n"); + return -EOPNOTSUPP; + } + + ret = fstrim_limits(vol, &discard_alignment, + &discard_granularity, &discard_max_bytes); + if (ret) + return ret; + if (discard_alignment != 0) { + ntfs_log_debug("fstrim: backing device is not aligned for discards\n"); + return -EOPNOTSUPP; + } + if (discard_granularity > vol->cluster_size) { + ntfs_log_debug("fstrim: discard granularity of backing device is larger than cluster size\n"); + return -EOPNOTSUPP; + } + if (discard_max_bytes == 0) { + ntfs_log_debug("fstrim: backing device does not support discard (discard_max_bytes == 0)\n"); + return -EOPNOTSUPP; + } + + /* Sync the device before doing anything. */ + ret = ntfs_device_sync(vol->dev); + if (ret) + return ret; + + /* Read through the bitmap. */ + buf = ntfs_malloc(FSTRIM_BUFSIZ); + if (buf == NULL) + return -errno; + for (start_buf = 0; start_buf < vol->nr_clusters; + start_buf += FSTRIM_BUFSIZ * 8) { + s64 count; + s64 br; + LCN end_buf, start_lcn; + + /* start_buf is LCN of first cluster in the current buffer. + * end_buf is LCN of last cluster + 1 in the current buffer. + */ + end_buf = start_buf + FSTRIM_BUFSIZ*8; + if (end_buf > vol->nr_clusters) + end_buf = vol->nr_clusters; + count = (end_buf - start_buf) / 8; + + br = ntfs_attr_pread(vol->lcnbmp_na, start_buf/8, count, buf); + if (br != count) { + if (br >= 0) + ret = -EIO; + else + ret = -errno; + goto free_out; + } + + /* Trim the clusters in large as possible blocks, but + * not larger than discard_max_bytes. + */ + for (start_lcn = start_buf; start_lcn < end_buf; ++start_lcn) { + if (!ntfs_bit_get(buf, start_lcn-start_buf)) { + LCN end_lcn; + + /* Cluster 'start_lcn' is not in use, + * find end of this run. + */ + end_lcn = start_lcn+1; + while (end_lcn < end_buf && + (u64) (end_lcn-start_lcn) << vol->cluster_size_bits + < discard_max_bytes && + !ntfs_bit_get(buf, end_lcn-start_buf)) + end_lcn++; + + ret = fstrim_clusters(vol, + start_lcn, end_lcn-start_lcn); + if (ret) + goto free_out; + + *trimmed += (end_lcn - start_lcn) + << vol->cluster_size_bits; + start_lcn = end_lcn-1; + } + } + } + + ret = 0; +free_out: + free(buf); + return ret; +} + +#endif /* FITRIM && BLKDISCARD */ + +int ntfs_ioctl(ntfs_inode *ni, int cmd, void *arg __attribute__((unused)), + unsigned int flags __attribute__((unused)), void *data) +{ + int ret = 0; + + switch (cmd) { +#if defined(FITRIM) && defined(BLKDISCARD) + case FITRIM: + if (!ni || !data) + ret = -EINVAL; + else { + u64 trimmed; + struct fstrim_range *range = (struct fstrim_range*)data; + + ret = fstrim(ni->vol, data, &trimmed); + range->len = trimmed; + } + break; +#else +#warning Trimming not supported : FITRIM or BLKDISCARD not defined +#endif + default : + ret = -EINVAL; + break; + } + return (ret); +} diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/ioctl.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/ioctl.h new file mode 100644 index 0000000000..4ed6c01085 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/ioctl.h @@ -0,0 +1,30 @@ +/* + * + * Copyright (c) 2014 Jean-Pierre Andre + * + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef IOCTL_H +#define IOCTL_H + +int ntfs_ioctl(ntfs_inode *ni, int cmd, void *arg, + unsigned int flags, void *data); + +#endif /* IOCTL_H */ diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/layout.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/layout.h index 427f1520bb..816be49b93 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/layout.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/layout.h @@ -47,20 +47,20 @@ * struct BIOS_PARAMETER_BLOCK - BIOS parameter block (bpb) structure. */ typedef struct { - u16 bytes_per_sector; /* Size of a sector in bytes. */ + le16 bytes_per_sector; /* Size of a sector in bytes. */ u8 sectors_per_cluster; /* Size of a cluster in sectors. */ - u16 reserved_sectors; /* zero */ + le16 reserved_sectors; /* zero */ u8 fats; /* zero */ - u16 root_entries; /* zero */ - u16 sectors; /* zero */ + le16 root_entries; /* zero */ + le16 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 + le16 sectors_per_fat; /* zero */ +/*0x0d*/le16 sectors_per_track; /* Required to boot Windows. */ +/*0x0f*/le16 heads; /* Required to boot Windows. */ +/*0x11*/le32 hidden_sectors; /* Offset to the start of the partition relative to the disk in sectors. Required to boot Windows. */ -/*0x15*/u32 large_sectors; /* zero */ +/*0x15*/le32 large_sectors; /* zero */ /* sizeof() = 25 (0x19) bytes */ } __attribute__((__packed__)) BIOS_PARAMETER_BLOCK; @@ -69,27 +69,27 @@ typedef struct { */ typedef struct { u8 jump[3]; /* Irrelevant (jump to boot up code).*/ - u64 oem_id; /* Magic "NTFS ". */ + le64 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 +/*0x28*/sle64 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. */ + sle64 mft_lcn; /* Cluster location of mft data. */ + sle64 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. */ + le64 volume_serial_number; /* Irrelevant (serial number). */ + le32 checksum; /* Boot sector checksum. */ /*0x54*/u8 bootstrap[426]; /* Irrelevant (boot up code). */ - u16 end_of_sector_marker; /* End of bootsector magic. Always is + le16 end_of_sector_marker; /* End of bootsector magic. Always is 0xaa55 in little endian. */ /* sizeof() = 512 (0x200) bytes */ } __attribute__((__packed__)) NTFS_BOOT_SECTOR; @@ -161,33 +161,39 @@ typedef enum { #define ntfs_is_empty_recordp(p) ( ntfs_is_magicp(p, empty) ) +/* + * The size of a logical sector in bytes, used as the sequence number stride for + * multi-sector transfers. This is intended to be less than or equal to the + * physical sector size, since if this were greater than the physical sector + * size, then incomplete multi-sector transfers may not be detected. + */ #define NTFS_BLOCK_SIZE 512 #define NTFS_BLOCK_SIZE_BITS 9 /** * struct NTFS_RECORD - * - * The Update Sequence Array (usa) is an array of the u16 values which belong + * The Update Sequence Array (usa) is an array of the le16 values which belong * to the end of each sector protected by the update sequence record in which * this array is contained. Note that the first entry is the Update Sequence * Number (usn), a cyclic counter of how many times the protected record has * been written to disk. The values 0 and -1 (ie. 0xffff) are not used. All - * last u16's of each sector have to be equal to the usn (during reading) or + * last le16's of each sector have to be equal to the usn (during reading) or * 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 - * 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. + * The 510 bytes comes from the fact that the last le16 in the array has to + * (obviously) finish before the last le16 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) + le16 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 + le16 usa_count; /* Number of le16 sized entries in the usa including the Update Sequence Number (usn), thus the number of fixups is the usa_count minus 1. */ @@ -235,7 +241,7 @@ typedef enum { FILE_reserved12 = 12, /* Reserved for future use (records 12-15). */ FILE_reserved13 = 13, FILE_reserved14 = 14, - FILE_reserved15 = 15, + FILE_mft_data = 15, /* Reserved for first extent of $MFT:$DATA */ FILE_first_user = 16, /* First user file, used as test limit for whether to allow opening a file or not. */ } NTFS_SYSTEM_FILES; @@ -340,17 +346,17 @@ 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. */ + le16 usa_ofs; /* See NTFS_RECORD definition above. */ + le16 usa_count; /* See NTFS_RECORD definition above. */ -/* 8*/ LSN lsn; /* $LogFile sequence number for this record. +/* 8*/ leLSN 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 +/* 16*/ le16 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 +/* 18*/ le16 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 @@ -360,18 +366,19 @@ typedef struct { 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 +/* 20*/ le16 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. +/* 24*/ le32 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 +/* 28*/ le32 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. +/* 32*/ leMFT_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 @@ -383,7 +390,7 @@ typedef struct { 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 +/* 40*/ le16 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 @@ -391,8 +398,8 @@ typedef struct { 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. */ +/* 42*/ le16 reserved; /* Reserved/alignment. */ +/* 44*/ le32 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 @@ -414,17 +421,17 @@ 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. */ + le16 usa_ofs; /* See NTFS_RECORD definition above. */ + le16 usa_count; /* See NTFS_RECORD definition above. */ -/* 8*/ LSN lsn; /* $LogFile sequence number for this record. +/* 8*/ leLSN 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 +/* 16*/ le16 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 +/* 18*/ le16 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 @@ -434,18 +441,19 @@ typedef struct { 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 +/* 20*/ le16 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. +/* 24*/ le32 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 +/* 28*/ le32 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. +/* 32*/ leMFT_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 @@ -457,7 +465,7 @@ typedef struct { 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 +/* 40*/ le16 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 @@ -513,51 +521,42 @@ typedef enum { * enum COLLATION_RULES - The collation rules for sorting views/indexes/etc * (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. - * 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. - * COLLATION_NTOFS_ULONG - Sorting is done according to ascending u32 key + * COLLATION_BINARY - Collate by binary compare where the first byte is most + * significant. + * COLLATION_FILE_NAME - Collate Unicode strings by comparing their 16-bit + * coding units, primarily ignoring case using the volume's $UpCase table, + * but falling back to a case-sensitive comparison if the names are equal + * ignoring case. + * COLLATION_UNICODE_STRING - TODO: this is not yet implemented and still needs + * to be properly documented --- is it really the same as + * COLLATION_FILE_NAME? + * COLLATION_NTOFS_ULONG - Sorting is done according to ascending le32 key * values. E.g. used for $SII index in FILE_Secure, which sorts by - * security_id (u32). + * security_id (le32). * COLLATION_NTOFS_SID - Sorting is done according to ascending SID values. * 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. * 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 + * le32 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 + * le32 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: + * To compare them, they are split into four le32 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. + * first le32 value of the 1st object_id is less than the first le32 of + * the 2nd object_id. If the first le32 values of both object_ids were + * equal then the second le32 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_BINARY = const_cpu_to_le32(0), + COLLATION_FILE_NAME = const_cpu_to_le32(1), + COLLATION_UNICODE_STRING = const_cpu_to_le32(2), 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), @@ -617,12 +616,12 @@ typedef struct { /* 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. +/* 84*/ le32 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. */ +/* 90*/ sle64 min_size; /* Optional minimum attribute size. */ +/* 98*/ sle64 max_size; /* Maximum size of attribute. */ /* sizeof() = 0xa0 or 160 bytes */ } __attribute__((__packed__)) ATTR_DEF; @@ -722,14 +721,14 @@ typedef enum { 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 +/* 4*/ le32 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 +/* 10*/ le16 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 @@ -739,15 +738,15 @@ typedef struct { 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 +/* 14*/ le16 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 +/* 16 */ le32 value_length; /* Byte size of attribute value. */ +/* 20 */ le16 value_offset; /* Byte offset of the attribute value from the start of the attribute record. When creating, align to 8-byte boundary if we @@ -763,18 +762,18 @@ typedef struct { } __attribute__((__packed__)); /* Non-resident attributes. */ struct { -/* 16*/ VCN lowest_vcn; /* Lowest valid virtual cluster number +/* 16*/ leVCN 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 +/* 24*/ leVCN 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 +/* 32*/ le16 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). @@ -789,7 +788,7 @@ typedef struct { /* 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 +/* 40*/ sle64 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 @@ -797,10 +796,10 @@ typedef struct { 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 +/* 48*/ sle64 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 +/* 56*/ sle64 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, @@ -808,7 +807,7 @@ typedef struct { size of a non resident attribute. */ /* sizeof(uncompressed attr) = 64*/ -/* 64*/ s64 compressed_size; /* Byte size of the attribute +/* 64*/ sle64 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 @@ -834,7 +833,7 @@ typedef enum { 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), */ + /* Old DOS volid. Unused in NT. = const_cpu_to_le32(0x00000008), */ FILE_ATTR_DIRECTORY = const_cpu_to_le32(0x00000010), /* FILE_ATTR_DIRECTORY is not considered valid in NT. It is reserved @@ -908,13 +907,13 @@ typedef enum { */ typedef struct { /*Ofs*/ -/* 0*/ s64 creation_time; /* Time file was created. Updated when +/* 0*/ sle64 creation_time; /* Time file was created. Updated when a filename is changed(?). */ -/* 8*/ s64 last_data_change_time; /* Time the data attribute was last +/* 8*/ sle64 last_data_change_time; /* Time the data attribute was last modified. */ -/* 16*/ s64 last_mft_change_time; /* Time this mft record was last +/* 16*/ sle64 last_mft_change_time; /* Time this mft record was last modified. */ -/* 24*/ s64 last_access_time; /* Approximate time when the file was +/* 24*/ sle64 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 @@ -952,23 +951,23 @@ typedef struct { * views that as a corruption, assuming that it behaves like this for all * attributes. */ - /* 36*/ u32 maximum_versions; /* Maximum allowed versions for + /* 36*/ le32 maximum_versions; /* Maximum allowed versions for file. Zero if version numbering is disabled. */ - /* 40*/ u32 version_number; /* This file's version (if any). + /* 40*/ le32 version_number; /* This file's version (if any). Set to zero if maximum_versions is zero. */ - /* 44*/ u32 class_id; /* Class id from bidirectional + /* 44*/ le32 class_id; /* Class id from bidirectional class id index (?). */ - /* 48*/ u32 owner_id; /* Owner_id of the user owning + /* 48*/ le32 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. + /* 52*/ le32 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 + /* 56*/ le64 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 + /* 64*/ le64 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. @@ -1020,13 +1019,13 @@ typedef struct { typedef struct { /*Ofs*/ /* 0*/ ATTR_TYPES type; /* Type of referenced attribute. */ -/* 4*/ u16 length; /* Byte size of this entry. */ +/* 4*/ le16 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 +/* 8*/ leVCN 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 @@ -1038,10 +1037,10 @@ typedef struct { 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 +/* 16*/ leMFT_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 +/* 24*/ le16 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 @@ -1069,12 +1068,17 @@ typedef enum { 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. */ + '>', '?', '\' and '|'. Trailing dots and spaces are allowed, + even though on Windows a filename with such a suffix can only + be created and accessed using a WinNT-style path, i.e. + \\?\-prefixed. (If a regular path is used, Windows will + strip the trailing dots and spaces, which makes such + filenames incompatible with most Windows software.) */ FILE_NAME_DOS = 0x02, /* The standard DOS filenames (8.3 format). Uppercase only. All 8-bit characters greater space, except: '"', '*', '+', - ',', '/', ':', ';', '<', '=', '>', '?' and '\'. */ + ',', '/', ':', ';', '<', '=', '>', '?' and '\'. Trailing + dots and spaces are forbidden. */ 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 @@ -1096,16 +1100,16 @@ typedef enum { */ typedef struct { /*hex ofs*/ -/* 0*/ MFT_REF parent_directory; /* Directory this filename is +/* 0*/ leMFT_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 +/* 8*/ sle64 creation_time; /* Time file was created. */ +/* 10*/ sle64 last_data_change_time; /* Time the data attribute was last modified. */ -/* 18*/ s64 last_mft_change_time; /* Time this mft record was last +/* 18*/ sle64 last_mft_change_time; /* Time this mft record was last modified. */ -/* 20*/ s64 last_access_time; /* Last time this mft record was +/* 20*/ sle64 last_access_time; /* Last time this mft record was accessed. */ -/* 28*/ s64 allocated_size; /* Byte size of on-disk allocated space +/* 28*/ sle64 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 @@ -1114,17 +1118,17 @@ typedef struct { 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 +/* 30*/ sle64 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 + /* 3c*/ le16 packed_ea_size; /* Size of the buffer needed to pack the extended attributes (EAs), if such are present.*/ - /* 3e*/ u16 reserved; /* Reserved for alignment. */ + /* 3e*/ le16 reserved; /* Reserved for alignment. */ } __attribute__((__packed__)); - /* 3c*/ u32 reparse_point_tag; /* Type of reparse point, + /* 3c*/ le32 reparse_point_tag; /* Type of reparse point, present only in reparse points and only if there are no EAs. */ @@ -1148,9 +1152,9 @@ typedef struct { * 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. */ + le32 data1; /* The first eight hexadecimal digits of the GUID. */ + le16 data2; /* The first group of four hexadecimal digits. */ + le16 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. */ @@ -1170,8 +1174,8 @@ typedef struct { * domain_id - Reserved (always zero). */ typedef struct { - MFT_REF mft_reference; /* Mft record containing the object_id in - the index entry key. */ + leMFT_REF mft_reference; /* Mft record containing the object_id + in the index entry key. */ union { struct { GUID birth_volume_id; @@ -1352,8 +1356,8 @@ typedef enum { /* Identifier authority. */ */ typedef union { struct { - u16 high_part; /* High 16-bits. */ - u32 low_part; /* Low 32-bits. */ + be16 high_part; /* High 16-bits. */ + be32 low_part; /* Low 32-bits. */ } __attribute__((__packed__)); u8 value[6]; /* Value as individual bytes. */ } __attribute__((__packed__)) SID_IDENTIFIER_AUTHORITY; @@ -1390,7 +1394,7 @@ typedef struct { u8 revision; u8 sub_authority_count; SID_IDENTIFIER_AUTHORITY identifier_authority; - u32 sub_authority[1]; /* At least one sub_authority. */ + le32 sub_authority[1]; /* At least one sub_authority. */ } __attribute__((__packed__)) SID; /** @@ -1471,7 +1475,7 @@ typedef enum { typedef struct { ACE_TYPES type; /* Type of the ACE. */ ACE_FLAGS flags; /* Flags describing the ACE. */ - u16 size; /* Size in bytes of the ACE. */ + le16 size; /* Size in bytes of the ACE. */ } __attribute__((__packed__)) ACE_HEADER; /** @@ -1637,7 +1641,7 @@ 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. */ + le16 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. */ @@ -1659,7 +1663,7 @@ 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. */ + le16 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. */ @@ -1682,10 +1686,10 @@ typedef struct { typedef struct { u8 revision; /* Revision of this ACL. */ u8 alignment1; - u16 size; /* Allocated space in bytes for ACL. Includes this + le16 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; + le16 ace_count; /* Number of ACEs in the ACL. */ + le16 alignment2; /* sizeof() = 8 bytes */ } __attribute__((__packed__)) ACL; @@ -1783,17 +1787,17 @@ typedef struct { 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 + le32 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 + le32 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 + le32 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 + le32 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. */ @@ -1904,21 +1908,21 @@ typedef SECURITY_DESCRIPTOR_RELATIVE SECURITY_DESCRIPTOR_ATTR; * 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. */ + le32 hash; /* Hash of the security descriptor. */ + le32 security_id; /* The security_id assigned to the descriptor. */ + le64 offset; /* Byte offset of this entry in the $SDS stream. */ + le32 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 + le32 hash; /* Hash of the security descriptor. */ + le32 security_id; /* The security_id assigned to the descriptor. */ + le64 offset; /* Byte offset of this entry in the $SDS stream. */ + le32 length; /* Size in bytes of this entry in $SDS stream. */ + le32 reserved_II; /* Padding - always unicode "II" or zero. This field isn't counted in INDEX_ENTRY's data_length. */ } __attribute__((__packed__)) SDH_INDEX_DATA; @@ -1943,10 +1947,10 @@ typedef SECURITY_DESCRIPTOR_HEADER SII_INDEX_DATA; 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. */ + le32 hash; /* Hash of the security descriptor. */ + le32 security_id; /* The security_id assigned to the descriptor. */ + le64 offset; /* Byte offset of this entry in the $SDS stream. */ + le32 length; /* Size in bytes of this entry in $SDS stream. */ /* 20*/ SECURITY_DESCRIPTOR_RELATIVE sid; /* The self-relative security descriptor. */ } __attribute__((__packed__)) SDS_ENTRY; @@ -1957,7 +1961,7 @@ typedef struct { * The collation type is COLLATION_NTOFS_ULONG. */ typedef struct { - u32 security_id; /* The security_id assigned to the descriptor. */ + le32 security_id; /* The security_id assigned to the descriptor. */ } __attribute__((__packed__)) SII_INDEX_KEY; /** @@ -1967,8 +1971,8 @@ typedef struct { * 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. */ + le32 hash; /* Hash of the security descriptor. */ + le32 security_id; /* The security_id assigned to the descriptor. */ } __attribute__((__packed__)) SDH_INDEX_KEY; /** @@ -2005,7 +2009,7 @@ typedef enum { * NTFS 1.2. I haven't personally seen other values yet. */ typedef struct { - u64 reserved; /* Not used (yet?). */ + le64 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. */ @@ -2056,11 +2060,11 @@ typedef enum { * start of the index root or index allocation structures themselves. */ typedef struct { -/* 0*/ u32 entries_offset; /* Byte offset from the INDEX_HEADER to first +/* 0*/ le32 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, +/* 4*/ le32 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), +/* 8*/ le32 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 @@ -2103,7 +2107,7 @@ typedef struct { /* 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 +/* 8*/ le32 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 @@ -2127,12 +2131,12 @@ typedef struct { 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. */ + le16 usa_ofs; /* See NTFS_RECORD definition. */ + le16 usa_count; /* See NTFS_RECORD definition. */ -/* 8*/ LSN lsn; /* $LogFile sequence number of the last +/* 8*/ leLSN lsn; /* $LogFile sequence number of the last modification of this index block. */ -/* 16*/ VCN index_block_vcn; /* Virtual cluster number of the index block. */ +/* 16*/ leVCN index_block_vcn; /* Virtual cluster number of the index block. */ /* 24*/ INDEX_HEADER index; /* Describes the following index entries. */ /* sizeof()= 40 (0x28) bytes */ /* @@ -2160,8 +2164,8 @@ typedef INDEX_BLOCK INDEX_ALLOCATION; * 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 + le32 reparse_tag; /* Reparse point type (inc. flags). */ + leMFT_REF file_id; /* Mft record of the file containing the reparse point attribute. */ } __attribute__((__packed__)) REPARSE_INDEX_KEY; @@ -2213,13 +2217,13 @@ typedef enum { * The $Q index entry data is the quota control entry and is defined below. */ typedef struct { - u32 version; /* Currently equals 2. */ + le32 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. */ + le64 bytes_used; /* How many bytes of the quota are in use. */ + sle64 change_time; /* Last time this quota entry was changed. */ + sle64 threshold; /* Soft quota (-1 if not limited). */ + sle64 limit; /* Hard quota (-1 if not limited). */ + sle64 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 @@ -2235,8 +2239,8 @@ typedef struct { * struct QUOTA_O_INDEX_DATA - */ typedef struct { - u32 owner_id; - u32 unknown; /* Always 32. Seems to be padding and it's not + le32 owner_id; + le32 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; @@ -2274,17 +2278,17 @@ typedef enum { */ typedef struct { /* 0*/ union { - MFT_REF indexed_file; + leMFT_REF indexed_file; struct { - u16 data_offset; - u16 data_length; - u32 reservedV; + le16 data_offset; + le16 data_length; + le32 reservedV; } __attribute__((__packed__)); } __attribute__((__packed__)); -/* 8*/ u16 length; -/* 10*/ u16 key_length; +/* 8*/ le16 length; +/* 10*/ le16 key_length; /* 12*/ INDEX_ENTRY_FLAGS flags; -/* 14*/ u16 reserved; +/* 14*/ le16 reserved; /* sizeof() = 16 bytes */ } __attribute__((__packed__)) INDEX_ENTRY_HEADER; @@ -2300,26 +2304,26 @@ typedef struct { 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 + leMFT_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 + le16 data_offset; /* Data byte offset from this INDEX_ENTRY. Follows the index key. */ - u16 data_length; /* Data length in bytes. */ - u32 reservedV; /* Reserved (zero). */ + le16 data_length; /* Data length in bytes. */ + le32 reservedV; /* Reserved (zero). */ } __attribute__((__packed__)); } __attribute__((__packed__)); -/* 8*/ u16 length; /* Byte size of this index entry, multiple of +/* 8*/ le16 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 +/* 10*/ le16 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. */ +/* 14*/ le16 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 @@ -2336,13 +2340,13 @@ typedef struct { 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: + le32 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 + leVCN 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. @@ -2409,6 +2413,7 @@ typedef enum { IO_REPARSE_TAG_SIS = const_cpu_to_le32(0x80000007), IO_REPARSE_TAG_SYMLINK = const_cpu_to_le32(0xA000000C), IO_REPARSE_TAG_WIM = const_cpu_to_le32(0x80000008), + IO_REPARSE_TAG_WOF = const_cpu_to_le32(0x80000017), IO_REPARSE_TAG_VALID_VALUES = const_cpu_to_le32(0xf000ffff), } PREDEFINED_REPARSE_TAGS; @@ -2419,9 +2424,9 @@ 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. */ + le32 reparse_tag; /* Reparse point type (inc. flags). */ + le16 reparse_data_length; /* Byte size of reparse data. */ + le16 reserved; /* Align to 8-byte boundary. */ u8 reparse_data[0]; /* Meaning depends on reparse_tag. */ } __attribute__((__packed__)) REPARSE_POINT; @@ -2431,11 +2436,11 @@ typedef struct { * NOTE: Always resident. */ typedef struct { - u16 ea_length; /* Byte size of the packed extended + le16 ea_length; /* Byte size of the packed extended attributes. */ - u16 need_ea_count; /* The number of extended attributes which have + le16 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 + le32 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 @@ -2462,11 +2467,11 @@ typedef enum { * FIXME: It seems that name is always uppercased. Is it true? */ typedef struct { - u32 next_entry_offset; /* Offset to the next EA_ATTR. */ + le32 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. */ + le16 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. */ @@ -2530,10 +2535,10 @@ 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? */ +/* 0*/ le32 length; /* Length of EFS attribute in bytes. */ + le32 state; /* Always 0? */ + le32 version; /* Efs version. Always 2? */ + le32 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 @@ -2541,20 +2546,20 @@ typedef struct { 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 +/* 64*/ le32 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 + le32 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. */ + le32 reserved; /* Reserved. */ } __attribute__((__packed__)) EFS_ATTR_HEADER; /** * struct EFS_DF_ARRAY_HEADER - */ typedef struct { - u32 df_count; /* Number of data decryption/recovery fields in + le32 df_count; /* Number of data decryption/recovery fields in the array. */ } __attribute__((__packed__)) EFS_DF_ARRAY_HEADER; @@ -2562,25 +2567,25 @@ typedef struct { * struct EFS_DF_HEADER - */ typedef struct { -/* 0*/ u32 df_length; /* Length of this data decryption/recovery +/* 0*/ le32 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 + le32 cred_header_offset; /* Offset in bytes to the credential header. */ + le32 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 + le32 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. */ +/* 16*/ le32 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 +/* 0*/ le32 cred_length; /* Length of this credential in bytes. */ + le32 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: +/* 8*/ le32 type; /* Type of this credential: 1 = CryptoAPI container. 2 = Unexpected type. 3 = Certificate thumbprint. @@ -2588,28 +2593,28 @@ typedef struct { union { /* CryptoAPI container. */ struct { -/* 12*/ u32 container_name_offset; /* Offset in bytes to +/* 12*/ le32 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 +/* 16*/ le32 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 + le32 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 +/* 24*/ le32 public_key_blob_size; /* Size in bytes of public key blob. */ } __attribute__((__packed__)); /* Certificate thumbprint. */ struct { -/* 12*/ u32 cert_thumbprint_header_size; /* Size in +/* 12*/ le32 cert_thumbprint_header_size; /* Size in bytes of the header of the certificate thumbprint. */ -/* 16*/ u32 cert_thumbprint_header_offset; /* Offset in +/* 16*/ le32 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... */ + le32 unknown1; /* Always 0? Might be padding... */ + le32 unknown2; /* Always 0? Might be padding... */ } __attribute__((__packed__)); } __attribute__((__packed__)); } __attribute__((__packed__)) EFS_DF_CREDENTIAL_HEADER; @@ -2620,16 +2625,16 @@ 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 +/* 0*/ le32 thumbprint_offset; /* Offset in bytes to the thumbprint. */ + le32 thumbprint_size; /* Size of thumbprint in bytes. */ +/* 8*/ le32 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 + le32 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 +/* 16*/ le32 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.) */ @@ -2651,8 +2656,8 @@ typedef struct { union { /* For character and block devices. */ struct { - u64 major; /* Major device number. */ - u64 minor; /* Minor device number. */ + le64 major; /* Major device number. */ + le64 minor; /* Minor device number. */ void *device_end[0]; /* Marker for offsetof(). */ } __attribute__((__packed__)); /* For symbolic links. */ diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/lcnalloc.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/lcnalloc.c index e84d2431b5..486e3510ba 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/lcnalloc.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/lcnalloc.c @@ -676,7 +676,7 @@ int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, s64 count) 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); + le32_to_cpu(na->type), (long long)count, (long long)start_vcn); rl = ntfs_attr_find_vcn(na, start_vcn); if (!rl) { diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/logfile.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/logfile.c index 336bdd282a..adc0557f58 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/logfile.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/logfile.c @@ -90,10 +90,10 @@ static BOOL ntfs_check_restart_page_header(RESTART_PAGE_HEADER *rp, s64 pos) * Windows 8, and we will refuse to mount. * Nevertheless, do all the relevant checks before rejecting. */ - if (((rp->major_ver != const_cpu_to_le16(1)) - || (rp->minor_ver != const_cpu_to_le16(1))) - && ((rp->major_ver != const_cpu_to_le16(2)) - || (rp->minor_ver != const_cpu_to_le16(0)))) { + if (((rp->major_ver != const_cpu_to_sle16(1)) + || (rp->minor_ver != const_cpu_to_sle16(1))) + && ((rp->major_ver != const_cpu_to_sle16(2)) + || (rp->minor_ver != const_cpu_to_sle16(0)))) { ntfs_log_error("$LogFile version %i.%i is not " "supported.\n (This driver supports version " "1.1 and 2.0 only.)\n", @@ -119,7 +119,7 @@ static BOOL ntfs_check_restart_page_header(RESTART_PAGE_HEADER *rp, s64 pos) /* 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) || + if (usa_ofs < offsetof(RESTART_PAGE_HEADER, usn) || usa_end > NTFS_BLOCK_SIZE - sizeof(u16)) { ntfs_log_error("$LogFile restart page specifies " "inconsistent update sequence array offset.\n"); @@ -134,7 +134,7 @@ skip_usa_checks: */ 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 < offsetof(RESTART_PAGE_HEADER, usn)) || ra_ofs > logfile_system_page_size) { ntfs_log_error("$LogFile restart page specifies " "inconsistent restart area offset.\n"); diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/logfile.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/logfile.h index 798d562d17..d1f1222ae3 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/logfile.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/logfile.h @@ -2,6 +2,7 @@ * logfile.h - Exports for $LogFile handling. Originated from the Linux-NTFS project. * * Copyright (c) 2000-2005 Anton Altaparmakov + * Copyright (c) 2016 Jean-Pierre Andre * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -93,7 +94,8 @@ typedef struct { version is 1. */ /* 28*/ sle16 major_ver; /* Log file major version. We only support version 1.1. */ -/* sizeof() = 30 (0x1e) bytes */ +/* 30*/ le16 usn; +/* sizeof() = 32 (0x20) bytes */ } __attribute__((__packed__)) RESTART_PAGE_HEADER; /* @@ -101,8 +103,8 @@ typedef struct { * 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(LOGFILE_NO_CLIENT_CPU) /* * These are the so far known RESTART_AREA_* flags (16-bit) which contain @@ -308,35 +310,36 @@ typedef struct { 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. + le16 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. */ + le16 usa_count; /* See NTFS_RECORD definition in layout.h. */ union { - LSN last_lsn; - s64 file_offset; + leLSN last_lsn; + sle64 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; + le32 flags; + le16 page_count; + le16 page_position; + le16 next_record_offset; + le16 reserved[3]; + leLSN last_end_lsn; } __attribute__((__packed__)) RECORD_PAGE_HEADER; /** * enum LOG_RECORD_FLAGS - Possible 16-bit flags for log records. * + * Some flags describe what kind of update is being logged. + * * (Or is it log record pages?) */ typedef enum { LOG_RECORD_MULTI_PAGE = const_cpu_to_le16(0x0001), /* ??? */ + /* The flags below were introduced in Windows 10 */ + LOG_RECORD_DELETING = const_cpu_to_le16(0x0002), + LOG_RECORD_ADDING = const_cpu_to_le16(0x0004), 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. */ @@ -346,47 +349,120 @@ typedef enum { * struct LOG_CLIENT_ID - The log client id structure identifying a log client. */ typedef struct { - u16 seq_number; - u16 client_index; + le16 seq_number; + le16 client_index; } __attribute__((__packed__)) LOG_CLIENT_ID; +/* + * LOG_RECORD_TYPE : types of log records + */ + +enum { + LOG_STANDARD = const_cpu_to_le32(1), + LOG_CHECKPOINT = const_cpu_to_le32(2), + LOG_RECORD_TYPE_PLACE_HOLDER = 0xffffffffU +} ; +typedef le32 LOG_RECORD_TYPE; + +/* + * ATTRIBUTE_FLAGS : flags describing the kind of NTFS record + * is being updated. + * These flags were introduced in Vista, only two flags are known? + */ + +enum { + ACTS_ON_MFT = const_cpu_to_le16(2), + ACTS_ON_INDX = const_cpu_to_le16(8), + ATTRIBUTE_FLAGS_PLACE_HOLDER = 0xffff, +} ; +typedef le16 ATTRIBUTE_FLAGS; + +#define LOG_RECORD_HEAD_SZ 0x30 /* size of header of struct LOG_RECORD */ + /** * 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; + leLSN this_lsn; + leLSN client_previous_lsn; + leLSN client_undo_next_lsn; + le32 client_data_length; LOG_CLIENT_ID client_id; - u32 record_type; - u32 transaction_id; - u16 flags; - u16 reserved_or_alignment[3]; + LOG_RECORD_TYPE record_type; + le32 transaction_id; + LOG_RECORD_FLAGS log_record_flags; + le16 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 + le16 redo_operation; + le16 undo_operation; + le16 redo_offset; + le16 redo_length; + union { + struct { + le16 undo_offset; + le16 undo_length; + le16 target_attribute; + le16 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; + le16 record_offset; + le16 attribute_offset; + le16 cluster_index; + ATTRIBUTE_FLAGS attribute_flags; + leVCN target_vcn; /* Now at ofs 0x50. */ - struct { /* Only present if lcns_to_follow - is not 0. */ - LCN lcn; - } __attribute__((__packed__)) lcn_list[0]; + leLCN lcn_list[0]; /* Only present if lcns_to_follow + is not 0. */ + } __attribute__((__packed__)); + struct { + leLSN transaction_lsn; + leLSN attributes_lsn; + leLSN names_lsn; + leLSN dirty_pages_lsn; + le64 unknown_list[0]; + } __attribute__((__packed__)); + } __attribute__((__packed__)); } __attribute__((__packed__)) LOG_RECORD; +/** + * struct BITMAP_ACTION - Bitmap change being logged + */ + +struct BITMAP_ACTION { + le32 firstbit; + le32 count; +} ; + +/** + * struct ATTR - Attribute record. + * + * The format of an attribute record has changed from Windows 10. + * The old format was 44 bytes long, despite having 8 bytes fields, + * and this leads to alignment problems in arrays. + * This problem does not occur in the new format, which is shorter. + * The format being used can generally be determined from size. + */ +typedef struct { /* Format up to Win10 (44 bytes) */ + le64 unknown1; + le64 unknown2; + le64 inode; + leLSN lsn; + le32 unknown3; + le32 type; + le32 unknown4; +} __attribute__((__packed__)) ATTR_OLD; + +typedef struct { /* Format since Win10 (40 bytes) */ + le64 unknown1; + le64 unknown2; + le32 type; + le32 unknown3; + le64 inode; + leLSN lsn; +} __attribute__((__packed__)) ATTR_NEW; + 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); diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/logging.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/logging.c index 76acdbc932..4f0ce96397 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/logging.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/logging.c @@ -226,6 +226,7 @@ static FILE * ntfs_log_get_stream(u32 level) return stream; } +#endif /** * ntfs_log_get_prefix - Default prefixes for logging levels @@ -297,7 +298,6 @@ void ntfs_log_set_handler(ntfs_log_handler *handler) } else ntfs_log.handler = ntfs_log_handler_null; } -#endif //__HAIKU___ /** * ntfs_log_redirect - Pass on the request to the real handler @@ -359,9 +359,9 @@ int ntfs_log_redirect(const char *function, const char *file, #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 *file __attribute__((unused)), + int line __attribute__((unused)), u32 level, + void *data __attribute__((unused)), const char *format, va_list args) { char logbuf[LOG_LINE_LEN]; @@ -370,20 +370,20 @@ int ntfs_log_handler_syslog(const char *function __attribute__((unused)), #ifndef DEBUG if ((level & NTFS_LOG_LEVEL_PERROR) && errno == ENOSPC) return 1; -#endif +#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; @@ -400,8 +400,10 @@ out: void ntfs_log_early_error(const char *format, ...) { -#ifndef __HAIKU__ va_list args; +#ifdef __HAIKU__ + char buffer[1024]; +#endif va_start(args, format); #ifdef HAVE_SYSLOG_H @@ -409,11 +411,13 @@ void ntfs_log_early_error(const char *format, ...) ntfs_log_handler_syslog(NULL, NULL, 0, NTFS_LOG_LEVEL_ERROR, NULL, format, args); +#elif defined(__HAIKU__) + vsnprintf(buffer, sizeof(buffer) - 1, format, args); + dprintf("%s", buffer); #else vfprintf(stderr,format,args); #endif va_end(args); -#endif //__HAIKU__ } /** @@ -437,7 +441,6 @@ void ntfs_log_early_error(const char *format, ...) * 0 Message wasn't logged * num Number of output characters */ -#ifndef __HAIKU__ int ntfs_log_handler_fprintf(const char *function, const char *file, int line, u32 level, void *data, const char *format, va_list args) { @@ -446,11 +449,17 @@ int ntfs_log_handler_fprintf(const char *function, const char *file, #endif int ret = 0; int olderr = errno; + +#ifndef __HAIKU__ FILE *stream; if (!data) /* Interpret data as a FILE stream. */ return 0; /* If it's NULL, we can't do anything. */ stream = (FILE*)data; +#else +#define fprintf(stream, ...) dprintf(__VA_ARGS__) + char buffer[1024]; +#endif #ifdef DEBUG if (level == NTFS_LOG_LEVEL_LEAVE) { @@ -458,10 +467,10 @@ int ntfs_log_handler_fprintf(const char *function, const char *file, tab--; return 0; } - + for (i = 0; i < tab; i++) ret += fprintf(stream, " "); -#endif +#endif if ((ntfs_log.flags & NTFS_LOG_FLAG_ONLYNAME) && (strchr(file, PATH_SEP))) /* Abbreviate the filename */ file = strrchr(file, PATH_SEP) + 1; @@ -479,7 +488,12 @@ int ntfs_log_handler_fprintf(const char *function, const char *file, (level & NTFS_LOG_LEVEL_TRACE) || (level & NTFS_LOG_LEVEL_ENTER)) ret += fprintf(stream, "%s(): ", function); +#ifndef __HAIKU__ ret += vfprintf(stream, format, args); +#else + ret += vsnprintf(buffer, sizeof(buffer - 1), format, args); + dprintf("%s", buffer); +#endif if (level & NTFS_LOG_LEVEL_PERROR) ret += fprintf(stream, ": %s\n", strerror(olderr)); @@ -487,12 +501,15 @@ int ntfs_log_handler_fprintf(const char *function, const char *file, #ifdef DEBUG if (level == NTFS_LOG_LEVEL_ENTER) tab++; -#endif +#endif +#ifndef __HAIKU__ fflush(stream); +#else +#undef fprintf +#endif errno = olderr; return ret; } -#endif // __HAIKU__ /** * ntfs_log_handler_null - Null logging handler (no output) @@ -537,12 +554,13 @@ int ntfs_log_handler_null(const char *function __attribute__((unused)), const ch * 0 Message wasn't logged * num Number of output characters */ -#ifndef __HAIKU__ int ntfs_log_handler_stdout(const char *function, const char *file, int line, u32 level, void *data, const char *format, va_list args) { +#ifndef __HAIKU__ if (!data) data = stdout; +#endif return ntfs_log_handler_fprintf(function, file, line, level, data, format, args); } @@ -572,8 +590,10 @@ int ntfs_log_handler_stdout(const char *function, const char *file, int ntfs_log_handler_outerr(const char *function, const char *file, int line, u32 level, void *data, const char *format, va_list args) { +#ifndef __HAIKU__ if (!data) data = ntfs_log_get_stream(level); +#endif return ntfs_log_handler_fprintf(function, file, line, level, data, format, args); } @@ -602,12 +622,14 @@ int ntfs_log_handler_outerr(const char *function, const char *file, int ntfs_log_handler_stderr(const char *function, const char *file, int line, u32 level, void *data, const char *format, va_list args) { +#ifndef __HAIKU__ if (!data) data = stderr; +#endif return ntfs_log_handler_fprintf(function, file, line, level, data, format, args); } -#endif //__HAIKU__ + /** * ntfs_log_parse_option - Act upon command line options diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/logging.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/logging.h index 1f55fda288..040caeba73 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/logging.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/logging.h @@ -38,10 +38,11 @@ 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. */ -#ifndef __HAIKU__ -void ntfs_log_set_handler(ntfs_log_handler *handler - __attribute__((format(printf, 6, 0)))); +void ntfs_log_set_handler(ntfs_log_handler *handler +#if __GNUC__ > 3 + __attribute__((format(printf, 6, 0))) #endif + ); /* Logging handlers */ ntfs_log_handler ntfs_log_handler_syslog __attribute__((format(printf, 6, 0))); diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/mft.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/mft.c index 0640efe973..29f1f4bcd9 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/mft.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/mft.c @@ -5,6 +5,7 @@ * Copyright (c) 2004-2005 Richard Russon * Copyright (c) 2004-2008 Szabolcs Szakacsits * Copyright (c) 2005 Yura Pakhuchiy + * Copyright (c) 2014-2015 Jean-Pierre Andre * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -354,7 +355,7 @@ int ntfs_mft_record_layout(const ntfs_volume *vol, const MFT_REF mref, * Set the NTFS 3.1+ specific fields while we know that the * volume version is 3.1+. */ - mrec->reserved = cpu_to_le16(0); + mrec->reserved = const_cpu_to_le16(0); mrec->mft_record_number = cpu_to_le32(MREF(mref)); } mrec->magic = magic_FILE; @@ -362,7 +363,7 @@ int ntfs_mft_record_layout(const ntfs_volume *vol, const MFT_REF mref, mrec->usa_count = cpu_to_le16(vol->mft_record_size / NTFS_BLOCK_SIZE + 1); else { - mrec->usa_count = cpu_to_le16(1); + mrec->usa_count = const_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 " @@ -371,14 +372,14 @@ int ntfs_mft_record_layout(const ntfs_volume *vol, const MFT_REF mref, "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); + *(le16*)((u8*)mrec + le16_to_cpu(mrec->usa_ofs)) = const_cpu_to_le16(1); + mrec->lsn = const_cpu_to_sle64(0ll); + mrec->sequence_number = const_cpu_to_le16(1); + mrec->link_count = const_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); + mrec->flags = const_cpu_to_le16(0); /* * Using attrs_offset plus eight bytes (for the termination attribute), * aligned to 8-byte boundary. @@ -386,11 +387,11 @@ int ntfs_mft_record_layout(const ntfs_volume *vol, const MFT_REF mref, 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); + mrec->base_mft_record = const_cpu_to_le64((MFT_REF)0); + mrec->next_attr_instance = const_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); + a->length = const_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; @@ -553,7 +554,7 @@ static int ntfs_mft_bitmap_find_free_rec(ntfs_volume *vol, ntfs_inode *base_ni) "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); + (u8) (byte ? *byte : -1), b); for (; bit < size && data_pos + bit < pass_end; bit &= ~7ull, bit += 8) { /* @@ -580,7 +581,7 @@ static int ntfs_mft_bitmap_find_free_rec(ntfs_volume *vol, ntfs_inode *base_ni) "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); + (u8) (byte ? *byte : -1), b); data_pos += size; /* * If the end of the pass has not been reached yet, @@ -1354,7 +1355,7 @@ undo_data_init: goto out; } -static ntfs_inode *ntfs_mft_rec_alloc(ntfs_volume *vol) +ntfs_inode *ntfs_mft_rec_alloc(ntfs_volume *vol, BOOL mft_data) { s64 ll, bit; ntfs_attr *mft_na, *mftbmp_na; @@ -1363,6 +1364,7 @@ static ntfs_inode *ntfs_mft_rec_alloc(ntfs_volume *vol) ntfs_inode *base_ni; int err; le16 seq_no, usn; + BOOL forced_mft_data; ntfs_log_enter("Entering\n"); @@ -1371,7 +1373,38 @@ static ntfs_inode *ntfs_mft_rec_alloc(ntfs_volume *vol) base_ni = mft_na->ni; - bit = ntfs_mft_bitmap_find_free_rec(vol, base_ni); + /* + * The first extent containing $MFT:$AT_DATA is better located + * in record 15 to make sure it can be read at mount time. + * The record 15 is prereserved as a base inode with no + * extents and no name, and it is marked in use. + */ + forced_mft_data = FALSE; + if (mft_data) { + ntfs_inode *ext_ni = ntfs_inode_open(vol, FILE_mft_data); + /* + * If record 15 cannot be opened, it is probably in + * use as an extent. Apply standard procedure for + * further extents. + */ + if (ext_ni) { + /* + * Make sure record 15 is a base extent and has + * no extents. + * Also make sure it has no name : a base inode with + * no extents and no name cannot be in use. + * Otherwise apply standard procedure. + */ + if (!ext_ni->mrec->base_mft_record + && !ext_ni->nr_extents) + forced_mft_data = TRUE; + ntfs_inode_close(ext_ni); + } + } + if (forced_mft_data) + bit = FILE_mft_data; + else + bit = ntfs_mft_bitmap_find_free_rec(vol, base_ni); if (bit >= 0) goto found_free_rec; @@ -1408,7 +1441,9 @@ found_free_rec: 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)) { + if (!forced_mft_data + && (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); @@ -1475,7 +1510,7 @@ found_free_rec: 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->flags = const_cpu_to_le32(0); ni->creation_time = ni->last_data_change_time = ni->last_mft_change_time = ni->last_access_time = ntfs_current_time(); @@ -1524,8 +1559,9 @@ err_out: * @base_ni is NULL we start where we last stopped and we perform wrap around * when we reach the end. Note, we do not try to allocate mft records below * number 24 because numbers 0 to 15 are the defined system files anyway and 16 - * to 24 are special in that they are used for storing extension mft records - * for the $DATA attribute of $MFT. This is required to avoid the possibility + * to 24 are used for storing extension mft records or used by chkdsk to store + * its log. However the record number 15 is dedicated to the first extent to + * the $DATA attribute of $MFT. This is required to avoid the possibility * of creating a run list with a circular dependence which once written to disk * can never be read in again. Windows will only use records 16 to 24 for * normal files if the volume is completely out of space. We never use them @@ -1591,7 +1627,9 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, ntfs_inode *base_ni) MFT_RECORD *m; ntfs_inode *ni = NULL; int err; + u32 usa_ofs; le16 seq_no, usn; + BOOL oldwarn; if (base_ni) ntfs_log_enter("Entering (allocating an extent mft record for " @@ -1605,7 +1643,7 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, ntfs_inode *base_ni) } if (ntfs_is_mft(base_ni)) { - ni = ntfs_mft_rec_alloc(vol); + ni = ntfs_mft_rec_alloc(vol, FALSE); goto out; } @@ -1705,10 +1743,22 @@ found_free_rec: if (!m) goto undo_mftbmp_alloc; + /* + * As this is allocating a new record, do not expect it to have + * been initialized previously, so do not warn over bad fixups + * (hence avoid warn flooding when an NTFS partition has been wiped). + */ + oldwarn = !NVolNoFixupWarn(vol); + NVolSetNoFixupWarn(vol); if (ntfs_mft_record_read(vol, bit, m)) { + if (oldwarn) + NVolClearNoFixupWarn(vol); free(m); goto undo_mftbmp_alloc; } + if (oldwarn) + NVolClearNoFixupWarn(vol); + /* 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 " @@ -1717,7 +1767,16 @@ found_free_rec: goto retry; } seq_no = m->sequence_number; - usn = *(le16*)((u8*)m + le16_to_cpu(m->usa_ofs)); + /* + * As ntfs_mft_record_read() returns what has been read + * even when the fixups have been found bad, we have to + * check where we fetch the initial usn from. + */ + usa_ofs = le16_to_cpu(m->usa_ofs); + if (!(usa_ofs & 1) && (usa_ofs < NTFS_BLOCK_SIZE)) { + usn = *(le16*)((u8*)m + usa_ofs); + } else + usn = const_cpu_to_le16(1); if (ntfs_mft_record_layout(vol, bit, m)) { ntfs_log_error("Failed to re-format mft record.\n"); free(m); @@ -1777,7 +1836,7 @@ found_free_rec: 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->flags = const_cpu_to_le32(0); ni->creation_time = ni->last_data_change_time = ni->last_mft_change_time = ni->last_access_time = ntfs_current_time(); diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/mft.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/mft.h index bb15f0f315..b135efce12 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/mft.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/mft.h @@ -124,6 +124,8 @@ 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_rec_alloc(ntfs_volume *vol, BOOL mft_data); + extern int ntfs_mft_record_free(ntfs_volume *vol, ntfs_inode *ni); extern int ntfs_mft_usn_dec(MFT_RECORD *mrec); diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/mst.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/mst.c index b7937b7a41..af17edbc08 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/mst.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/mst.c @@ -31,6 +31,21 @@ #include "mst.h" #include "logging.h" +/* + * Basic validation of a NTFS multi-sector record. The record size must be a + * multiple of the logical sector size; and the update sequence array must be + * properly aligned, of the expected length, and must end before the last le16 + * in the first logical sector. + */ +static BOOL +is_valid_record(u32 size, u16 usa_ofs, u16 usa_count) +{ + return size % NTFS_BLOCK_SIZE == 0 && + usa_ofs % 2 == 0 && + usa_count == 1 + (size / NTFS_BLOCK_SIZE) && + usa_ofs + ((u32)usa_count * 2) <= NTFS_BLOCK_SIZE - 2; +} + /** * ntfs_mst_post_read_fixup - deprotect multi sector transfer protected data * @b: pointer to the data to deprotect @@ -57,12 +72,9 @@ int ntfs_mst_post_read_fixup_warn(NTFS_RECORD *b, const u32 size, /* 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) { + usa_count = le16_to_cpu(b->usa_count); + + if (!is_valid_record(size, usa_ofs, usa_count)) { errno = EINVAL; if (warn) { ntfs_log_perror("%s: magic: 0x%08lx size: %ld " @@ -91,7 +103,7 @@ int ntfs_mst_post_read_fixup_warn(NTFS_RECORD *b, const u32 size, /* * Check for incomplete multi sector transfer(s). */ - while (usa_count--) { + while (--usa_count) { if (*data_pos != usn) { /* * Incomplete multi sector transfer detected! )-: @@ -101,7 +113,7 @@ int ntfs_mst_post_read_fixup_warn(NTFS_RECORD *b, const u32 size, 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, + " %d data: %d usn: %d", le32_to_cpu(*(le32 *)b), size, usa_ofs, usa_count, *data_pos, usn); b->magic = magic_BAAD; return -1; @@ -109,10 +121,10 @@ int ntfs_mst_post_read_fixup_warn(NTFS_RECORD *b, const u32 size, data_pos += NTFS_BLOCK_SIZE/sizeof(u16); } /* Re-setup the variables. */ - usa_count = le16_to_cpu(b->usa_count) - 1; + usa_count = le16_to_cpu(b->usa_count); data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; /* Fixup all sectors. */ - while (usa_count--) { + while (--usa_count) { /* * Increment position in usa and restore original data from * the usa into the data buffer. @@ -157,7 +169,8 @@ int ntfs_mst_post_read_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; + le16 le_usn; + le16 *usa_pos, *data_pos; ntfs_log_trace("Entering\n"); @@ -170,18 +183,15 @@ int ntfs_mst_pre_write_fixup(NTFS_RECORD *b, const u32 size) } /* 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) { + usa_count = le16_to_cpu(b->usa_count); + + if (!is_valid_record(size, usa_ofs, 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); + usa_pos = (le16*)((u8*)b + usa_ofs); /* * Cyclically increment the update sequence number * (skipping 0 and -1, i.e. 0xffff). @@ -189,21 +199,21 @@ int ntfs_mst_pre_write_fixup(NTFS_RECORD *b, const u32 size) 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; + le_usn = cpu_to_le16(usn); + *usa_pos = le_usn; + /* Position in data of first le16 that needs fixing up. */ + data_pos = (le16*)b + NTFS_BLOCK_SIZE/sizeof(le16) - 1; /* Fixup all sectors. */ - while (usa_count--) { + 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; + *data_pos = le_usn; /* Increment position in data as well. */ - data_pos += NTFS_BLOCK_SIZE/sizeof(u16); + data_pos += NTFS_BLOCK_SIZE/sizeof(le16); } return 0; } @@ -222,7 +232,7 @@ void ntfs_mst_post_write_fixup(NTFS_RECORD *b) 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_count = le16_to_cpu(b->usa_count); ntfs_log_trace("Entering\n"); @@ -233,7 +243,7 @@ void ntfs_mst_post_write_fixup(NTFS_RECORD *b) data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; /* Fixup all sectors. */ - while (usa_count--) { + while (--usa_count) { /* * Increment position in usa and restore original data from * the usa into the data buffer. diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/ntfstime.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/ntfstime.h index 236c0fb499..f3a89dd885 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/ntfstime.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/ntfstime.h @@ -102,7 +102,7 @@ static __inline__ ntfs_time timespec2ntfs(struct timespec spec) units = (s64)spec.tv_sec * 10000000 + NTFS_TIME_OFFSET + spec.tv_nsec/100; - return (cpu_to_le64(units)); + return (cpu_to_sle64(units)); } /* diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/object_id.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/object_id.c index 299357e562..bd72d0265e 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/object_id.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/object_id.c @@ -37,11 +37,6 @@ #ifdef HAVE_SYS_STAT_H #include #endif - -#ifdef HAVE_SETXATTR -#include -#endif - #ifdef HAVE_SYS_SYSMACROS_H #include #endif @@ -59,6 +54,7 @@ #include "object_id.h" #include "logging.h" #include "misc.h" +#include "xattrs.h" /* * Endianness considerations @@ -130,7 +126,6 @@ struct OBJECT_ID_INDEX { /* index entry in $Extend/$ObjId */ 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 @@ -176,8 +171,6 @@ static int set_object_id_index(ntfs_inode *ni, ntfs_index_context *xo, return (ntfs_ie_add(xo,(INDEX_ENTRY*)&indx)); } -#endif /* HAVE_SETXATTR */ - /* * Open the $Extend/$ObjId file and its index * @@ -213,7 +206,6 @@ static ntfs_index_context *open_object_id_index(ntfs_volume *vol) return (xo); } -#ifdef HAVE_SETXATTR /* extended attributes interface required */ /* * Merge object_id data stored in the index into @@ -263,7 +255,6 @@ static int merge_index_data(ntfs_inode *ni, return (res); } -#endif /* HAVE_SETXATTR */ /* * Remove an object id index entry if attribute present @@ -311,7 +302,6 @@ static int remove_object_id_index(ntfs_attr *na, ntfs_index_context *xo, return (ret); } -#ifdef HAVE_SETXATTR /* extended attributes interface required */ /* * Update the object id and index @@ -417,7 +407,6 @@ static int add_object_id(ntfs_inode *ni, int flags) return (res); } -#endif /* HAVE_SETXATTR */ /* * Delete an object_id index entry @@ -456,7 +445,6 @@ int ntfs_delete_object_id_index(ntfs_inode *ni) return (res); } -#ifdef HAVE_SETXATTR /* extended attributes interface required */ /* * Get the ntfs object id into an extended attribute @@ -636,5 +624,3 @@ int ntfs_remove_ntfs_object_id(ntfs_inode *ni) } return (res ? -1 : 0); } - -#endif /* HAVE_SETXATTR */ diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/param.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/param.h index da794abba1..23a728cc88 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/param.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/param.h @@ -76,6 +76,14 @@ enum { /* only update the final extent of a runlist when appending data */ #define PARTIAL_RUNLIST_UPDATING 1 +/* + * Parameters for upper-case table + */ + + /* Create upper-case tables as defined by Windows 6.1 (Win7) */ +#define UPCASE_MAJOR 6 +#define UPCASE_MINOR 1 + /* * Parameters for user and xattr mappings */ @@ -99,31 +107,45 @@ enum { * performances, but bad on security with internal fuse or external * fuse older than 2.8 * + * On Linux, cacheing is discouraged for the high level interface + * in order to get proper support of hard links. As a consequence, + * having access control in the file system leads to fewer requests + * to the file system and fewer context switches. + * * Possible values for high level : * 1 : no cache, kernel control (recommended) * 4 : no cache, file system control + * 6 : kernel/fuse cache, file system control (OpenIndiana only) * 7 : no cache, kernel control for ACLs * * Possible values for low level : * 2 : no cache, kernel control - * 3 : use kernel/fuse cache, kernel control (external fuse >= 2.8) - * 5 : no cache, file system control (recommended) + * 3 : use kernel/fuse cache, kernel control (recommended) + * 5 : no cache, file system control + * 6 : kernel/fuse cache, file system control (OpenIndiana only) * 8 : no cache, kernel control for ACLs + * 9 : kernel/fuse cache, kernel control for ACLs (target) * - * Use of options 7 and 8 requires a patch to fuse + * Use of options 7, 8 and 9 requires a fuse module upgrade * When Posix ACLs are selected in the configure options, a value * of 6 is added in the mount report. */ #if defined(__sun) && defined(__SVR4) -#define HPERMSCONFIG 4 /* access control by kernel is broken on OpenIndiana */ -#else +/* + * Access control by kernel is not implemented on OpenIndiana, + * however care is taken of cacheing hard-linked files. + */ +#define HPERMSCONFIG 6 +#define LPERMSCONFIG 6 +#else /* defined(__sun) && defined(__SVR4) */ +/* + * Cacheing by kernel is buggy on Linux when access control is done + * by the file system, and also when using hard-linked files on + * the fuse high level interface. + */ #define HPERMSCONFIG 1 -#endif -#if defined(FUSE_INTERNAL) || !defined(FUSE_VERSION) || (FUSE_VERSION < 28) -#define LPERMSCONFIG 5 -#else -#define LPERMSCONFIG 3 -#endif +#define LPERMSCONFIG 3 /* Use 9 when ACLs are supported by fuse kernel */ +#endif /* defined(__sun) && defined(__SVR4) */ #endif /* defined _NTFS_PARAM_H */ diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/plugin.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/plugin.h new file mode 100644 index 0000000000..d32e149d39 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/plugin.h @@ -0,0 +1,141 @@ +/* + * plugin.h : define interface for plugin development + * + * Copyright (c) 2015 Jean-Pierre Andre + * + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * This file defines the interface to ntfs-3g plugins which + * add support for processing some type of reparse points. + */ + +#ifndef _NTFS_PLUGIN_H +#define _NTFS_PLUGIN_H + +#include "inode.h" +#include "layout.h" + +struct fuse_file_info; +struct stat; + + /* + * The plugin operations currently defined. + * These functions should return a non-negative value when they + * succeed, or a negative errno value when they fail. + * They must not close or free their arguments. + * The file system must be left in a consistent state after + * each individual call. + * If an operation is not defined, an EOPNOTSUPP error is + * returned to caller. + */ +typedef struct plugin_operations { + /* + * Set the attributes st_size, st_blocks and st_mode + * into a struct stat. The returned st_mode must at least + * define the file type. Depending on the permissions options + * used for mounting, the umask will be applied to the returned + * permissions, or the permissions will be changed according + * to the ACL set on the file. + */ + int (*getattr)(ntfs_inode *ni, const REPARSE_POINT *reparse, + struct stat *stbuf); + + /* + * Open a file for reading or writing + * The field fi->flags indicates the kind of opening. + * The field fi->fh may be used to store some information which + * will be available to subsequent reads and writes. When used + * this field must be non-null. + * The returned value is zero for success and a negative errno + * value for failure. + */ + int (*open)(ntfs_inode *ni, const REPARSE_POINT *reparse, + struct fuse_file_info *fi); + + /* + * Release an open file + * This is only called if fi->fh has been set to a non-null + * value while opening. It may be used to free some context + * specific to the open file. + * The returned value is zero for success or a negative errno + * value for failure. + */ + int (*release)(ntfs_inode *ni, const REPARSE_POINT *reparse, + struct fuse_file_info *fi); + + /* + * Read from an open file + * The returned value is the count of bytes which were read + * or a negative errno value for failure. + * If the returned value is positive, the access time stamp + * will be updated after the call. + */ + int (*read)(ntfs_inode *ni, const REPARSE_POINT *reparse, + char *buf, size_t size, + off_t offset, struct fuse_file_info *fi); + + /* + * Write to an open file + * The file system must be left consistent after each write call, + * the file itself must be at least deletable if the application + * writing to it is killed for some reason. + * The returned value is the count of bytes which were written + * or a negative errno value for failure. + * If the returned value is positive, the modified time stamp + * will be updated after the call. + */ + int (*write)(ntfs_inode *ni, const REPARSE_POINT *reparse, + const char *buf, size_t size, + off_t offset, struct fuse_file_info *fi); + + /* + * Get a symbolic link + * The symbolic link must be returned in an allocated buffer, + * encoded in a zero terminated multibyte string compatible + * which the locale mount option. + * The returned value is zero for success or a negative errno + * value for failure. + */ + int (*readlink)(ntfs_inode *ni, const REPARSE_POINT *reparse, + char **pbuf); + + /* + * Truncate a file (shorten or append zeroes) + * The returned value is zero for success or a negative errno + * value for failure. + * If the returned value is zero, the modified time stamp + * will be updated after the call. + */ + int (*truncate)(ntfs_inode *ni, const REPARSE_POINT *reparse, + off_t size); +} plugin_operations_t; + + +/* + * Plugin initialization routine + * Returns the entry table if successful, otherwise returns NULL + * and sets errno (e.g. to EINVAL if the tag is not supported by + * the plugin.) + */ +typedef const struct plugin_operations *(*plugin_init_t)(le32 tag); +const struct plugin_operations *init(le32 tag); + +#endif /* _NTFS_PLUGIN_H */ diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/reparse.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/reparse.c index a47b5c8e9c..2e92fbb629 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/reparse.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/reparse.c @@ -3,7 +3,7 @@ * * This module is part of ntfs-3g library * - * Copyright (c) 2008-2013 Jean-Pierre Andre + * Copyright (c) 2008-2016 Jean-Pierre Andre * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -37,11 +37,6 @@ #ifdef HAVE_SYS_STAT_H #include #endif - -#ifdef HAVE_SETXATTR -#include -#endif - #ifdef HAVE_SYS_SYSMACROS_H #include #endif @@ -60,6 +55,7 @@ #include "logging.h" #include "misc.h" #include "reparse.h" +#include "xattrs.h" struct MOUNT_POINT_REPARSE_DATA { /* reparse data for junctions */ le16 subst_name_offset; @@ -195,7 +191,7 @@ static u64 ntfs_fix_file_name(ntfs_inode *dir_ni, ntfschar *uname, uname[i] = found->file_name[i]; } else { for (i=0; ifile_name_length; i++) - uname[i] = vol->locase[found->file_name[i]]; + uname[i] = vol->locase[le16_to_cpu(found->file_name[i])]; } } } @@ -401,8 +397,8 @@ static int ntfs_drive_letter(ntfs_volume *vol, ntfschar letter) strcpy(defines,mappingdir); if ((*drive >= 'a') && (*drive <= 'z')) *drive += 'A' - 'a'; - strlcat(defines, drive, sizeof(defines)); - strlcat(defines, ":", sizeof(defines)); + strcat(defines,drive); + strcat(defines,":"); olderrno = errno; ni = ntfs_pathname_to_inode(vol, NULL, defines); if (ni && !ntfs_inode_close(ni)) @@ -422,8 +418,10 @@ static int ntfs_drive_letter(ntfs_volume *vol, ntfschar letter) /* * Do some sanity checks on reparse data * - * The only general check is about the size (at least the tag must - * be present) + * Microsoft reparse points have an 8-byte header whereas + * non-Microsoft reparse points have a 24-byte header. In each case, + * 'reparse_data_length' must equal the number of non-header bytes. + * * If the reparse data looks like a junction point or symbolic * link, more checks can be done. * @@ -440,11 +438,19 @@ static BOOL valid_reparse_data(ntfs_inode *ni, ok = ni && reparse_attr && (size >= sizeof(REPARSE_POINT)) + && (reparse_attr->reparse_tag != IO_REPARSE_TAG_RESERVED_ZERO) && (((size_t)le16_to_cpu(reparse_attr->reparse_data_length) - + sizeof(REPARSE_POINT)) == size); + + sizeof(REPARSE_POINT) + + ((reparse_attr->reparse_tag & + IO_REPARSE_TAG_IS_MICROSOFT) ? 0 : sizeof(GUID))) == size); if (ok) { switch (reparse_attr->reparse_tag) { case IO_REPARSE_TAG_MOUNT_POINT : + if (size < sizeof(REPARSE_POINT) + + sizeof(struct MOUNT_POINT_REPARSE_DATA)) { + ok = FALSE; + break; + } mount_point_data = (const struct MOUNT_POINT_REPARSE_DATA*) reparse_attr->reparse_data; offs = le16_to_cpu(mount_point_data->subst_name_offset); @@ -457,6 +463,11 @@ static BOOL valid_reparse_data(ntfs_inode *ni, ok = FALSE; break; case IO_REPARSE_TAG_SYMLINK : + if (size < sizeof(REPARSE_POINT) + + sizeof(struct SYMLINK_REPARSE_DATA)) { + ok = FALSE; + break; + } symlink_data = (const struct SYMLINK_REPARSE_DATA*) reparse_attr->reparse_data; offs = le16_to_cpu(symlink_data->subst_name_offset); @@ -713,8 +724,7 @@ static char *ntfs_get_rellink(ntfs_inode *ni, ntfschar *junction, int count) * 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) { s64 attr_size = 0; char *target; @@ -809,7 +819,6 @@ char *ntfs_make_symlink(ntfs_inode *ni, const char *mnt_point, } free(reparse_attr); } - *pattr_size = attr_size; if (bad) errno = EOPNOTSUPP; return (target); @@ -844,7 +853,6 @@ BOOL ntfs_possible_symlink(ntfs_inode *ni) return (possible); } -#ifdef HAVE_SETXATTR /* extended attributes interface required */ /* * Set the index for new reparse data @@ -883,7 +891,6 @@ static int set_reparse_index(ntfs_inode *ni, ntfs_index_context *xr, return (ntfs_ie_add(xr,(INDEX_ENTRY*)&indx)); } -#endif /* HAVE_SETXATTR */ /* * Remove a reparse data index entry if attribute present @@ -960,7 +967,6 @@ static ntfs_index_context *open_reparse_index(ntfs_volume *vol) return (xr); } -#ifdef HAVE_SETXATTR /* extended attributes interface required */ /* * Update the reparse data and index @@ -1026,7 +1032,6 @@ static int update_reparse_data(ntfs_inode *ni, ntfs_index_context *xr, return (res); } -#endif /* HAVE_SETXATTR */ /* * Delete a reparse index entry @@ -1065,7 +1070,6 @@ int ntfs_delete_reparse_index(ntfs_inode *ni) return (res); } -#ifdef HAVE_SETXATTR /* extended attributes interface required */ /* * Get the ntfs reparse data into an extended attribute @@ -1117,7 +1121,11 @@ int ntfs_set_ntfs_reparse_data(ntfs_inode *ni, ntfs_index_context *xr; res = 0; - if (ni && valid_reparse_data(ni, (const REPARSE_POINT*)value, size)) { + /* reparse data is not compatible with EA */ + if (ni + && !ntfs_attr_exist(ni, AT_EA_INFORMATION, AT_UNNAMED, 0) + && !ntfs_attr_exist(ni, AT_EA, AT_UNNAMED, 0) + && 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, @@ -1246,4 +1254,31 @@ int ntfs_remove_ntfs_reparse_data(ntfs_inode *ni) return (res ? -1 : 0); } -#endif /* HAVE_SETXATTR */ + +/* + * Get the reparse data into a buffer + * + * Returns the buffer if the reparse data exists and is valid + * NULL otherwise (with errno set according to the cause). + * When a buffer is returned, it has to be freed by caller. + */ + +REPARSE_POINT *ntfs_get_reparse_point(ntfs_inode *ni) +{ + s64 attr_size = 0; + REPARSE_POINT *reparse_attr; + + reparse_attr = (REPARSE_POINT*)NULL; + if (ni) { + reparse_attr = (REPARSE_POINT*)ntfs_attr_readall(ni, + AT_REPARSE_POINT,(ntfschar*)NULL, 0, &attr_size); + if (reparse_attr + && !valid_reparse_data(ni, reparse_attr, attr_size)) { + free(reparse_attr); + reparse_attr = (REPARSE_POINT*)NULL; + errno = ENOENT; + } + } else + errno = EINVAL; + return (reparse_attr); +} diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/reparse.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/reparse.h index 35f4aa4562..76af9150bf 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/reparse.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/reparse.h @@ -24,12 +24,14 @@ #ifndef REPARSE_H #define REPARSE_H -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); + BOOL ntfs_possible_symlink(ntfs_inode *ni); int ntfs_get_ntfs_reparse_data(ntfs_inode *ni, char *value, size_t size); +REPARSE_POINT *ntfs_get_reparse_point(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); diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/runlist.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/runlist.c index 7e158d443a..c83c2b7dda 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/runlist.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/runlist.c @@ -939,40 +939,45 @@ mpa_err: "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) + + /* + * If this is the base of runlist (if 'lowest_vcn' is 0), then + * 'allocated_size' is valid, and we can use it to compute the total + * number of clusters across all extents. If the runlist covers all + * clusters, then it fits into a single extent and we can terminate + * the runlist with LCN_NOENT. Otherwise, we must terminate the runlist + * with LCN_RL_NOT_MAPPED and let the caller look for more extents. + */ + if (!attr->lowest_vcn) { + VCN num_clusters; + + num_clusters = ((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) { + vol->cluster_size_bits); + + if (num_clusters > vcn) { /* - * 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. + * The runlist doesn't cover all the clusters, so there + * must be more extents. */ - 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; - } + ntfs_log_debug("More extents to follow; vcn = 0x%llx, " + "num_clusters = 0x%llx\n", + (long long)vcn, + (long long)num_clusters); + rl[rlpos].vcn = vcn; + vcn += rl[rlpos].length = num_clusters - vcn; + rl[rlpos].lcn = (LCN)LCN_RL_NOT_MAPPED; + rlpos++; + } else if (vcn > num_clusters) { + /* + * There are more VCNs in the runlist than expected, so + * the runlist is corrupt. + */ + ntfs_log_error("Corrupt attribute. vcn = 0x%llx, " + "num_clusters = 0x%llx\n", + (long long)vcn, + (long long)num_clusters); + goto mpa_err; } rl[rlpos].lcn = (LCN)LCN_ENOENT; } else /* Not the base extent. There may be more extents to follow. */ diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/security.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/security.c index 8ab23efc56..b4a71ffe5a 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/security.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/security.c @@ -4,7 +4,7 @@ * Copyright (c) 2004 Anton Altaparmakov * Copyright (c) 2005-2006 Szabolcs Szakacsits * Copyright (c) 2006 Yura Pakhuchiy - * Copyright (c) 2007-2012 Jean-Pierre Andre + * Copyright (c) 2007-2015 Jean-Pierre Andre * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -41,9 +41,6 @@ #ifdef HAVE_FCNTL_H #include #endif -#ifdef HAVE_SETXATTR -#include -#endif #ifdef HAVE_SYS_STAT_H #include #endif @@ -52,6 +49,13 @@ #include #include +#ifdef __HAIKU__ +#define getgrgid(a) NULL +#define getpwuid(a) NULL +#define getgrnam(x) NULL +#define getpwnam(x) NULL +#endif + #include "compat.h" #include "param.h" #include "types.h" @@ -64,12 +68,7 @@ #include "acls.h" #include "cache.h" #include "misc.h" - -#ifdef __HAIKU__ -#define getgrgid(a) NULL -#define getpwuid(a) NULL -#endif - +#include "xattrs.h" /* * JPA NTFS constants or structs @@ -134,8 +133,8 @@ struct SDH { /* this is an image of an $SDH index entry */ 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('I'), + const_cpu_to_le16('I'), const_cpu_to_le16(0) }; static ntfschar sdh_stream[] = { const_cpu_to_le16('$'), const_cpu_to_le16('S'), @@ -230,7 +229,7 @@ int ntfs_sid_to_mbs_size(const SID *sid) { int size, i; - if (!ntfs_sid_is_valid(sid)) { + if (!ntfs_valid_sid(sid)) { errno = EINVAL; return -1; } @@ -304,7 +303,7 @@ char *ntfs_sid_to_mbs(const SID *sid, char *sid_str, size_t sid_str_size) * 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))) { + if (sid_str && (sid_str_size < 8 || !ntfs_valid_sid(sid))) { errno = EINVAL; return NULL; } @@ -520,9 +519,18 @@ static int entersecurity_data(ntfs_volume *vol, STREAM_SDS, 4, fullattr, fullsz, offs - gap + ALIGN_SDS_BLOCK); if ((written1 == fullsz) - && (written2 == written1)) - res = 0; - else + && (written2 == written1)) { + /* + * Make sure the data size for $SDS marks the end + * of the last security attribute. Windows uses + * this to determine where the next attribute will + * be written, which causes issues if chkdsk had + * previously deleted the last entries without + * adjusting the size. + */ + res = ntfs_attr_shrink_size(vol->secure_ni,STREAM_SDS, + 4, offs - gap + ALIGN_SDS_BLOCK + fullsz); + } else errno = ENOSPC; free(fullattr); } else @@ -693,7 +701,7 @@ static le32 entersecurityattr(ntfs_volume *vol, retries = 0; while (entry) { next = ntfs_index_next(entry,xsii); - if (next) { + if (next) { psii = (struct SII*)next; /* save last key and */ /* available position */ @@ -713,10 +721,24 @@ static le32 entersecurityattr(ntfs_volume *vol, sizeof(SII_INDEX_KEY), xsii); if (!found && (errno != ENOENT)) { ntfs_log_perror("Index $SII is broken"); + psii = (struct SII*)NULL; } else { /* restore errno */ errno = olderrno; entry = xsii->entry; + psii = (struct SII*)entry; + } + if (psii + && !(psii->flags & INDEX_ENTRY_END)) { + /* save first 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); } retries++; } @@ -731,7 +753,8 @@ static le32 entersecurityattr(ntfs_volume *vol, 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)) { + if ((size_t)na->data_size < (sizeof(struct SII) + + sizeof(INDEX_ENTRY_HEADER))) { ntfs_log_error("Creating the first security_id\n"); securid = const_cpu_to_le32(FIRST_SECURITY_ID); } @@ -1033,7 +1056,7 @@ static int update_secur_descr(ntfs_volume *vol, * 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 * @@ -1114,7 +1137,7 @@ static int upgrade_secur_desc(ntfs_volume *vol, * Should not be called for user root, however the group may be root * */ -#ifndef __HAIKU__ + static BOOL staticgroupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid) { BOOL ingroup; @@ -1136,7 +1159,6 @@ static BOOL staticgroupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid } return (ingroup); } -#endif //__HAIKU__ #if defined(__sun) && defined (__SVR4) @@ -1329,6 +1351,48 @@ static BOOL groupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid) #endif /* defined(__sun) && defined (__SVR4) */ +#if POSIXACLS + +/* + * Extract the basic permissions from a Posix ACL + * + * This is only to be used when Posix ACLs are compiled in, + * but not enabled in the mount options. + * + * it replaces the permission mask by the group permissions. + * If special groups are mapped, they are also considered as world. + */ + +static int ntfs_basic_perms(const struct SECURITY_CONTEXT *scx, + const struct POSIX_SECURITY *pxdesc) +{ + int k; + int perms; + const struct POSIX_ACE *pace; + const struct MAPPING* group; + + k = 0; + perms = pxdesc->mode; + for (k=0; k < pxdesc->acccnt; k++) { + pace = &pxdesc->acl.ace[k]; + if (pace->tag == POSIX_ACL_GROUP_OBJ) + perms = (perms & 07707) + | ((pace->perms & 7) << 3); + else + if (pace->tag == POSIX_ACL_GROUP) { + group = scx->mapping[MAPGROUPS]; + while (group && (group->xid != pace->id)) + group = group->next; + if (group && group->grcnt + && (*(group->groups) == (gid_t)pace->id)) + perms |= pace->perms & 7; + } + } + return (perms); +} + +#endif /* POSIXACLS */ + /* * Cacheing is done two-way : * - from uid, gid and perm to securid (CACHED_SECURID) @@ -1467,7 +1531,7 @@ static int leg_compare(const struct CACHED_PERMISSIONS_LEGACY *cached, /* * 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. @@ -1829,7 +1893,7 @@ static char *getsecurityattr(ntfs_volume *vol, ntfs_inode *ni) * attribute */ if (test_nino_flag(ni, v3_Extensions) - && vol->secure_ni && ni->security_id) { + && vol->secure_ni && ni->security_id) { /* get v3.x descriptor in $Secure */ securid.security_id = ni->security_id; securattr = retrievesecurityattr(vol,securid); @@ -1870,7 +1934,9 @@ static char *getsecurityattr(ntfs_volume *vol, ntfs_inode *ni) * 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 + * When Posix ACLs are compiled in but not enabled in the mount + * options POSIX_ACL_USER, POSIX_ACL_GROUP and POSIX_ACL_MASK + * are ignored. */ static int access_check_posix(struct SECURITY_CONTEXT *scx, @@ -1883,10 +1949,15 @@ static int access_check_posix(struct SECURITY_CONTEXT *scx, int mask; BOOL somegroup; BOOL needgroups; + BOOL noacl; mode_t perms; int i; - perms = pxdesc->mode; + noacl = !(scx->vol->secure_flags & (1 << SECURITY_ACL)); + if (noacl) + perms = ntfs_basic_perms(scx, pxdesc); + else + perms = pxdesc->mode; /* owner and root access */ if (!scx->uid || (uid == scx->uid)) { if (!scx->uid) { @@ -1902,11 +1973,16 @@ static int access_check_posix(struct SECURITY_CONTEXT *scx, switch (pxace->tag) { case POSIX_ACL_USER_OBJ : case POSIX_ACL_GROUP_OBJ : - case POSIX_ACL_GROUP : groupperms |= pxace->perms; break; + case POSIX_ACL_GROUP : + if (!noacl) + groupperms + |= pxace->perms; + break; case POSIX_ACL_MASK : - mask = pxace->perms & 7; + if (!noacl) + mask = pxace->perms & 7; break; default : break; @@ -1933,18 +2009,25 @@ static int access_check_posix(struct SECURITY_CONTEXT *scx, pxace = &pxdesc->acl.ace[i]; switch (pxace->tag) { case POSIX_ACL_USER : - if ((uid_t)pxace->id == scx->uid) + if (!noacl + && ((uid_t)pxace->id == scx->uid)) userperms = pxace->perms; break; case POSIX_ACL_MASK : - mask = pxace->perms & 7; + if (!noacl) + 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; + case POSIX_ACL_GROUP : + if (!noacl + && (((pxace->perms & mask) ^ perms) + & (request >> 6) & 7)) + needgroups = TRUE; + break; default : break; } @@ -1960,14 +2043,14 @@ static int access_check_posix(struct SECURITY_CONTEXT *scx, && ((gid == scx->gid) || groupmember(scx, scx->uid, gid))) perms &= 07070; - else { + else if (!noacl) { /* 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)) { + && groupmember(scx, scx->uid, pxace->id)) { if (!(~pxace->perms & request & mask)) groupperms = pxace->perms; somegroup = TRUE; @@ -1980,7 +2063,8 @@ static int access_check_posix(struct SECURITY_CONTEXT *scx, perms = 0; else perms &= 07007; - } + } else + perms &= 07007; } } return (perms); @@ -2097,7 +2181,7 @@ static int ntfs_get_perm(struct SECURITY_CONTEXT *scx, */ int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, - const char *name, char *value, size_t size) + const char *name, char *value, size_t size) { const SECURITY_DESCRIPTOR_RELATIVE *phead; struct POSIX_SECURITY *pxdesc; @@ -2385,7 +2469,13 @@ int ntfs_get_owner_mode(struct SECURITY_CONTEXT *scx, /* check whether available in cache */ cached = fetch_cache(scx,ni); if (cached) { - perm = cached->mode; +#if POSIXACLS + if (!(scx->vol->secure_flags & (1 << SECURITY_ACL)) + && cached->pxdesc) + perm = ntfs_basic_perms(scx,cached->pxdesc); + else +#endif + perm = cached->mode; stbuf->st_uid = cached->uid; stbuf->st_gid = cached->gid; stbuf->st_mode = (stbuf->st_mode & ~07777) + perm; @@ -2407,11 +2497,17 @@ int ntfs_get_owner_mode(struct SECURITY_CONTEXT *scx, 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 + pxdesc = ntfs_build_permissions_posix( + scx->mapping, securattr, + usid, gsid, isdir); + if (pxdesc) { + if (!(scx->vol->secure_flags + & (1 << SECURITY_ACL))) + perm = ntfs_basic_perms(scx, + pxdesc); + else + perm = pxdesc->mode & 07777; + } else perm = -1; #else perm = ntfs_build_permissions(securattr, @@ -2435,7 +2531,7 @@ int ntfs_get_owner_mode(struct SECURITY_CONTEXT *scx, stbuf->st_uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); #else if (!perm && ntfs_same_sid(usid, adminsid)) { - stbuf->st_uid = + stbuf->st_uid = find_tenant(scx, securattr); if (stbuf->st_uid) @@ -2490,8 +2586,12 @@ static struct POSIX_SECURITY *inherit_posix(struct SECURITY_CONTEXT *scx, gid = cached->gid; pxdesc = cached->pxdesc; if (pxdesc) { - pydesc = ntfs_build_inherited_posix(pxdesc,mode, - scx->umask,isdir); + if (scx->vol->secure_flags & (1 << SECURITY_ACL)) + pydesc = ntfs_build_inherited_posix(pxdesc, + mode, scx->umask, isdir); + else + pydesc = ntfs_build_basic_posix(pxdesc, + mode, scx->umask, isdir); } } else { securattr = getsecurityattr(scx->vol, dir_ni); @@ -2535,8 +2635,15 @@ static struct POSIX_SECURITY *inherit_posix(struct SECURITY_CONTEXT *scx, enter_cache(scx, dir_ni, uid, gid, pxdesc); } - pydesc = ntfs_build_inherited_posix(pxdesc, - mode, scx->umask, isdir); + if (scx->vol->secure_flags + & (1 << SECURITY_ACL)) + pydesc = ntfs_build_inherited_posix( + pxdesc, mode, + scx->umask, isdir); + else + pydesc = ntfs_build_basic_posix( + pxdesc, mode, + scx->umask, isdir); free(pxdesc); } free(securattr); @@ -2547,7 +2654,7 @@ static struct POSIX_SECURITY *inherit_posix(struct SECURITY_CONTEXT *scx, /* * Allocate a security_id for a file being created - * + * * Returns zero if not possible (NTFS v3.x required) */ @@ -2769,7 +2876,7 @@ le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx, /* * Update ownership and mode of a file, reusing an existing * security descriptor when possible - * + * * Returns zero if successful */ @@ -2820,6 +2927,14 @@ int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, if (cached) { ni->security_id = cached->securid; NInoSetDirty(ni); + /* adjust Windows read-only flag */ + if (!isdir) { + if (mode & S_IWUSR) + ni->flags &= ~FILE_ATTR_READONLY; + else + ni->flags |= FILE_ATTR_READONLY; + NInoFileNameSetDirty(ni); + } } } else cached = (struct CACHED_SECURID*)NULL; @@ -2959,7 +3074,6 @@ BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni) return (allowed); } -#ifdef HAVE_SETXATTR /* extended attributes interface required */ #if POSIXACLS @@ -3138,7 +3252,6 @@ int ntfs_set_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, return (res ? -1 : 0); } -#endif /* HAVE_SETXATTR */ /* * Set new permissions to a file @@ -3268,7 +3381,7 @@ int ntfs_sd_add_everyone(ntfs_inode *ni) 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 @@ -3276,14 +3389,14 @@ int ntfs_sd_add_everyone(ntfs_inode *ni) * 4 bytes to every SID. */ sd_len = sizeof(SECURITY_DESCRIPTOR_ATTR) + 2 * (sizeof(SID) + 4) + - sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE); + 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; @@ -3291,21 +3404,21 @@ int ntfs_sd_add_everyone(ntfs_inode *ni) 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 = (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; @@ -3320,7 +3433,7 @@ int ntfs_sd_add_everyone(ntfs_inode *ni) sd_len); if (ret) ntfs_log_perror("Failed to add initial SECURITY_DESCRIPTOR"); - + free(sd); return ret; } @@ -3469,7 +3582,7 @@ int ntfs_allowed_create(struct SECURITY_CONTEXT *scx, * * 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 * * This is used for Posix ACL and checking creation of DOS file names @@ -3619,7 +3732,7 @@ int ntfs_set_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, mode &= 01777; #endif #if POSIXACLS - res = ntfs_set_owner_mode(scx, ni, uid, gid, + res = ntfs_set_owner_mode(scx, ni, uid, gid, mode, pxdesc); #else res = ntfs_set_owner_mode(scx, ni, uid, gid, mode); @@ -3735,7 +3848,7 @@ int ntfs_set_ownmod(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, if ((int)gid < 0) gid = filegid; #if POSIXACLS - res = ntfs_set_owner_mode(scx, ni, uid, gid, + res = ntfs_set_owner_mode(scx, ni, uid, gid, mode, newpxdesc); #else res = ntfs_set_owner_mode(scx, ni, uid, gid, mode); @@ -3774,7 +3887,6 @@ static le32 build_inherited_id(struct SECURITY_CONTEXT *scx, BIGSID defusid; BIGSID defgsid; int offpacl; - int offowner; int offgroup; SECURITY_DESCRIPTOR_RELATIVE *pnhead; ACL *pnacl; @@ -3792,21 +3904,48 @@ static le32 build_inherited_id(struct SECURITY_CONTEXT *scx, 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 OWNERFROMACL + /* Get approximation of parent owner when cannot map */ + if (!gsid) + gsid = adminsid; + if (!usid) { + usid = ntfs_acl_owner(parentattr); + if (!ntfs_is_user_sid(gsid)) + gsid = usid; + } +#else + /* Define owner as root when cannot map */ if (!usid) usid = adminsid; if (!gsid) gsid = adminsid; +#endif } else { /* - * If there is no user mapping, we have to copy owner - * and group from parent directory. + * If there is no user mapping and this is not a root + * user, we have to get owner and group from somewhere, + * and the parent directory has to contribute. * 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]; + if (!scx->uid) + usid = adminsid; + else { +#if OWNERFROMACL + usid = ntfs_acl_owner(parentattr); +#else + int offowner; + + offowner = le32_to_cpu(pphead->owner); + usid = (const SID*)&parentattr[offowner]; +#endif + } + if (!scx->gid) + gsid = adminsid; + else { + offgroup = le32_to_cpu(pphead->group); + gsid = (const SID*)&parentattr[offgroup]; + } } /* * new attribute is smaller than parent's @@ -3911,12 +4050,14 @@ le32 ntfs_inherited_id(struct SECURITY_CONTEXT *scx, securid = const_cpu_to_le32(0); cached = (struct CACHED_PERMISSIONS*)NULL; /* - * Try to get inherited id from cache + * Try to get inherited id from cache, possible when + * the current process owns the parent directory */ if (test_nino_flag(dir_ni, v3_Extensions) && dir_ni->security_id) { cached = fetch_cache(scx, dir_ni); - if (cached) + if (cached + && (cached->uid == scx->uid) && (cached->gid == scx->gid)) securid = (fordir ? cached->inh_dirid : cached->inh_fileid); } @@ -3932,10 +4073,13 @@ le32 ntfs_inherited_id(struct SECURITY_CONTEXT *scx, free(parentattr); /* * Store the result into cache for further use + * if the current process owns the parent directory */ if (securid) { cached = fetch_cache(scx, dir_ni); - if (cached) { + if (cached + && (cached->uid == scx->uid) + && (cached->gid == scx->gid)) { if (fordir) cached->inh_dirid = securid; else @@ -4248,7 +4392,6 @@ int ntfs_build_mapping(struct SECURITY_CONTEXT *scx, const char *usermap_path, return (!scx->mapping[MAPUSERS] || link_group_members(scx)); } -#ifdef HAVE_SETXATTR /* extended attributes interface required */ /* * Get the ntfs attribute into an extended attribute @@ -4334,58 +4477,83 @@ int ntfs_set_ntfs_attrib(ntfs_inode *ni, 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 the volume's security descriptor index ($Secure) + * + * returns 0 if it succeeds + * -1 with errno set if it fails and the volume is NTFS v3.0+ */ - - int ntfs_open_secure(ntfs_volume *vol) { ntfs_inode *ni; - int res; + ntfs_index_context *sii; + ntfs_index_context *sdh; - 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; - } - } + if (vol->secure_ni) /* Already open? */ + return 0; + + ni = ntfs_pathname_to_inode(vol, NULL, "$Secure"); + if (!ni) + goto err; + + if (ni->mft_no != FILE_Secure) { + ntfs_log_error("$Secure does not have expected inode number!"); + errno = EINVAL; + goto err_close_ni; } - return (res); + + /* Allocate the needed index contexts. */ + sii = ntfs_index_ctx_get(ni, sii_stream, 4); + if (!sii) + goto err_close_ni; + + sdh = ntfs_index_ctx_get(ni, sdh_stream, 4); + if (!sdh) + goto err_close_sii; + + vol->secure_xsdh = sdh; + vol->secure_xsii = sii; + vol->secure_ni = ni; + return 0; + +err_close_sii: + ntfs_index_ctx_put(sii); +err_close_ni: + ntfs_inode_close(ni); +err: + /* Failing on NTFS pre-v3.0 is expected. */ + if (vol->major_ver < 3) + return 0; + ntfs_log_perror("Failed to open $Secure"); + return -1; } /* - * Final cleaning + * Close the volume's security descriptor index ($Secure) + * + * returns 0 if it succeeds + * -1 with errno set if it fails + */ +int ntfs_close_secure(ntfs_volume *vol) +{ + int res = 0; + + if (vol->secure_ni) { + ntfs_index_ctx_put(vol->secure_xsdh); + ntfs_index_ctx_put(vol->secure_xsii); + res = ntfs_inode_close(vol->secure_ni); + vol->secure_ni = NULL; + } + return res; +} + +/* + * Destroy a security context * Allocated memory is freed to facilitate the detection of memory leaks */ - -void ntfs_close_secure(struct SECURITY_CONTEXT *scx) +void ntfs_destroy_security_context(struct SECURITY_CONTEXT *scx) { - 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); } @@ -5204,7 +5372,6 @@ struct SECURITY_API *ntfs_initialize_file_security(const char *device, 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); @@ -5236,7 +5403,7 @@ BOOL ntfs_leave_file_security(struct SECURITY_API *scapi) ok = FALSE; if (scapi && (scapi->magic == MAGIC_API) && scapi->security.vol) { vol = scapi->security.vol; - ntfs_close_secure(&scapi->security); + ntfs_destroy_security_context(&scapi->security); free(scapi); if (!ntfs_umount(vol, 0)) ok = TRUE; diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/security.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/security.h index 8875c9c14f..c76ce9f229 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/security.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/security.h @@ -29,23 +29,12 @@ #include "layout.h" #include "inode.h" #include "dir.h" +#include "endians.h" #ifndef POSIXACLS #define POSIXACLS 0 #endif -typedef u16 be16; -typedef u32 be32; - -#if __BYTE_ORDER == __LITTLE_ENDIAN -#define const_cpu_to_be16(x) ((((x) & 255L) << 8) + (((x) >> 8) & 255L)) -#define const_cpu_to_be32(x) ((((x) & 255L) << 24) + (((x) & 0xff00L) << 8) \ - + (((x) >> 8) & 0xff00L) + (((x) >> 24) & 255L)) -#else -#define const_cpu_to_be16(x) (x) -#define const_cpu_to_be32(x) (x) -#endif - /* * item in the mapping list */ @@ -184,7 +173,7 @@ struct POSIX_SECURITY { int defcnt; int firstdef; u16 tagsset; - s32 alignment[0]; + u16 filler; struct POSIX_ACL acl; } ; @@ -222,22 +211,6 @@ enum { 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 - * - * 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) -{ - 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); @@ -283,7 +256,9 @@ int ntfs_set_owner_mode(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); +int ntfs_close_secure(ntfs_volume *vol); + +void ntfs_destroy_security_context(struct SECURITY_CONTEXT *scx); #if POSIXACLS diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/types.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/types.h index a64f12faae..43d91b204f 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/types.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/types.h @@ -48,15 +48,23 @@ typedef u16 le16; typedef u32 le32; typedef u64 le64; +typedef u16 be16; +typedef u32 be32; +typedef u64 be64; + /* - * Declare sle{16,32,64} to be unsigned because we do not want sign extension - * on BE architectures. + * Declare s{l,b}e{16,32,64} to be unsigned because we do not want sign + * extension on BE architectures. */ typedef u16 sle16; typedef u32 sle32; typedef u64 sle64; -typedef u16 ntfschar; /* 2-byte Unicode character type. */ +typedef u16 sbe16; +typedef u32 sbe32; +typedef u64 sbe64; + +typedef le16 ntfschar; /* 2-byte Unicode character type. */ #define UCHAR_T_SIZE_BITS 1 /* diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/unistr.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/unistr.c index 72dd3b0501..d7b97fa6c1 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/unistr.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/unistr.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2004 Anton Altaparmakov * Copyright (c) 2002-2009 Szabolcs Szakacsits - * Copyright (c) 2008-2011 Jean-Pierre Andre + * Copyright (c) 2008-2015 Jean-Pierre Andre * Copyright (c) 2008 Bernhard Kaindl * * This program/include file is free software; you can redistribute it and/or @@ -59,16 +59,11 @@ #include "logging.h" #include "misc.h" -#define NOREVBOM 0 /* JPA rejecting U+FFFE and U+FFFF, open to debate */ - -// no wchar support in the Haiku kernel -#if defined(__HAIKU__) && defined(_KERNEL_MODE) -# include -# define mbstowcs(a, b, c) (panic("mbstowcs"), 0) -# define wctomb(a, b) (panic("wctomb"), 0) -# define mbtowc(a, b, c) (panic("mbtowc"), 0) -# define setlocale(a, b) (panic("setlocale"), 0) -#endif +#ifndef ALLOW_BROKEN_UNICODE +/* Erik allowing broken UTF-16 surrogate pairs and U+FFFE and U+FFFF by default, + * open to debate. */ +#define ALLOW_BROKEN_UNICODE 1 +#endif /* !defined(ALLOW_BROKEN_UNICODE) */ /* * IMPORTANT @@ -148,14 +143,24 @@ BOOL ntfs_names_are_equal(const ntfschar *s1, size_t s1_len, * @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) + * @ic: either CASE_SENSITIVE or IGNORE_CASE (see below) + * @upcase: upcase table + * @upcase_len: upcase table size * + * If @ic is CASE_SENSITIVE, then the names are compared primarily ignoring + * case, but if the names are equal ignoring case, then they are compared + * case-sensitively. As an example, "abc" would collate before "BCD" (since + * "abc" and "BCD" differ ignoring case and 'A' < 'B') but after "ABC" (since + * "ABC" and "abc" are equal ignoring case and 'A' < 'a'). This matches the + * collation order of filenames as indexed in NTFS directories. + * + * If @ic is IGNORE_CASE, then the names are only compared case-insensitively + * and are considered to match if and only if they are equal ignoring case. + * + * Returns: * -1 if the first name collates before the second one, - * 0 if the names match, - * 1 if the second name collates before the first one, or - * + * 0 if the names match, or + * 1 if the second name collates before the first one */ int ntfs_names_full_collate(const ntfschar *name1, const u32 name1_len, const ntfschar *name2, const u32 name2_len, @@ -167,7 +172,7 @@ int ntfs_names_full_collate(const ntfschar *name1, const u32 name1_len, u16 u1, u2; #ifdef DEBUG - if (!name1 || !name2 || (ic && (!upcase || !upcase_len))) { + if (!name1 || !name2 || !upcase || !upcase_len) { ntfs_log_debug("ntfs_names_collate received NULL pointer!\n"); exit(1); } @@ -210,9 +215,9 @@ int ntfs_names_full_collate(const ntfschar *name1, const u32 name1_len, return 1; } else { do { - u1 = c1 = le16_to_cpu(*name1); + u1 = le16_to_cpu(*name1); name1++; - u2 = c2 = le16_to_cpu(*name2); + u2 = le16_to_cpu(*name2); name2++; if (u1 < upcase_len) u1 = le16_to_cpu(upcase[u1]); @@ -253,7 +258,7 @@ int ntfs_names_full_collate(const ntfschar *name1, const u32 name1_len, */ int ntfs_ucsncmp(const ntfschar *s1, const ntfschar *s2, size_t n) { - ntfschar c1, c2; + u16 c1, c2; size_t i; #ifdef DEBUG @@ -369,7 +374,7 @@ ntfschar *ntfs_ucsndup(const ntfschar *s, u32 maxlen) dst = ntfs_malloc((len + 1) * sizeof(ntfschar)); if (dst) { memcpy(dst, s, len * sizeof(ntfschar)); - dst[len] = cpu_to_le16(L'\0'); + dst[len] = const_cpu_to_le16(L'\0'); } return dst; } @@ -441,22 +446,27 @@ 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 the number of bytes in UTF-8 needed (without the terminating null) to + * store the given UTF-16LE string. * - * Return -1 with errno set if string has invalid byte sequence or too long. + * On error, -1 is returned, and errno is set to the error code. The following + * error codes can be expected: + * EILSEQ The input string is not valid UTF-16LE (only possible + * if compiled without ALLOW_BROKEN_UNICODE). + * ENAMETOOLONG The length of the UTF-8 string in bytes (without the + * terminating null) would exceed @outs_len. */ static int utf16_to_utf8_size(const ntfschar *ins, const int ins_len, int outs_len) { @@ -465,14 +475,28 @@ static int utf16_to_utf8_size(const ntfschar *ins, const int ins_len, int outs_l BOOL surrog; surrog = FALSE; - for (i = 0; i < ins_len && ins[i]; i++) { + for (i = 0; i < ins_len && ins[i] && count <= outs_len; i++) { unsigned short c = le16_to_cpu(ins[i]); if (surrog) { if ((c >= 0xdc00) && (c < 0xe000)) { surrog = FALSE; count += 4; - } else + } else { +#if ALLOW_BROKEN_UNICODE + /* The first UTF-16 unit of a surrogate pair has + * a value between 0xd800 and 0xdc00. It can be + * encoded as an individual UTF-8 sequence if we + * cannot combine it with the next UTF-16 unit + * unit as a surrogate pair. */ + surrog = FALSE; + count += 3; + + --i; + continue; +#else goto fail; +#endif /* ALLOW_BROKEN_UNICODE */ + } } else if (c < 0x80) count++; @@ -482,21 +506,30 @@ static int utf16_to_utf8_size(const ntfschar *ins, const int ins_len, int outs_l count += 3; else if (c < 0xdc00) surrog = TRUE; -#if NOREVBOM - else if ((c >= 0xe000) && (c < 0xfffe)) -#else - else if (c >= 0xe000) -#endif +#if ALLOW_BROKEN_UNICODE + else if (c < 0xe000) count += 3; - else + else if (c >= 0xe000) +#else + else if ((c >= 0xe000) && (c < 0xfffe)) +#endif /* ALLOW_BROKEN_UNICODE */ + count += 3; + else goto fail; - if (count > outs_len) { - errno = ENAMETOOLONG; - goto out; - } } - if (surrog) + + if (surrog && count <= outs_len) { +#if ALLOW_BROKEN_UNICODE + count += 3; /* ending with a single surrogate */ +#else goto fail; +#endif /* ALLOW_BROKEN_UNICODE */ + } + + if (count > outs_len) { + errno = ENAMETOOLONG; + goto out; + } ret = count; out: @@ -511,7 +544,7 @@ fail: * @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 + * @outs_len: length of output buffer in bytes (ignored if *@outs is NULL) * * Return -1 with errno set if string has invalid byte sequence or too long. */ @@ -530,10 +563,16 @@ static int ntfs_utf16_to_utf8(const ntfschar *ins, const int ins_len, int halfpair; halfpair = 0; - if (!*outs) + if (!*outs) { + /* If no output buffer was provided, we will allocate one and + * limit its length to PATH_MAX. Note: we follow the standard + * convention of PATH_MAX including the terminating null. */ outs_len = PATH_MAX; + } - size = utf16_to_utf8_size(ins, ins_len, outs_len); + /* The size *with* the terminating null is limited to @outs_len, + * so the size *without* the terminating null is limited to one less. */ + size = utf16_to_utf8_size(ins, ins_len, outs_len - 1); if (size < 0) goto out; @@ -557,8 +596,24 @@ static int ntfs_utf16_to_utf8(const ntfschar *ins, const int ins_len, *t++ = 0x80 + ((c >> 6) & 15) + ((halfpair & 3) << 4); *t++ = 0x80 + (c & 63); halfpair = 0; - } else + } else { +#if ALLOW_BROKEN_UNICODE + /* The first UTF-16 unit of a surrogate pair has + * a value between 0xd800 and 0xdc00. It can be + * encoded as an individual UTF-8 sequence if we + * cannot combine it with the next UTF-16 unit + * unit as a surrogate pair. */ + *t++ = 0xe0 | (halfpair >> 12); + *t++ = 0x80 | ((halfpair >> 6) & 0x3f); + *t++ = 0x80 | (halfpair & 0x3f); + halfpair = 0; + + --i; + continue; +#else goto fail; +#endif /* ALLOW_BROKEN_UNICODE */ + } } else if (c < 0x80) { *t++ = c; } else { @@ -571,16 +626,30 @@ static int ntfs_utf16_to_utf8(const ntfschar *ins, const int ins_len, *t++ = 0x80 | (c & 0x3f); } else if (c < 0xdc00) halfpair = c; +#if ALLOW_BROKEN_UNICODE + else if (c < 0xe000) { + *t++ = 0xe0 | (c >> 12); + *t++ = 0x80 | ((c >> 6) & 0x3f); + *t++ = 0x80 | (c & 0x3f); + } +#endif /* ALLOW_BROKEN_UNICODE */ else if (c >= 0xe000) { *t++ = 0xe0 | (c >> 12); *t++ = 0x80 | ((c >> 6) & 0x3f); *t++ = 0x80 | (c & 0x3f); - } else + } else goto fail; } } +#if ALLOW_BROKEN_UNICODE + if (halfpair) { /* ending with a single surrogate */ + *t++ = 0xe0 | (halfpair >> 12); + *t++ = 0x80 | ((halfpair >> 6) & 0x3f); + *t++ = 0x80 | (halfpair & 0x3f); + } +#endif /* ALLOW_BROKEN_UNICODE */ *t = '\0'; - + #if defined(__APPLE__) || defined(__DARWIN__) #ifdef ENABLE_NFCONV if(nfconvert_utf8 && (t - *outs) > 0) { @@ -609,7 +678,7 @@ static int ntfs_utf16_to_utf8(const ntfschar *ins, const int ins_len, } #endif /* ENABLE_NFCONV */ #endif /* defined(__APPLE__) || defined(__DARWIN__) */ - + ret = t - *outs; out: return ret; @@ -618,8 +687,8 @@ fail: 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. @@ -634,22 +703,22 @@ static int utf8_to_utf16_size(const char *s) size_t count = 0; while ((byte = *((const unsigned char *)s++))) { - if (++count >= PATH_MAX) + if (++count >= PATH_MAX) goto fail; if (byte >= 0xc0) { if (byte >= 0xF5) { errno = EILSEQ; goto out; } - if (!*s) + if (!*s) break; - if (byte >= 0xC0) + if (byte >= 0xC0) s++; - if (!*s) + if (!*s) break; - if (byte >= 0xE0) + if (byte >= 0xE0) s++; - if (!*s) + if (!*s) break; if (byte >= 0xF0) { s++; @@ -665,11 +734,11 @@ fail: 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) @@ -700,15 +769,16 @@ static int utf8_to_unicode(u32 *wc, const char *s) | ((u32)(s[1] & 0x3F) << 6) | ((u32)(s[2] & 0x3F)); /* Check valid ranges */ -#if NOREVBOM +#if ALLOW_BROKEN_UNICODE if (((*wc >= 0x800) && (*wc <= 0xD7FF)) - || ((*wc >= 0xe000) && (*wc <= 0xFFFD))) + || ((*wc >= 0xD800) && (*wc <= 0xDFFF)) + || ((*wc >= 0xe000) && (*wc <= 0xFFFF))) return 3; #else if (((*wc >= 0x800) && (*wc <= 0xD7FF)) - || ((*wc >= 0xe000) && (*wc <= 0xFFFF))) + || ((*wc >= 0xe000) && (*wc <= 0xFFFD))) return 3; -#endif +#endif /* ALLOW_BROKEN_UNICODE */ } goto fail; /* four-byte */ @@ -735,7 +805,7 @@ fail: * @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) @@ -796,7 +866,7 @@ static int ntfs_utf8_to_utf16(const char *ins, ntfschar **outs) } t += m; } - + ret = --outpos - *outs; fail: #if defined(__APPLE__) || defined(__DARWIN__) @@ -813,7 +883,7 @@ fail: * @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 + * @outs_len: length of output buffer in bytes (ignored if *@outs is NULL) * * Convert the input little endian, 2-byte Unicode string @ins, of length * @ins_len into the multibyte string format dictated by the current locale. @@ -869,7 +939,9 @@ int ntfs_ucstombs(const ntfschar *ins, const int ins_len, char **outs, #ifdef HAVE_MBSINIT memset(&mbstate, 0, sizeof(mbstate)); #else +#ifndef __HAIKU__ wctomb(NULL, 0); +#endif #endif for (i = o = 0; i < ins_len; i++) { /* Reallocate memory if necessary or abort. */ @@ -894,6 +966,8 @@ int ntfs_ucstombs(const ntfschar *ins, const int ins_len, char **outs, /* Convert the CPU endian wide character to multibyte. */ #ifdef HAVE_MBSINIT cnt = wcrtomb(mbs + o, wc, &mbstate); +#elif defined(__HAIKU__) + cnt = -1; #else cnt = wctomb(mbs + o, wc); #endif @@ -939,7 +1013,7 @@ err_out: * 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 @@ -970,7 +1044,7 @@ int ntfs_mbstoucs(const char *ins, ntfschar **outs) errno = EINVAL; return -1; } - + if (use_utf8) return ntfs_utf8_to_utf16(ins, outs); @@ -988,7 +1062,7 @@ int ntfs_mbstoucs(const char *ins, ntfschar **outs) ins_len = strlen(ins); } #endif -#elif !defined(DJGPP) +#elif !defined(DJGPP) && !defined(__HAIKU__) ins_len = mbstowcs(NULL, s, 0); #else /* Eeek!!! DJGPP has broken mbstowcs() implementation!!! */ @@ -1013,7 +1087,9 @@ int ntfs_mbstoucs(const char *ins, ntfschar **outs) #ifdef HAVE_MBSINIT memset(&mbstate, 0, sizeof(mbstate)); #else +#ifndef __HAIKU__ mbtowc(NULL, NULL, 0); +#endif #endif for (i = o = cnt = 0; i < ins_size; i += cnt, o++) { /* Reallocate memory if necessary. */ @@ -1029,6 +1105,8 @@ int ntfs_mbstoucs(const char *ins, ntfschar **outs) /* Convert the multibyte character to a wide character. */ #ifdef HAVE_MBSINIT cnt = mbrtowc(&wc, ins + i, ins_size - i, &mbstate); +#elif defined(__HAIKU__) + cnt = -1; #else cnt = mbtowc(&wc, ins + i, ins_size - i); #endif @@ -1059,7 +1137,7 @@ int ntfs_mbstoucs(const char *ins, ntfschar **outs) } #endif /* Now write the NULL character. */ - ucs[o] = cpu_to_le16(L'\0'); + ucs[o] = const_cpu_to_le16(L'\0'); *outs = ucs; return o; err_out: @@ -1137,66 +1215,15 @@ char *ntfs_uppercase_mbs(const char *low, */ void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len) { -#if 1 /* Vista */ - /* - * This is the table as defined by Vista - */ - /* - * "Start" is inclusive and "End" is exclusive, every value has the - * value of "Add" added to it. - */ - static int uc_run_table[][3] = { /* Start, End, Add */ - {0x0061, 0x007b, -32}, {0x00e0, 0x00f7, -32}, {0x00f8, 0x00ff, -32}, - {0x0256, 0x0258, -205}, {0x028a, 0x028c, -217}, {0x037b, 0x037e, 130}, - {0x03ac, 0x03ad, -38}, {0x03ad, 0x03b0, -37}, {0x03b1, 0x03c2, -32}, - {0x03c2, 0x03c3, -31}, {0x03c3, 0x03cc, -32}, {0x03cc, 0x03cd, -64}, - {0x03cd, 0x03cf, -63}, {0x0430, 0x0450, -32}, {0x0450, 0x0460, -80}, - {0x0561, 0x0587, -48}, {0x1f00, 0x1f08, 8}, {0x1f10, 0x1f16, 8}, - {0x1f20, 0x1f28, 8}, {0x1f30, 0x1f38, 8}, {0x1f40, 0x1f46, 8}, - {0x1f51, 0x1f52, 8}, {0x1f53, 0x1f54, 8}, {0x1f55, 0x1f56, 8}, - {0x1f57, 0x1f58, 8}, {0x1f60, 0x1f68, 8}, {0x1f70, 0x1f72, 74}, - {0x1f72, 0x1f76, 86}, {0x1f76, 0x1f78, 100}, {0x1f78, 0x1f7a, 128}, - {0x1f7a, 0x1f7c, 112}, {0x1f7c, 0x1f7e, 126}, {0x1f80, 0x1f88, 8}, - {0x1f90, 0x1f98, 8}, {0x1fa0, 0x1fa8, 8}, {0x1fb0, 0x1fb2, 8}, - {0x1fb3, 0x1fb4, 9}, {0x1fcc, 0x1fcd, -9}, {0x1fd0, 0x1fd2, 8}, - {0x1fe0, 0x1fe2, 8}, {0x1fe5, 0x1fe6, 7}, {0x1ffc, 0x1ffd, -9}, - {0x2170, 0x2180, -16}, {0x24d0, 0x24ea, -26}, {0x2c30, 0x2c5f, -48}, - {0x2d00, 0x2d26, -7264}, {0xff41, 0xff5b, -32}, {0} - }; - /* - * "Start" is exclusive and "End" is inclusive, every second value is - * decremented by one. - */ - static int uc_dup_table[][2] = { /* Start, End */ - {0x0100, 0x012f}, {0x0132, 0x0137}, {0x0139, 0x0149}, {0x014a, 0x0178}, - {0x0179, 0x017e}, {0x01a0, 0x01a6}, {0x01b3, 0x01b7}, {0x01cd, 0x01dd}, - {0x01de, 0x01ef}, {0x01f4, 0x01f5}, {0x01f8, 0x01f9}, {0x01fa, 0x0220}, - {0x0222, 0x0234}, {0x023b, 0x023c}, {0x0241, 0x0242}, {0x0246, 0x024f}, - {0x03d8, 0x03ef}, {0x03f7, 0x03f8}, {0x03fa, 0x03fb}, {0x0460, 0x0481}, - {0x048a, 0x04bf}, {0x04c1, 0x04c4}, {0x04c5, 0x04c8}, {0x04c9, 0x04ce}, - {0x04ec, 0x04ed}, {0x04d0, 0x04eb}, {0x04ee, 0x04f5}, {0x04f6, 0x0513}, - {0x1e00, 0x1e95}, {0x1ea0, 0x1ef9}, {0x2183, 0x2184}, {0x2c60, 0x2c61}, - {0x2c67, 0x2c6c}, {0x2c75, 0x2c76}, {0x2c80, 0x2ce3}, {0} - }; - /* - * Set the Unicode character at offset "Offset" to "Value". Note, - * "Value" is host endian. - */ - static int uc_byte_table[][2] = { /* Offset, Value */ - {0x00ff, 0x0178}, {0x0180, 0x0243}, {0x0183, 0x0182}, {0x0185, 0x0184}, - {0x0188, 0x0187}, {0x018c, 0x018b}, {0x0192, 0x0191}, {0x0195, 0x01f6}, - {0x0199, 0x0198}, {0x019a, 0x023d}, {0x019e, 0x0220}, {0x01a8, 0x01a7}, - {0x01ad, 0x01ac}, {0x01b0, 0x01af}, {0x01b9, 0x01b8}, {0x01bd, 0x01bc}, - {0x01bf, 0x01f7}, {0x01c6, 0x01c4}, {0x01c9, 0x01c7}, {0x01cc, 0x01ca}, - {0x01dd, 0x018e}, {0x01f3, 0x01f1}, {0x023a, 0x2c65}, {0x023e, 0x2c66}, - {0x0253, 0x0181}, {0x0254, 0x0186}, {0x0259, 0x018f}, {0x025b, 0x0190}, - {0x0260, 0x0193}, {0x0263, 0x0194}, {0x0268, 0x0197}, {0x0269, 0x0196}, - {0x026b, 0x2c62}, {0x026f, 0x019c}, {0x0272, 0x019d}, {0x0275, 0x019f}, - {0x027d, 0x2c64}, {0x0280, 0x01a6}, {0x0283, 0x01a9}, {0x0288, 0x01ae}, - {0x0289, 0x0244}, {0x028c, 0x0245}, {0x0292, 0x01b7}, {0x03f2, 0x03f9}, - {0x04cf, 0x04c0}, {0x1d7d, 0x2c63}, {0x214e, 0x2132}, {0} - }; -#else /* Vista */ + struct NEWUPPERCASE { + unsigned short first; + unsigned short last; + short diff; + unsigned char step; + unsigned char osmajor; + unsigned char osminor; + } ; + /* * This is the table as defined by Windows XP */ @@ -1236,9 +1263,89 @@ void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len) {0x01A8, 0x01A7}, {0x01DD, 0x018E}, {0x0268, 0x0197}, {0} }; -#endif /* Vista */ + +/* + * Changes which were applied to later Windows versions + * + * md5 for $UpCase from Winxp : 6fa3db2468275286210751e869d36373 + * Vista : 2f03b5a69d486ff3864cecbd07f24440 + * Win8 : 7ff498a44e45e77374cc7c962b1b92f2 + */ + static const struct NEWUPPERCASE newuppercase[] = { + /* from Windows 6.0 (Vista) */ + { 0x37b, 0x37d, 0x82, 1, 6, 0 }, + { 0x1f80, 0x1f87, 0x8, 1, 6, 0 }, + { 0x1f90, 0x1f97, 0x8, 1, 6, 0 }, + { 0x1fa0, 0x1fa7, 0x8, 1, 6, 0 }, + { 0x2c30, 0x2c5e, -0x30, 1, 6, 0 }, + { 0x2d00, 0x2d25, -0x1c60, 1, 6, 0 }, + { 0x2c68, 0x2c6c, -0x1, 2, 6, 0 }, + { 0x219, 0x21f, -0x1, 2, 6, 0 }, + { 0x223, 0x233, -0x1, 2, 6, 0 }, + { 0x247, 0x24f, -0x1, 2, 6, 0 }, + { 0x3d9, 0x3e1, -0x1, 2, 6, 0 }, + { 0x48b, 0x48f, -0x1, 2, 6, 0 }, + { 0x4fb, 0x513, -0x1, 2, 6, 0 }, + { 0x2c81, 0x2ce3, -0x1, 2, 6, 0 }, + { 0x3f8, 0x3fb, -0x1, 3, 6, 0 }, + { 0x4c6, 0x4ce, -0x1, 4, 6, 0 }, + { 0x23c, 0x242, -0x1, 6, 6, 0 }, + { 0x4ed, 0x4f7, -0x1, 10, 6, 0 }, + { 0x450, 0x45d, -0x50, 13, 6, 0 }, + { 0x2c61, 0x2c76, -0x1, 21, 6, 0 }, + { 0x1fcc, 0x1ffc, -0x9, 48, 6, 0 }, + { 0x180, 0x180, 0xc3, 1, 6, 0 }, + { 0x195, 0x195, 0x61, 1, 6, 0 }, + { 0x19a, 0x19a, 0xa3, 1, 6, 0 }, + { 0x19e, 0x19e, 0x82, 1, 6, 0 }, + { 0x1bf, 0x1bf, 0x38, 1, 6, 0 }, + { 0x1f9, 0x1f9, -0x1, 1, 6, 0 }, + { 0x23a, 0x23a, 0x2a2b, 1, 6, 0 }, + { 0x23e, 0x23e, 0x2a28, 1, 6, 0 }, + { 0x26b, 0x26b, 0x29f7, 1, 6, 0 }, + { 0x27d, 0x27d, 0x29e7, 1, 6, 0 }, + { 0x280, 0x280, -0xda, 1, 6, 0 }, + { 0x289, 0x289, -0x45, 1, 6, 0 }, + { 0x28c, 0x28c, -0x47, 1, 6, 0 }, + { 0x3f2, 0x3f2, 0x7, 1, 6, 0 }, + { 0x4cf, 0x4cf, -0xf, 1, 6, 0 }, + { 0x1d7d, 0x1d7d, 0xee6, 1, 6, 0 }, + { 0x1fb3, 0x1fb3, 0x9, 1, 6, 0 }, + { 0x214e, 0x214e, -0x1c, 1, 6, 0 }, + { 0x2184, 0x2184, -0x1, 1, 6, 0 }, + /* from Windows 6.1 (Win7) */ + { 0x23a, 0x23e, 0x0, 4, 6, 1 }, + { 0x250, 0x250, 0x2a1f, 2, 6, 1 }, + { 0x251, 0x251, 0x2a1c, 2, 6, 1 }, + { 0x271, 0x271, 0x29fd, 2, 6, 1 }, + { 0x371, 0x373, -0x1, 2, 6, 1 }, + { 0x377, 0x377, -0x1, 2, 6, 1 }, + { 0x3c2, 0x3c2, 0x0, 2, 6, 1 }, + { 0x3d7, 0x3d7, -0x8, 2, 6, 1 }, + { 0x515, 0x523, -0x1, 2, 6, 1 }, + /* below, -0x75fc stands for 0x8a04 and truncation */ + { 0x1d79, 0x1d79, -0x75fc, 2, 6, 1 }, + { 0x1efb, 0x1eff, -0x1, 2, 6, 1 }, + { 0x1fc3, 0x1ff3, 0x9, 48, 6, 1 }, + { 0x1fcc, 0x1ffc, 0x0, 48, 6, 1 }, + { 0x2c65, 0x2c65, -0x2a2b, 2, 6, 1 }, + { 0x2c66, 0x2c66, -0x2a28, 2, 6, 1 }, + { 0x2c73, 0x2c73, -0x1, 2, 6, 1 }, + { 0xa641, 0xa65f, -0x1, 2, 6, 1 }, + { 0xa663, 0xa66d, -0x1, 2, 6, 1 }, + { 0xa681, 0xa697, -0x1, 2, 6, 1 }, + { 0xa723, 0xa72f, -0x1, 2, 6, 1 }, + { 0xa733, 0xa76f, -0x1, 2, 6, 1 }, + { 0xa77a, 0xa77c, -0x1, 2, 6, 1 }, + { 0xa77f, 0xa787, -0x1, 2, 6, 1 }, + { 0xa78c, 0xa78c, -0x1, 2, 6, 1 }, + /* end mark */ + { 0 } + } ; + int i, r; int k, off; + const struct NEWUPPERCASE *puc; memset((char*)uc, 0, uc_len); uc_len >>= 1; @@ -1258,6 +1365,16 @@ void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len) k = uc_byte_table[r][1]; uc[uc_byte_table[r][0]] = cpu_to_le16(k); } + for (r=0; newuppercase[r].first; r++) { + puc = &newuppercase[r]; + if ((puc->osmajor < UPCASE_MAJOR) + || ((puc->osmajor == UPCASE_MAJOR) + && (puc->osminor <= UPCASE_MINOR))) { + off = puc->diff; + for (i = puc->first; i <= puc->last; i += puc->step) + uc[i] = cpu_to_le16(i + off); + } + } } /* @@ -1319,12 +1436,12 @@ ntfschar *ntfs_locase_table_build(const ntfschar *uc, u32 uc_cnt) * @len: length of output buffer in Unicode characters * * Convert the input @s string into the corresponding little endian, - * 2-byte Unicode string. The length of the converted string is less + * 2-byte Unicode string. The length of the converted string is less * or equal to the maximum length allowed by the NTFS format (255). * * If @s is NULL then return AT_UNNAMED. * - * On success the function returns the Unicode string in an allocated + * On success the function returns the Unicode string in an allocated * buffer and the caller is responsible to free it when it's not needed * anymore. * @@ -1368,15 +1485,19 @@ void ntfs_ucsfree(ntfschar *ucs) * Check whether a name contains no chars forbidden * for DOS or Win32 use * + * If @strict is TRUE, then trailing dots and spaces are forbidden. + * These names are technically allowed in the Win32 namespace, but + * they can be problematic. See comment for FILE_NAME_WIN32. + * * 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 strict) { BOOL forbidden; int ch; int i; - u32 mainset = (1L << ('\"' - 0x20)) + static const u32 mainset = (1L << ('\"' - 0x20)) | (1L << ('*' - 0x20)) | (1L << ('/' - 0x20)) | (1L << (':' - 0x20)) @@ -1384,9 +1505,9 @@ BOOL ntfs_forbidden_chars(const ntfschar *name, int len) | (1L << ('>' - 0x20)) | (1L << ('?' - 0x20)); - forbidden = (len == 0) - || (le16_to_cpu(name[len-1]) == ' ') - || (le16_to_cpu(name[len-1]) == '.'); + forbidden = (len == 0) || + (strict && (name[len-1] == const_cpu_to_le16(' ') || + name[len-1] == const_cpu_to_le16('.'))); for (i=0; i= 3)) { + /* + * Rough hash check to tell whether the first couple of chars + * may be one of CO PR AU NU LP or lowercase variants. + */ + h = ((le16_to_cpu(name[0]) & 31)*48) + ^ ((le16_to_cpu(name[1]) & 31)*165); + if ((h % 23) == 17) { + /* do a full check, depending on the third char */ + switch (le16_to_cpu(name[2]) & ~0x20) { + case 'N' : + if (((len == 3) || (name[3] == dot)) + && (!ntfs_ucsncasecmp(name, con, 3, + vol->upcase, vol->upcase_len) + || !ntfs_ucsncasecmp(name, prn, 3, + vol->upcase, vol->upcase_len))) + forbidden = TRUE; + break; + case 'X' : + if (((len == 3) || (name[3] == dot)) + && !ntfs_ucsncasecmp(name, aux, 3, + vol->upcase, vol->upcase_len)) + forbidden = TRUE; + break; + case 'L' : + if (((len == 3) || (name[3] == dot)) + && !ntfs_ucsncasecmp(name, nul, 3, + vol->upcase, vol->upcase_len)) + forbidden = TRUE; + break; + case 'M' : + if ((len > 3) + && (le16_to_cpu(name[3]) >= '1') + && (le16_to_cpu(name[3]) <= '9') + && ((len == 4) || (name[4] == dot)) + && !ntfs_ucsncasecmp(name, com, 3, + vol->upcase, vol->upcase_len)) + forbidden = TRUE; + break; + case 'T' : + if ((len > 3) + && (le16_to_cpu(name[3]) >= '1') + && (le16_to_cpu(name[3]) <= '9') + && ((len == 4) || (name[4] == dot)) + && !ntfs_ucsncasecmp(name, lpt, 3, + vol->upcase, vol->upcase_len)) + forbidden = TRUE; + break; + } + } + } + + if (forbidden) + errno = EINVAL; + return (forbidden); +} + /* * Check whether the same name can be used as a DOS and * a Win32 name @@ -1443,9 +1655,12 @@ int ntfs_set_char_encoding(const char *locale) || strstr(locale,"utf-8") || strstr(locale,"UTF-8")) use_utf8 = 1; else +#ifndef __HAIKU__ if (setlocale(LC_ALL, locale)) use_utf8 = 0; - else { + else +#endif + { ntfs_log_error("Invalid locale, encoding to UTF-8\n"); use_utf8 = 1; } @@ -1456,75 +1671,95 @@ int ntfs_set_char_encoding(const char *locale) int ntfs_macosx_normalize_filenames(int normalize) { #ifdef ENABLE_NFCONV - if(normalize == 0 || normalize == 1) { + if (normalize == 0 || normalize == 1) { nfconvert_utf8 = normalize; return 0; } - else + else { return -1; + } #else return -1; #endif /* ENABLE_NFCONV */ -} +} int ntfs_macosx_normalize_utf8(const char *utf8_string, char **target, - int composed) { + int composed) +{ #ifdef ENABLE_NFCONV - /* For this code to compile, the CoreFoundation framework must be fed to the linker. */ + /* 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) { + 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); + + /* 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) { + 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. */ + 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); + 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"); + + 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 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"); } - else - ntfs_log_error("Could not perform check for required length of UTF-8 conversion of normalized CFMutableString.\n"); - CFRelease(cfMutableString); - - if(result != NULL) { + + if (result != NULL) { *target = result; return resultLength - 1; } - else + else { return -1; + } #else return -1; #endif /* ENABLE_NFCONV */ diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/unistr.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/unistr.h index 8cadc3c4fc..28048ed7fa 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/unistr.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/unistr.h @@ -68,7 +68,9 @@ extern ntfschar *ntfs_str2ucs(const char *s, int *len); extern void ntfs_ucsfree(ntfschar *ucs); -extern BOOL ntfs_forbidden_chars(const ntfschar *name, int len); +extern BOOL ntfs_forbidden_chars(const ntfschar *name, int len, BOOL strict); +extern BOOL ntfs_forbidden_names(ntfs_volume *vol, + const ntfschar *name, int len, BOOL strict); extern BOOL ntfs_collapsible_chars(ntfs_volume *vol, const ntfschar *shortname, int shortlen, const ntfschar *longname, int longlen); diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/volume.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/volume.c index 1cb58dbdfa..233ba8e483 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/volume.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/volume.c @@ -74,8 +74,9 @@ #include "cache.h" #include "realpath.h" #include "misc.h" +#include "security.h" -const char *ntfs_home = +const char *ntfs_home = "News, support and information: http://tuxera.com\n"; static const char *invalid_ntfs_msg = @@ -97,6 +98,11 @@ static const char *hibernated_volume_msg = "Windows fully (no hibernation or fast restarting), or mount the volume\n" "read-only with the 'ro' mount option.\n"; +static const char *fallback_readonly_msg = +"Falling back to read-only mount because the NTFS partition is in an\n" +"unsafe state. Please resume and shutdown Windows fully (no hibernation\n" +"or fast restarting.)\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"; @@ -108,7 +114,7 @@ static const char *opened_volume_msg = 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" +"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"; @@ -144,8 +150,8 @@ static int ntfs_inode_free(ntfs_inode **ni) if (ni && *ni) { ret = ntfs_inode_close(*ni); *ni = NULL; - } - + } + return ret; } @@ -167,9 +173,12 @@ static int __ntfs_volume_release(ntfs_volume *v) { int err = 0; + if (ntfs_close_secure(v)) + 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. */ @@ -178,20 +187,20 @@ static int __ntfs_volume_release(ntfs_volume *v) 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; @@ -260,10 +269,10 @@ static int ntfs_mft_load(ntfs_volume *vol) 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; @@ -288,7 +297,7 @@ static int ntfs_mft_load(ntfs_volume *vol) 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"); @@ -304,9 +313,9 @@ static int ntfs_mft_load(ntfs_volume *vol) mft_has_no_attr_list: 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) { @@ -428,18 +437,18 @@ static int ntfs_mftmirr_load(ntfs_volume *vol) 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: @@ -484,12 +493,12 @@ ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, 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 = ntfs_upcase_build_default(&vol->upcase); if (!vol->upcase_len || !vol->upcase) @@ -498,7 +507,7 @@ ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, /* 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); @@ -511,7 +520,7 @@ ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, #endif if (flags & NTFS_MNT_RDONLY) NVolSetReadOnly(vol); - + /* ...->open needs bracketing to compile with glibc 2.7 */ if ((dev->d_ops->open)(dev, NVolReadOnly(vol) ? O_RDONLY: O_RDWR)) { if (!NVolReadOnly(vol) && (errno == EROFS)) { @@ -531,7 +540,7 @@ ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, } /* 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)) { @@ -549,7 +558,7 @@ ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, } 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. */ @@ -558,7 +567,7 @@ ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, "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% */ @@ -652,14 +661,14 @@ static int ntfs_volume_check_logfile(ntfs_volume *vol) 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; /* @@ -682,7 +691,7 @@ static int ntfs_volume_check_logfile(ntfs_volume *vol) } free(rp); ntfs_attr_close(na); -out: +out: if (ntfs_inode_close(ni)) ntfs_error_set(&err); if (err) { @@ -910,10 +919,13 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags) ATTR_RECORD *a; VOLUME_INFORMATION *vinf; ntfschar *vname; + u32 record_size; int i, j, eo; unsigned int k; u32 u; + BOOL need_fallback_ro; + need_fallback_ro = FALSE; vol = ntfs_volume_startup(dev, flags); if (!vol) return NULL; @@ -963,13 +975,13 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags) mrec = (MFT_RECORD*)(m + i * vol->mft_record_size); if (mrec->flags & MFT_RECORD_IN_USE) { - if (ntfs_is_baad_recordp(mrec)) { + if (ntfs_is_baad_record(mrec->magic)) { ntfs_log_error("$MFT error: Incomplete multi " "sector transfer detected in " "'%s'.\n", s); goto io_error_exit; } - if (!ntfs_is_mft_recordp(mrec)) { + if (!ntfs_is_mft_record(mrec->magic)) { ntfs_log_error("$MFT error: Invalid mft " "record for '%s'.\n", s); goto io_error_exit; @@ -977,19 +989,22 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags) } mrec2 = (MFT_RECORD*)(m2 + i * vol->mft_record_size); if (mrec2->flags & MFT_RECORD_IN_USE) { - if (ntfs_is_baad_recordp(mrec2)) { + if (ntfs_is_baad_record(mrec2->magic)) { ntfs_log_error("$MFTMirr error: Incomplete " "multi sector transfer " "detected in '%s'.\n", s); goto io_error_exit; } - if (!ntfs_is_mft_recordp(mrec2)) { + if (!ntfs_is_mft_record(mrec2->magic)) { 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))) { + record_size = ntfs_mft_record_get_data_size(mrec); + if ((record_size <= sizeof(MFT_RECORD)) + || (record_size > vol->mft_record_size) + || memcmp(mrec, mrec2, record_size)) { ntfs_log_error("$MFTMirr does not match $MFT (record " "%d).\n", i); goto io_error_exit; @@ -1007,16 +1022,16 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags) 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->data_size, (long long)vol->lcnbmp_na->allocated_size); goto io_error_exit; } @@ -1172,7 +1187,7 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags) 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) @@ -1223,26 +1238,46 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags) ntfs_log_perror("Failed to close $AttrDef"); goto error_exit; } + + /* Open $Secure. */ + if (ntfs_open_secure(vol)) + goto error_exit; + /* * Check for dirty logfile and hibernated Windows. * We care only about read-write mounts. */ if (!(flags & (NTFS_MNT_RDONLY | NTFS_MNT_FORENSIC))) { if (!(flags & NTFS_MNT_IGNORE_HIBERFILE) && - ntfs_volume_check_hiberfile(vol, 1) < 0) - goto error_exit; + ntfs_volume_check_hiberfile(vol, 1) < 0) { + if (flags & NTFS_MNT_MAY_RDONLY) + need_fallback_ro = TRUE; + else + goto error_exit; + } if (ntfs_volume_check_logfile(vol) < 0) { /* Always reject cached metadata for now */ - if (!(flags & NTFS_MNT_RECOVER) || (errno == EPERM)) - 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; + if (!(flags & NTFS_MNT_RECOVER) || (errno == EPERM)) { + if (flags & NTFS_MNT_MAY_RDONLY) + need_fallback_ro = TRUE; + else + goto error_exit; + } else { + 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 (fix_txf_data(vol)) - goto error_exit; + if (!(flags & NTFS_MNT_RDONLY) && !need_fallback_ro) { + if (fix_txf_data(vol)) + goto error_exit; + } + } + if (need_fallback_ro) { + NVolSetReadOnly(vol); + ntfs_log_error("%s", fallback_readonly_msg); } return vol; @@ -1640,7 +1675,7 @@ int ntfs_logfile_reset(ntfs_volume *vol) ntfs_attr_close(na); goto error_exit; } - + ntfs_attr_close(na); return ntfs_inode_close(ni); diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/volume.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/volume.h index a181ccc6be..cee91e214d 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/volume.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/volume.h @@ -36,9 +36,7 @@ #ifdef HAVE_SYS_PARAM_H #include #endif -#ifdef HAVE_SYS_MOUNT_H -#include -#endif + /* Do not #include here : conflicts with */ #ifdef HAVE_MNTENT_H #include #endif @@ -62,6 +60,7 @@ typedef struct _ntfs_volume ntfs_volume; enum { NTFS_MNT_NONE = 0x00000000, NTFS_MNT_RDONLY = 0x00000001, + NTFS_MNT_MAY_RDONLY = 0x02000000, /* Allow fallback to ro */ NTFS_MNT_FORENSIC = 0x04000000, /* No modification during * mount. */ NTFS_MNT_EXCLUSIVE = 0x08000000, diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/win32_io.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/win32_io.c index 437a3fc798..e5cfb64008 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/win32_io.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/win32_io.c @@ -6,7 +6,7 @@ * Copyright (c) 2003-2004 Lode Leroy * Copyright (c) 2003-2006 Anton Altaparmakov * Copyright (c) 2004-2005 Yuval Fledel - * Copyright (c) 2012-2013 Jean-Pierre Andre + * Copyright (c) 2012-2014 Jean-Pierre Andre * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -150,6 +150,7 @@ enum { /* see http://msdn.microsoft.com/en-us/library/cc704588(v=prot.10).aspx * STATUS_FILE_NOT_FOUND = 0xC0000028, STATUS_OBJECT_NAME_INVALID = 0xC0000033, STATUS_OBJECT_NAME_NOT_FOUND = 0xC0000034, + STATUS_SHARING_VIOLATION = 0xC0000043, STATUS_INVALID_PARAMETER_1 = 0xC00000EF, STATUS_IO_DEVICE_ERROR = 0xC0000185, STATUS_GUARD_PAGE_VIOLATION = 0x80000001 @@ -354,6 +355,8 @@ static int ntfs_ntstatus_to_errno(NTSTATUS status) case STATUS_IO_DEVICE_ERROR : case STATUS_END_OF_FILE : return (EIO); + case STATUS_SHARING_VIOLATION : + return (EBUSY); default: /* generic message */ return ENOMSG; @@ -1547,6 +1550,7 @@ static int ntfs_device_win32_close(struct ntfs_device *dev) rvl = NtClose(fd->handle) == STATUS_SUCCESS; } else rvl = CloseHandle(fd->handle); + NDevClearOpen(dev); free(fd); if (!rvl) { errno = ntfs_w32error_to_errno(GetLastError()); @@ -1633,7 +1637,8 @@ static s64 ntfs_device_win32_write(struct ntfs_device *dev, const void *b, { s64 old_pos, to_write, i, bw = 0; win32_fd *fd = (win32_fd *)dev->d_private; - BYTE *alignedbuffer; + const BYTE *alignedbuffer; + BYTE *readbuffer; int old_ofs, ofs; old_pos = fd->pos; @@ -1658,15 +1663,16 @@ static s64 ntfs_device_win32_write(struct ntfs_device *dev, const void *b, if (!count) return 0; NDevSetDirty(dev); + readbuffer = (BYTE*)NULL; if (!((unsigned long)b & (fd->geo_sector_size - 1)) && !old_ofs && !(count & (fd->geo_sector_size - 1))) - alignedbuffer = (BYTE *)b; + alignedbuffer = (const BYTE *)b; else { s64 end; - alignedbuffer = (BYTE *)VirtualAlloc(NULL, to_write, + readbuffer = (BYTE *)VirtualAlloc(NULL, to_write, MEM_COMMIT, PAGE_READWRITE); - if (!alignedbuffer) { + if (!readbuffer) { errno = ntfs_w32error_to_errno(GetLastError()); ntfs_log_trace("VirtualAlloc failed for write.\n"); return -1; @@ -1675,7 +1681,7 @@ static s64 ntfs_device_win32_write(struct ntfs_device *dev, const void *b, if (ofs) { i = ntfs_device_win32_pread_simple(fd, old_pos & ~(s64)(fd->geo_sector_size - 1), - fd->geo_sector_size, alignedbuffer); + fd->geo_sector_size, readbuffer); if (i != fd->geo_sector_size) { if (i >= 0) errno = EIO; @@ -1693,7 +1699,7 @@ static s64 ntfs_device_win32_write(struct ntfs_device *dev, const void *b, ((to_write > fd->geo_sector_size) || !ofs)) { i = ntfs_device_win32_pread_simple(fd, end & ~(s64)(fd->geo_sector_size - 1), - fd->geo_sector_size, alignedbuffer + + fd->geo_sector_size, readbuffer + to_write - fd->geo_sector_size); if (i != fd->geo_sector_size) { if (i >= 0) @@ -1701,8 +1707,9 @@ static s64 ntfs_device_win32_write(struct ntfs_device *dev, const void *b, goto write_error; } } - /* Copy the data to be written into @alignedbuffer. */ - memcpy(alignedbuffer + ofs, b, count); + /* Copy the data to be written into @readbuffer. */ + memcpy(readbuffer + ofs, b, count); + alignedbuffer = readbuffer; } if (fd->vol_handle != INVALID_HANDLE_VALUE && old_pos < fd->geo_size) { s64 vol_to_write = fd->geo_size - old_pos; @@ -1740,8 +1747,8 @@ static s64 ntfs_device_win32_write(struct ntfs_device *dev, const void *b, bw = count; fd->pos = old_pos + bw; write_partial: - if (alignedbuffer != b) - VirtualFree(alignedbuffer, 0, MEM_RELEASE); + if (readbuffer) + VirtualFree(readbuffer, 0, MEM_RELEASE); return bw; write_error: bw = -1; @@ -1877,7 +1884,11 @@ static int ntfs_device_win32_ioctl(struct ntfs_device *dev, int request, #ifdef BLKSSZGET case BLKSSZGET: ntfs_log_debug("BLKSSZGET detected.\n"); - return ntfs_win32_blksszget(dev, (int *)argp); + if (fd && !fd->ntdll) { + *(int*)argp = fd->geo_sector_size; + return (0); + } else + return ntfs_win32_blksszget(dev, (int *)argp); #endif #ifdef BLKBSZSET case BLKBSZSET: diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/xattrs.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/xattrs.c index 5be2c06a68..2b7e70919c 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/xattrs.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/xattrs.c @@ -1,7 +1,7 @@ /** * xattrs.c : common functions to deal with system extended attributes * - * Copyright (c) 2010 Jean-Pierre Andre + * Copyright (c) 2010-2014 Jean-Pierre Andre * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -23,8 +23,6 @@ #include "config.h" #endif -#ifdef HAVE_SETXATTR /* extended attributes support required */ - #ifdef HAVE_STDIO_H #include #endif @@ -55,6 +53,7 @@ #include "efs.h" #include "reparse.h" #include "object_id.h" +#include "ea.h" #include "misc.h" #include "logging.h" #include "xattrs.h" @@ -82,10 +81,6 @@ struct LE_POSIX_ACL { #endif #endif -static const char xattr_ntfs_3g[] = "ntfs-3g."; -static const char nf_ns_user_prefix[] = "user."; -static const int nf_ns_user_prefix_len = sizeof(nf_ns_user_prefix) - 1; - static const char nf_ns_xattr_ntfs_acl[] = "system.ntfs_acl"; static const char nf_ns_xattr_attrib[] = "system.ntfs_attrib"; static const char nf_ns_xattr_attrib_be[] = "system.ntfs_attrib_be"; @@ -97,6 +92,7 @@ static const char nf_ns_xattr_times[] = "system.ntfs_times"; static const char nf_ns_xattr_times_be[] = "system.ntfs_times_be"; static const char nf_ns_xattr_crtime[] = "system.ntfs_crtime"; static const char nf_ns_xattr_crtime_be[] = "system.ntfs_crtime_be"; +static const char nf_ns_xattr_ea[] = "system.ntfs_ea"; static const char nf_ns_xattr_posix_access[] = "system.posix_acl_access"; static const char nf_ns_xattr_posix_default[] = "system.posix_acl_default"; @@ -119,6 +115,7 @@ static struct XATTRNAME nf_ns_xattr_names[] = { { XATTR_NTFS_TIMES_BE, nf_ns_xattr_times_be }, { XATTR_NTFS_CRTIME, nf_ns_xattr_crtime }, { XATTR_NTFS_CRTIME_BE, nf_ns_xattr_crtime_be }, + { XATTR_NTFS_EA, nf_ns_xattr_ea }, { XATTR_POSIX_ACC, nf_ns_xattr_posix_access }, { XATTR_POSIX_DEF, nf_ns_xattr_posix_default }, { XATTR_UNMAPPED, (char*)NULL } /* terminator */ @@ -473,7 +470,6 @@ void ntfs_xattr_free_mapping(struct XATTRMAPPING *mapping) #endif /* XATTR_MAPPINGS */ - int ntfs_xattr_system_getxattr(struct SECURITY_CONTEXT *scx, enum SYSTEMXATTRS attr, ntfs_inode *ni, ntfs_inode *dir_ni, @@ -589,6 +585,9 @@ int ntfs_xattr_system_getxattr(struct SECURITY_CONTEXT *scx, if ((res >= (int)sizeof(u64)) && value) fix_big_endian(value,sizeof(u64)); break; + case XATTR_NTFS_EA : + res = ntfs_get_ntfs_ea(ni, value, size); + break; default : errno = EOPNOTSUPP; res = -errno; @@ -712,6 +711,9 @@ int ntfs_xattr_system_setxattr(struct SECURITY_CONTEXT *scx, } else res = ntfs_inode_set_times(ni, value, size, flags); break; + case XATTR_NTFS_EA : + res = ntfs_set_ntfs_ea(ni, value, size, flags); + break; default : errno = EOPNOTSUPP; res = -errno; @@ -780,6 +782,9 @@ int ntfs_xattr_system_removexattr(struct SECURITY_CONTEXT *scx, } else res = -errno; break; + case XATTR_NTFS_EA : + res = ntfs_remove_ntfs_ea(ni); + break; default : errno = EOPNOTSUPP; res = -errno; @@ -787,5 +792,3 @@ int ntfs_xattr_system_removexattr(struct SECURITY_CONTEXT *scx, } return (res); } - -#endif /* HAVE_SETXATTR */ diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/xattrs.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/xattrs.h index 515831142e..c5faeac537 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/xattrs.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/xattrs.h @@ -19,8 +19,27 @@ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef _NTFS_XATTR_H_ -#define _NTFS_XATTR_H_ +#ifndef _NTFS_XATTRS_H_ +#define _NTFS_XATTRS_H_ + +/* + * Flags that modify setxattr() semantics. These flags are also used by a + * number of libntfs-3g functions, such as ntfs_set_ntfs_acl(), which were + * originally tied to extended attributes support but now can be used by + * applications even if the platform does not support extended attributes. + * + * Careful: applications including this header should define HAVE_SETXATTR or + * HAVE_SYS_XATTR_H if the platform supports extended attributes. Otherwise the + * defined flags values may be incorrect (they will be correct for Linux but not + * necessarily for other platforms). + */ +#if defined(HAVE_SETXATTR) || defined(HAVE_SYS_XATTR_H) +#include +#else +#include "compat.h" /* may be needed for ENODATA definition */ +#define XATTR_CREATE 1 +#define XATTR_REPLACE 2 +#endif /* * Identification of data mapped to the system name space @@ -39,6 +58,7 @@ enum SYSTEMXATTRS { XATTR_NTFS_TIMES_BE, XATTR_NTFS_CRTIME, XATTR_NTFS_CRTIME_BE, + XATTR_NTFS_EA, XATTR_POSIX_ACC, XATTR_POSIX_DEF } ; @@ -60,6 +80,8 @@ void ntfs_xattr_free_mapping(struct XATTRMAPPING*); enum SYSTEMXATTRS ntfs_xattr_system_type(const char *name, ntfs_volume *vol); +struct SECURITY_CONTEXT; + int ntfs_xattr_system_getxattr(struct SECURITY_CONTEXT *scx, enum SYSTEMXATTRS attr, ntfs_inode *ni, ntfs_inode *dir_ni, @@ -72,4 +94,4 @@ int ntfs_xattr_system_removexattr(struct SECURITY_CONTEXT *scx, enum SYSTEMXATTRS attr, ntfs_inode *ni, ntfs_inode *dir_ni); -#endif /* _NTFS_XATTR_H_ */ +#endif /* _NTFS_XATTRS_H_ */