From 0490778ed1bf2e39eb9d9ea8d62a2b0ab378fecd Mon Sep 17 00:00:00 2001 From: Augustin Cavalier Date: Wed, 17 Oct 2018 17:46:55 -0400 Subject: [PATCH] NTFS: Update libntfs from 2014.2.15 to 2017.3.23. I rewrote the "config.h" at the same time, which enables usage of gettimeofday and some other API functions we didn't have whenever this port was first done, enabling more accurate timestamp handling, among other things. --- src/add-ons/kernel/file_systems/ntfs/Jamfile | 5 - .../kernel/file_systems/ntfs/libntfs/Jamfile | 1 + .../kernel/file_systems/ntfs/libntfs/acls.c | 323 ++++++---- .../kernel/file_systems/ntfs/libntfs/acls.h | 32 +- .../kernel/file_systems/ntfs/libntfs/attrib.c | 319 ++++++---- .../kernel/file_systems/ntfs/libntfs/attrib.h | 7 + .../file_systems/ntfs/libntfs/attrlist.c | 8 +- .../file_systems/ntfs/libntfs/bootsect.c | 6 +- .../kernel/file_systems/ntfs/libntfs/compat.h | 6 +- .../file_systems/ntfs/libntfs/compress.c | 476 ++++++++++----- .../kernel/file_systems/ntfs/libntfs/config.h | 362 +++++++---- .../kernel/file_systems/ntfs/libntfs/device.c | 15 +- .../file_systems/ntfs/libntfs/device_io.h | 2 +- .../kernel/file_systems/ntfs/libntfs/dir.c | 176 +++--- .../kernel/file_systems/ntfs/libntfs/dir.h | 10 +- .../kernel/file_systems/ntfs/libntfs/ea.c | 395 ++++++++++++ .../kernel/file_systems/ntfs/libntfs/ea.h | 33 + .../kernel/file_systems/ntfs/libntfs/efs.c | 17 +- .../file_systems/ntfs/libntfs/endians.h | 116 +++- .../kernel/file_systems/ntfs/libntfs/index.c | 37 +- .../kernel/file_systems/ntfs/libntfs/index.h | 1 + .../kernel/file_systems/ntfs/libntfs/inode.c | 38 +- .../kernel/file_systems/ntfs/libntfs/inode.h | 2 +- .../kernel/file_systems/ntfs/libntfs/ioctl.c | 388 ++++++++++++ .../kernel/file_systems/ntfs/libntfs/ioctl.h | 30 + .../kernel/file_systems/ntfs/libntfs/layout.h | 457 +++++++------- .../file_systems/ntfs/libntfs/lcnalloc.c | 2 +- .../file_systems/ntfs/libntfs/logfile.c | 12 +- .../file_systems/ntfs/libntfs/logfile.h | 160 +++-- .../file_systems/ntfs/libntfs/logging.c | 54 +- .../file_systems/ntfs/libntfs/logging.h | 7 +- .../kernel/file_systems/ntfs/libntfs/mft.c | 101 ++- .../kernel/file_systems/ntfs/libntfs/mft.h | 2 + .../kernel/file_systems/ntfs/libntfs/mst.c | 64 +- .../file_systems/ntfs/libntfs/ntfstime.h | 2 +- .../file_systems/ntfs/libntfs/object_id.c | 16 +- .../kernel/file_systems/ntfs/libntfs/param.h | 44 +- .../kernel/file_systems/ntfs/libntfs/plugin.h | 141 +++++ .../file_systems/ntfs/libntfs/reparse.c | 79 ++- .../file_systems/ntfs/libntfs/reparse.h | 6 +- .../file_systems/ntfs/libntfs/runlist.c | 65 +- .../file_systems/ntfs/libntfs/security.c | 393 ++++++++---- .../file_systems/ntfs/libntfs/security.h | 35 +- .../kernel/file_systems/ntfs/libntfs/types.h | 14 +- .../kernel/file_systems/ntfs/libntfs/unistr.c | 575 ++++++++++++------ .../kernel/file_systems/ntfs/libntfs/unistr.h | 4 +- .../kernel/file_systems/ntfs/libntfs/volume.c | 127 ++-- .../kernel/file_systems/ntfs/libntfs/volume.h | 5 +- .../file_systems/ntfs/libntfs/win32_io.c | 35 +- .../kernel/file_systems/ntfs/libntfs/xattrs.c | 23 +- .../kernel/file_systems/ntfs/libntfs/xattrs.h | 28 +- 51 files changed, 3746 insertions(+), 1510 deletions(-) create mode 100644 src/add-ons/kernel/file_systems/ntfs/libntfs/ea.c create mode 100644 src/add-ons/kernel/file_systems/ntfs/libntfs/ea.h create mode 100644 src/add-ons/kernel/file_systems/ntfs/libntfs/ioctl.c create mode 100644 src/add-ons/kernel/file_systems/ntfs/libntfs/ioctl.h create mode 100644 src/add-ons/kernel/file_systems/ntfs/libntfs/plugin.h 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_ */