diff --git a/Makefile b/Makefile index 8745416..1af50f9 100644 --- a/Makefile +++ b/Makefile @@ -22,8 +22,13 @@ wii-release: clean: $(MAKE) -C source clean -install: cube-release wii-release - $(MAKE) -C source install +cube-install: cube-release + $(MAKE) -C source cube-install PLATFORM=cube + +wii-install: wii-release + $(MAKE) -C source wii-install PLATFORM=wii + +install: wii-install run: install $(MAKE) -C example diff --git a/source/Makefile b/source/Makefile index 9cb768d..6a6bbec 100644 --- a/source/Makefile +++ b/source/Makefile @@ -97,14 +97,13 @@ clean: all: $(NTFSBIN) -install: +cube-install: cp ../include/ntfs.h $(DEVKITPRO)/libogc/include - cp ../lib/wii/libntfs.a $(DEVKITPRO)/libogc/lib/wii cp ../lib/cube/libntfs.a $(DEVKITPRO)/libogc/lib/cube wii-install: - cp ../include/ntfs.h $(DEVKITPRO)/libogc/include - cp ../lib/wii/libntfs.a $(DEVKITPRO)/libogc/lib/wii + cp ../include/ntfs.h $(PORTLIBS)/include + cp ../lib/wii/libntfs.a $(PORTLIBS)/lib #--------------------------------------------------------------------------------- else diff --git a/source/acls.c b/source/acls.c index fe466c2..7d16210 100644 --- a/source/acls.c +++ b/source/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-2010 Jean-Pierre Andre + * Copyright (c) 2007-2012 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 @@ -136,6 +136,19 @@ static const char worldsidbytes[] = { 0, 0, 0, 0 /* 1st level */ } ; +/* + * SID for authenticated user (S-1-5-11) + */ + +static const char authsidbytes[] = { + 1, /* revision */ + 1, /* auth count */ + 0, 0, 0, 0, 0, 5, /* base */ + 11, 0, 0, 0 /* 1st level */ +}; + +static const SID *authsid = (const SID*)authsidbytes; + const SID *worldsid = (const SID*)worldsidbytes; /* @@ -235,6 +248,12 @@ static int is_world_sid(const SID * usid) && (usid->identifier_authority.low_part == const_cpu_to_be32(5)) && (usid->sub_authority[0] == const_cpu_to_le32(32)) && (usid->sub_authority[1] == const_cpu_to_le32(545))) + + /* check whether S-1-5-11 : authenticated 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(11))) ); } @@ -623,7 +642,7 @@ BOOL ntfs_valid_descr(const char *securattr, unsigned int attrsz) && (offdacl+sizeof(ACL) < attrsz))) && (!offsacl || ((offsacl >= sizeof(SECURITY_DESCRIPTOR_RELATIVE)) - && (offsacl+sizeof(ACL) < attrsz))) + && (offsacl+sizeof(ACL) <= attrsz))) && !(phead->owner & const_cpu_to_le32(3)) && !(phead->group & const_cpu_to_le32(3)) && !(phead->dacl & const_cpu_to_le32(3)) @@ -664,7 +683,8 @@ BOOL ntfs_valid_descr(const char *securattr, unsigned int attrsz) */ int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl, - const SID *usid, const SID *gsid, BOOL fordir) + const SID *usid, const SID *gsid, BOOL fordir, + le16 inherited) { unsigned int src; unsigned int dst; @@ -677,7 +697,9 @@ int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl, int gsidsz; const ACCESS_ALLOWED_ACE *poldace; ACCESS_ALLOWED_ACE *pnewace; + ACCESS_ALLOWED_ACE *pauthace; + pauthace = (ACCESS_ALLOWED_ACE*)NULL; usidsz = ntfs_sid_size(usid); gsidsz = ntfs_sid_size(gsid); @@ -694,25 +716,19 @@ int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl, for (nace = 0; nace < oldcnt; nace++) { poldace = (const ACCESS_ALLOWED_ACE*)((const char*)oldacl + src); acesz = le16_to_cpu(poldace->size); - /* inheritance for access */ - if (poldace->flags & selection) { + src += acesz; + /* + * Inheritance for access, unless this is inheriting + * an inherited ACL to a directory. + */ + if ((poldace->flags & selection) + && !(fordir && inherited) + && !ntfs_same_sid(&poldace->sid, ownersid) + && !ntfs_same_sid(&poldace->sid, groupsid)) { pnewace = (ACCESS_ALLOWED_ACE*) ((char*)newacl + dst); memcpy(pnewace,poldace,acesz); - /* - * Replace generic creator-owner and - * creator-group by owner and group - */ - if (ntfs_same_sid(&pnewace->sid, ownersid)) { - memcpy(&pnewace->sid, usid, usidsz); - acesz = usidsz + 8; - pnewace->size = cpu_to_le16(acesz); - } - if (ntfs_same_sid(&pnewace->sid, groupsid)) { - memcpy(&pnewace->sid, gsid, gsidsz); - acesz = gsidsz + 8; - pnewace->size = cpu_to_le16(acesz); - } + /* reencode GENERIC_ALL */ if (pnewace->mask & GENERIC_ALL) { pnewace->mask &= ~GENERIC_ALL; if (fordir) @@ -730,17 +746,71 @@ int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl, | FILE_WRITE | FILE_EXEC | cpu_to_le32(0x40); + } + /* reencode GENERIC_READ (+ EXECUTE) */ + if (pnewace->mask & GENERIC_READ) { + if (fordir) + pnewace->mask |= OWNER_RIGHTS + | DIR_READ + | DIR_EXEC; + else + pnewace->mask |= OWNER_RIGHTS + | FILE_READ + | FILE_EXEC; + pnewace->mask &= ~(GENERIC_READ + | GENERIC_EXECUTE + | WRITE_DAC + | WRITE_OWNER + | DELETE | FILE_WRITE_EA + | FILE_WRITE_ATTRIBUTES); + } + /* reencode GENERIC_WRITE */ + if (pnewace->mask & GENERIC_WRITE) { + if (fordir) + pnewace->mask |= OWNER_RIGHTS + | DIR_WRITE; + else + pnewace->mask |= OWNER_RIGHTS + | FILE_WRITE; + pnewace->mask &= ~(GENERIC_WRITE + | WRITE_DAC + | WRITE_OWNER + | FILE_DELETE_CHILD); } /* remove inheritance flags */ pnewace->flags &= ~(OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE); - dst += acesz; - newcnt++; + /* + * Group similar ACE for authenticated users + * (should probably be done for other SIDs) + */ + if (!fordir + && (poldace->type == ACCESS_ALLOWED_ACE_TYPE) + && ntfs_same_sid(&poldace->sid, authsid)) { + if (pauthace) { + pauthace->flags |= pnewace->flags; + pauthace->mask |= pnewace->mask; + } else { + pauthace = pnewace; + if (inherited) + pnewace->flags |= INHERITED_ACE; + dst += acesz; + newcnt++; + } + } else { + if (inherited) + pnewace->flags |= INHERITED_ACE; + dst += acesz; + newcnt++; + } } - /* inheritance for further inheritance */ - if (fordir - && (poldace->flags + /* + * Inheritance for access, specific to + * creator-owner (and creator-group) + */ + if (fordir || !inherited + || (poldace->flags & (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE))) { pnewace = (ACCESS_ALLOWED_ACE*) ((char*)newacl + dst); @@ -748,19 +818,46 @@ int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl, /* * Replace generic creator-owner and * creator-group by owner and group + * (but keep for further inheritance) */ if (ntfs_same_sid(&pnewace->sid, ownersid)) { memcpy(&pnewace->sid, usid, usidsz); - acesz = usidsz + 8; + pnewace->size = cpu_to_le16(usidsz + 8); + /* remove inheritance flags */ + pnewace->flags &= ~(OBJECT_INHERIT_ACE + | CONTAINER_INHERIT_ACE + | INHERIT_ONLY_ACE); + if (inherited) + pnewace->flags |= INHERITED_ACE; + dst += usidsz + 8; + newcnt++; } if (ntfs_same_sid(&pnewace->sid, groupsid)) { memcpy(&pnewace->sid, gsid, gsidsz); - acesz = gsidsz + 8; + pnewace->size = cpu_to_le16(gsidsz + 8); + /* remove inheritance flags */ + pnewace->flags &= ~(OBJECT_INHERIT_ACE + | CONTAINER_INHERIT_ACE + | INHERIT_ONLY_ACE); + if (inherited) + pnewace->flags |= INHERITED_ACE; + dst += gsidsz + 8; + newcnt++; } + } + + /* inheritance for further inheritance */ + if (fordir + && (poldace->flags + & (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE))) { + pnewace = (ACCESS_ALLOWED_ACE*) + ((char*)newacl + dst); + memcpy(pnewace,poldace,acesz); + if (inherited) + pnewace->flags |= INHERITED_ACE; dst += acesz; newcnt++; } - src += acesz; } /* * Adjust header if something was inherited @@ -3059,6 +3156,7 @@ static int build_owngrp_permissions(const char *securattr, int nace; le32 special; BOOL grppresent; + BOOL ownpresent; le32 allowown, allowgrp, allowall; le32 denyown, denygrp, denyall; @@ -3068,21 +3166,26 @@ static int build_owngrp_permissions(const char *securattr, special = const_cpu_to_le32(0); allowown = allowgrp = allowall = const_cpu_to_le32(0); denyown = denygrp = denyall = const_cpu_to_le32(0); + ownpresent = FALSE; grppresent = FALSE; if (offdacl) { acecnt = le16_to_cpu(pacl->ace_count); offace = offdacl + sizeof(ACL); - } else + } else { acecnt = 0; + offace = 0; + } for (nace = 0; nace < acecnt; nace++) { pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace]; if (!(pace->flags & INHERIT_ONLY_ACE)) { if ((ntfs_same_sid(usid, &pace->sid) || ntfs_same_sid(ownersid, &pace->sid)) && (pace->mask & WRITE_OWNER)) { - if (pace->type == ACCESS_ALLOWED_ACE_TYPE) + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) { allowown |= pace->mask; - } else + ownpresent = TRUE; + } + } else if (ntfs_same_sid(usid, &pace->sid) && (!(pace->mask & WRITE_OWNER))) { if (pace->type == ACCESS_ALLOWED_ACE_TYPE) { @@ -3103,6 +3206,8 @@ static int build_owngrp_permissions(const char *securattr, } offace += le16_to_cpu(pace->size); } + if (!ownpresent) + allowown = allowall; if (!grppresent) allowgrp = allowall; return (merge_permissions(isdir, @@ -3705,7 +3810,7 @@ struct POSIX_SECURITY *ntfs_build_permissions_posix( pxace->perms |= POSIX_PERM_DENIAL; else if (pxace->tag == POSIX_ACL_OTHER) - pctx->permswrld = pxace->perms; + pctx->permswrld |= pxace->perms; pctx->tagsset |= pxace->tag; if (pace->flags & INHERIT_ONLY_ACE) { l--; diff --git a/source/acls.h b/source/acls.h index 8a83d32..4b08388 100644 --- a/source/acls.h +++ b/source/acls.h @@ -185,7 +185,8 @@ char *ntfs_build_descr_posix(struct MAPPING* const mapping[], #endif /* POSIXACLS */ int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl, - const SID *usid, const SID *gsid, BOOL fordir); + const SID *usid, const SID *gsid, + BOOL fordir, le16 inherited); int ntfs_build_permissions(const char *securattr, const SID *usid, const SID *gsid, BOOL isdir); char *ntfs_build_descr(mode_t mode, diff --git a/source/attrib.c b/source/attrib.c index 281a620..c116d9a 100644 --- a/source/attrib.c +++ b/source/attrib.c @@ -1849,6 +1849,13 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) ntfs_log_perror("Failed to enlarge attribute"); goto errno_set; } + /* + * If we avoided updating the runlist, we must be sure + * to cancel the enlargement and put back the runlist to + * a clean state if we get into some error. + */ + if (NAttrDataAppending(na)) + need_to.undo_data_size = 1; #else if (ntfs_attr_truncate_i(na, pos + count, HOLES_OK)) { ntfs_log_perror("Failed to enlarge attribute"); @@ -3732,8 +3739,8 @@ int ntfs_make_room_for_attr(MFT_RECORD *m, u8 *pos, u32 size) * EIO - I/O error occurred or damaged filesystem. */ int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, - ntfschar *name, u8 name_len, u8 *val, u32 size, - ATTR_FLAGS data_flags) + const ntfschar *name, u8 name_len, const u8 *val, + u32 size, ATTR_FLAGS data_flags) { ntfs_attr_search_ctx *ctx; u32 length; @@ -3864,7 +3871,7 @@ put_err_out: * EIO - I/O error occurred or damaged filesystem. */ int ntfs_non_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, - ntfschar *name, u8 name_len, VCN lowest_vcn, int dataruns_size, + const ntfschar *name, u8 name_len, VCN lowest_vcn, int dataruns_size, ATTR_FLAGS flags) { ntfs_attr_search_ctx *ctx; @@ -4147,7 +4154,7 @@ int ntfs_attr_record_rm(ntfs_attr_search_ctx *ctx) * On success return 0. On error return -1 with errno set to the error code. */ int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type, - ntfschar *name, u8 name_len, u8 *val, s64 size) + ntfschar *name, u8 name_len, const u8 *val, s64 size) { u32 attr_rec_size; int err, i, offset; @@ -4342,8 +4349,8 @@ err_out: * Change an attribute flag */ -int ntfs_attr_set_flags(ntfs_inode *ni, ATTR_TYPES type, - ntfschar *name, u8 name_len, ATTR_FLAGS flags, ATTR_FLAGS mask) +int ntfs_attr_set_flags(ntfs_inode *ni, ATTR_TYPES type, const ntfschar *name, + u8 name_len, ATTR_FLAGS flags, ATTR_FLAGS mask) { ntfs_attr_search_ctx *ctx; int res; @@ -6232,10 +6239,18 @@ static int ntfs_non_resident_attr_expand_i(ntfs_attr *na, const s64 newsize, /* Prepare to mapping pairs update. */ na->allocated_size = first_free_vcn << vol->cluster_size_bits; - /* Write mapping pairs for new runlist. */ #if PARTIAL_RUNLIST_UPDATING - if (ntfs_attr_update_mapping_pairs_i(na, start_update, holes)) { + /* + * Write mapping pairs for new runlist, unless this is + * a temporary state before appending data. + * If the update is not done, we must be sure to do + * it later, and to get to a clean state even on errors. + */ + if ((holes != HOLES_DELAY) + && ntfs_attr_update_mapping_pairs_i(na, start_update, + holes)) { #else + /* Write mapping pairs for new runlist. */ if (ntfs_attr_update_mapping_pairs(na, 0)) { #endif err = errno; @@ -6646,9 +6661,8 @@ exit: * Returns the amount of data written, negative if there was an error */ -int ntfs_attr_data_write(ntfs_inode *ni, - ntfschar *stream_name, int stream_name_len, - char *buf, size_t size, off_t offset) +int ntfs_attr_data_write(ntfs_inode *ni, ntfschar *stream_name, + int stream_name_len, const char *buf, size_t size, off_t offset) { ntfs_attr *na = NULL; int res, total = 0; @@ -6680,7 +6694,7 @@ exit: } -int ntfs_attr_exist(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name, +int ntfs_attr_exist(ntfs_inode *ni, const ATTR_TYPES type, const ntfschar *name, u32 name_len) { ntfs_attr_search_ctx *ctx; diff --git a/source/attrib.h b/source/attrib.h index 67fb957..1979ba6 100644 --- a/source/attrib.h +++ b/source/attrib.h @@ -320,17 +320,18 @@ int ntfs_attr_force_non_resident(ntfs_attr *na); extern int ntfs_make_room_for_attr(MFT_RECORD *m, u8 *pos, u32 size); extern int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, - ntfschar *name, u8 name_len, u8 *val, u32 size, + const ntfschar *name, u8 name_len, const u8 *val, u32 size, ATTR_FLAGS flags); extern int ntfs_non_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, - ntfschar *name, u8 name_len, VCN lowest_vcn, int dataruns_size, - ATTR_FLAGS flags); + const ntfschar *name, u8 name_len, VCN lowest_vcn, + int dataruns_size, ATTR_FLAGS flags); extern int ntfs_attr_record_rm(ntfs_attr_search_ctx *ctx); extern int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type, - ntfschar *name, u8 name_len, u8 *val, s64 size); + ntfschar *name, u8 name_len, const u8 *val, s64 size); extern int ntfs_attr_set_flags(ntfs_inode *ni, ATTR_TYPES type, - ntfschar *name, u8 name_len, ATTR_FLAGS flags, ATTR_FLAGS mask); + const ntfschar *name, u8 name_len, ATTR_FLAGS flags, + ATTR_FLAGS mask); extern int ntfs_attr_rm(ntfs_attr *na); extern int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size); @@ -379,7 +380,7 @@ extern s64 ntfs_get_attribute_value(const ntfs_volume *vol, extern void ntfs_attr_name_free(char **name); extern char *ntfs_attr_name_get(const ntfschar *uname, const int uname_len); extern int ntfs_attr_exist(ntfs_inode *ni, const ATTR_TYPES type, - ntfschar *name, u32 name_len); + const ntfschar *name, u32 name_len); extern int ntfs_attr_remove(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name, u32 name_len); extern s64 ntfs_attr_get_free_bits(ntfs_attr *na); @@ -388,7 +389,7 @@ extern int ntfs_attr_data_read(ntfs_inode *ni, char *buf, size_t size, off_t offset); extern int ntfs_attr_data_write(ntfs_inode *ni, ntfschar *stream_name, int stream_name_len, - char *buf, size_t size, off_t offset); + const char *buf, size_t size, off_t offset); #endif /* defined _NTFS_ATTRIB_H */ diff --git a/source/bootsect.c b/source/bootsect.c index e9bea37..e9be072 100644 --- a/source/bootsect.c +++ b/source/bootsect.c @@ -215,8 +215,8 @@ int ntfs_boot_sector_parse(ntfs_volume *vol, const NTFS_BOOT_SECTOR *bs) vol->mftmirr_lcn = sle64_to_cpu(bs->mftmirr_lcn); ntfs_log_debug("MFT LCN = %lld\n", (long long)vol->mft_lcn); ntfs_log_debug("MFTMirr LCN = %lld\n", (long long)vol->mftmirr_lcn); - if (vol->mft_lcn > vol->nr_clusters || - vol->mftmirr_lcn > vol->nr_clusters) { + if ((vol->mft_lcn < 0 || vol->mft_lcn > vol->nr_clusters) || + (vol->mftmirr_lcn < 0 || vol->mftmirr_lcn > vol->nr_clusters)) { ntfs_log_error("$MFT LCN (%lld) or $MFTMirr LCN (%lld) is " "greater than the number of clusters (%lld).\n", (long long)vol->mft_lcn, (long long)vol->mftmirr_lcn, diff --git a/source/compat.h b/source/compat.h index 957752a..d680c20 100644 --- a/source/compat.h +++ b/source/compat.h @@ -31,6 +31,12 @@ #include #endif +#include /* ENODATA */ + +#ifndef ENODATA +#define ENODATA ENOENT +#endif + #ifndef PATH_MAX #define PATH_MAX 4096 #endif diff --git a/source/config.h b/source/config.h index f145094..f0ab2e8 100644 --- a/source/config.h +++ b/source/config.h @@ -107,8 +107,8 @@ /* Define to 1 if you have the header file. */ #define HAVE_MACHINE_ENDIAN_H 1 -/* Define to 1 if you have the header file. */ -#define HAVE_MATH_H 1 +/* 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. */ #define HAVE_MBRTOWC 1 @@ -116,6 +116,9 @@ /* Define to 1 if you have the `mbsinit' function. */ #define HAVE_MBSINIT 1 +/* Define to 1 if you have the `memcpy' function. */ +#define HAVE_MEMCPY 1 + /* Define to 1 if you have the `memmove' function. */ #define HAVE_MEMMOVE 1 @@ -131,6 +134,9 @@ /* Define to 1 if you have the header file. */ #define HAVE_PWD_H 1 +/* Define to 1 if you have the `random' function. */ +#undef HAVE_RANDOM + /* Define to 1 if you have the `realpath' function. */ #undef HAVE_REALPATH @@ -143,9 +149,12 @@ /* Define to 1 if you have the `setxattr' function. */ #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. */ -#define HAVE_STAT_EMPTY_STRING_BUG 1 +#undef HAVE_STAT_EMPTY_STRING_BUG /* Define to 1 if you have the header file. */ #define HAVE_STDARG_H 1 @@ -312,13 +321,13 @@ #define PACKAGE_NAME "ntfs-3g" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "ntfs-3g 2012.1.15" +#define PACKAGE_STRING "ntfs-3g 2013.1.13" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "ntfs-3g" /* Define to the version of this package. */ -#define PACKAGE_VERSION "2012.1.15" +#define PACKAGE_VERSION "2013.1.13" /* POSIX ACL support */ #undef POSIXACLS @@ -326,29 +335,8 @@ /* Define to 1 if you have the ANSI C header files. */ #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 */ -#define VERSION "2012.1.15" +#define VERSION "2013.1.13" /* Define to 1 if this is a Windows OS */ #undef WINDOWS @@ -392,3 +380,6 @@ /* Define to `unsigned int' if does not define. */ #undef size_t + +/* Define to 1 if you have the header file. */ +#define HAVE_MATH_H 1 diff --git a/source/dir.c b/source/dir.c index ccae47c..2539586 100644 --- a/source/dir.c +++ b/source/dir.c @@ -867,6 +867,87 @@ typedef enum { INDEX_TYPE_ALLOCATION, /* index allocation */ } INDEX_TYPE; +/* + * Decode Interix file types + * + * Non-Interix types are returned as plain files, because a + * Windows user may force patterns very similar to Interix, + * and most metadata files have such similar patters. + */ + +static u32 ntfs_interix_types(ntfs_inode *ni) +{ + ntfs_attr *na; + u32 dt_type; + le64 magic; + + 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; + if (na->data_size <= 1) { + if (!(ni->flags & FILE_ATTR_HIDDEN)) + dt_type = (na->data_size ? + NTFS_DT_SOCK : NTFS_DT_FIFO); + } else { + if ((na->data_size >= (s64)sizeof(magic)) + && (ntfs_attr_pread(na, 0, sizeof(magic), &magic) + == sizeof(magic))) { + if (magic == INTX_SYMBOLIC_LINK) + dt_type = NTFS_DT_LNK; + else if (magic == INTX_BLOCK_DEVICE) + dt_type = NTFS_DT_BLK; + else if (magic == INTX_CHARACTER_DEVICE) + dt_type = NTFS_DT_CHR; + } + } + ntfs_attr_close(na); + } + return (dt_type); +} + +/* + * Decode file types + * + * Better only use for Interix types and junctions, + * unneeded complexity when used for plain files or directories + * + * Error cases are logged and returned as unknown. + */ + +static u32 ntfs_dir_entry_type(ntfs_inode *dir_ni, MFT_REF mref, + FILE_ATTR_FLAGS attributes) +{ + ntfs_inode *ni; + u32 dt_type; + + dt_type = NTFS_DT_UNKNOWN; + ni = ntfs_inode_open(dir_ni->vol, mref); + if (ni) { + if ((attributes & FILE_ATTR_REPARSE_POINT) + && ntfs_possible_symlink(ni)) + dt_type = NTFS_DT_LNK; + else + if ((attributes & FILE_ATTR_SYSTEM) + && !(attributes & FILE_ATTR_I30_INDEX_PRESENT)) + dt_type = ntfs_interix_types(ni); + else + dt_type = (attributes + & FILE_ATTR_I30_INDEX_PRESENT + ? NTFS_DT_DIR : NTFS_DT_REG); + if (ntfs_inode_close(ni)) { + /* anything special worth doing ? */ + ntfs_log_error("Failed to close inode %lld\n", + (long long)MREF(mref)); + } + } + if (dt_type == NTFS_DT_UNKNOWN) + ntfs_log_error("Could not decode the type of inode %lld\n", + (long long)MREF(mref)); + return (dt_type); +} + /** * ntfs_filldir - ntfs specific filldir method * @dir_ni: ntfs inode of current directory @@ -901,19 +982,23 @@ static int ntfs_filldir(ntfs_inode *dir_ni, s64 *pos, u8 ivcn_bits, dir_ni->vol->mft_record_size; else /* if (index_type == INDEX_TYPE_ROOT) */ *pos = (u8*)ie - (u8*)iu.ir; + mref = le64_to_cpu(ie->indexed_file); + metadata = (MREF(mref) != FILE_root) && (MREF(mref) < FILE_first_user); /* Skip root directory self reference entry. */ if (MREF_LE(ie->indexed_file) == FILE_root) return 0; - if (ie->key.file_name.file_attributes & FILE_ATTR_I30_INDEX_PRESENT) + if ((ie->key.file_name.file_attributes + & (FILE_ATTR_REPARSE_POINT | FILE_ATTR_SYSTEM)) + && !metadata) + dt_type = ntfs_dir_entry_type(dir_ni, mref, + ie->key.file_name.file_attributes); + else if (ie->key.file_name.file_attributes + & FILE_ATTR_I30_INDEX_PRESENT) dt_type = NTFS_DT_DIR; - else if (fn->file_attributes & FILE_ATTR_SYSTEM) - dt_type = NTFS_DT_UNKNOWN; else dt_type = NTFS_DT_REG; /* return metadata files and hidden files if requested */ - mref = le64_to_cpu(ie->indexed_file); - metadata = (MREF(mref) != FILE_root) && (MREF(mref) < FILE_first_user); if ((!metadata && (NVolShowHidFiles(dir_ni->vol) || !(fn->file_attributes & FILE_ATTR_HIDDEN))) || (NVolShowSysFiles(dir_ni->vol) && (NVolShowHidFiles(dir_ni->vol) @@ -1397,8 +1482,8 @@ err_out: * on error with errno set to the error code. */ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, - ntfschar *name, u8 name_len, mode_t type, dev_t dev, - ntfschar *target, int target_len) + const ntfschar *name, u8 name_len, mode_t type, dev_t dev, + const ntfschar *target, int target_len) { ntfs_inode *ni; int rollback_data = 0, rollback_sd = 0; @@ -1667,7 +1752,7 @@ err_out: * Some wrappers around __ntfs_create() ... */ -ntfs_inode *ntfs_create(ntfs_inode *dir_ni, le32 securid, ntfschar *name, +ntfs_inode *ntfs_create(ntfs_inode *dir_ni, le32 securid, const ntfschar *name, u8 name_len, mode_t type) { if (type != S_IFREG && type != S_IFDIR && type != S_IFIFO && @@ -1679,7 +1764,7 @@ ntfs_inode *ntfs_create(ntfs_inode *dir_ni, le32 securid, ntfschar *name, } ntfs_inode *ntfs_create_device(ntfs_inode *dir_ni, le32 securid, - ntfschar *name, u8 name_len, mode_t type, dev_t dev) + const ntfschar *name, u8 name_len, mode_t type, dev_t dev) { if (type != S_IFCHR && type != S_IFBLK) { ntfs_log_error("Invalid arguments.\n"); @@ -1689,7 +1774,8 @@ ntfs_inode *ntfs_create_device(ntfs_inode *dir_ni, le32 securid, } ntfs_inode *ntfs_create_symlink(ntfs_inode *dir_ni, le32 securid, - ntfschar *name, u8 name_len, ntfschar *target, int target_len) + const ntfschar *name, u8 name_len, const ntfschar *target, + int target_len) { if (!target || !target_len) { ntfs_log_error("%s: Invalid argument (%p, %d)\n", __FUNCTION__, @@ -1764,7 +1850,8 @@ no_hardlink: * Return 0 on success or -1 on error with errno set to the error code. */ int ntfs_delete(ntfs_volume *vol, const char *pathname, - ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, u8 name_len) + ntfs_inode *ni, ntfs_inode *dir_ni, const ntfschar *name, + u8 name_len) { ntfs_attr_search_ctx *actx = NULL; FILE_NAME_ATTR *fn = NULL; @@ -1878,8 +1965,18 @@ search: if (ntfs_index_remove(dir_ni, ni, fn, le32_to_cpu(actx->attr->value_length))) goto err_out; - if (ntfs_attr_record_rm(actx)) - goto err_out; + /* + * 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) { + /* 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); @@ -2002,6 +2099,7 @@ search: "Leaving inconsistent metadata.\n"); } #endif + debug_double_inode(ni->mft_no,0); if (ntfs_mft_record_free(ni->vol, ni)) { err = errno; ntfs_log_error("Failed to free base MFT record. " @@ -2044,7 +2142,7 @@ err_out: * * Return 0 on success or -1 on error with errno set to the error code. */ -static int ntfs_link_i(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, +static int ntfs_link_i(ntfs_inode *ni, ntfs_inode *dir_ni, const ntfschar *name, u8 name_len, FILE_NAME_TYPE_FLAGS nametype) { FILE_NAME_ATTR *fn = NULL; @@ -2064,6 +2162,15 @@ static int ntfs_link_i(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, err = EOPNOTSUPP; goto err_out; } + if (NVolHideDotFiles(dir_ni->vol)) { + /* Set hidden flag according to the latest name */ + if ((name_len > 1) + && (name[0] == const_cpu_to_le16('.')) + && (name[1] != const_cpu_to_le16('.'))) + ni->flags |= FILE_ATTR_HIDDEN; + else + ni->flags &= ~FILE_ATTR_HIDDEN; + } /* Create FILE_NAME attribute. */ fn_len = sizeof(FILE_NAME_ATTR) + name_len * sizeof(ntfschar); @@ -2121,7 +2228,8 @@ err_out: return -1; } -int ntfs_link(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, u8 name_len) +int ntfs_link(ntfs_inode *ni, ntfs_inode *dir_ni, const ntfschar *name, + u8 name_len) { return (ntfs_link_i(ni, dir_ni, name, name_len, FILE_NAME_POSIX)); } @@ -2172,6 +2280,8 @@ ntfs_inode *ntfs_dir_parent_inode(ntfs_inode *ni) /* * Get a DOS name for a file in designated directory * + * Not allowed if there are several non-dos names (EMLINK) + * * Returns size if found * 0 if not found * -1 if there was an error (described by errno) @@ -2180,6 +2290,7 @@ ntfs_inode *ntfs_dir_parent_inode(ntfs_inode *ni) static int get_dos_name(ntfs_inode *ni, u64 dnum, ntfschar *dosname) { size_t outsize = 0; + int namecount = 0; FILE_NAME_ATTR *fn; ntfs_attr_search_ctx *ctx; @@ -2194,6 +2305,8 @@ static int get_dos_name(ntfs_inode *ni, u64 dnum, ntfschar *dosname) fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + le16_to_cpu(ctx->attr->value_offset)); + if (fn->file_name_type != FILE_NAME_DOS) + namecount++; if ((fn->file_name_type & FILE_NAME_DOS) && (MREF_LE(fn->parent_directory) == dnum)) { /* @@ -2208,6 +2321,10 @@ static int get_dos_name(ntfs_inode *ni, u64 dnum, ntfschar *dosname) } } ntfs_attr_put_search_ctx(ctx); + if ((outsize > 0) && (namecount > 1)) { + outsize = -1; + errno = EMLINK; /* this error implies there is a dos name */ + } return (outsize); } @@ -2215,6 +2332,8 @@ static int get_dos_name(ntfs_inode *ni, u64 dnum, ntfschar *dosname) /* * Get a long name for a file in designated directory * + * Not allowed if there are several non-dos names (EMLINK) + * * Returns size if found * 0 if not found * -1 if there was an error (described by errno) @@ -2223,6 +2342,7 @@ static int get_dos_name(ntfs_inode *ni, u64 dnum, ntfschar *dosname) static int get_long_name(ntfs_inode *ni, u64 dnum, ntfschar *longname) { size_t outsize = 0; + int namecount = 0; FILE_NAME_ATTR *fn; ntfs_attr_search_ctx *ctx; @@ -2238,6 +2358,8 @@ static int get_long_name(ntfs_inode *ni, u64 dnum, ntfschar *longname) fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + le16_to_cpu(ctx->attr->value_offset)); + if (fn->file_name_type != FILE_NAME_DOS) + namecount++; if ((fn->file_name_type & FILE_NAME_WIN32) && (MREF_LE(fn->parent_directory) == dnum)) { /* @@ -2247,6 +2369,11 @@ static int get_long_name(ntfs_inode *ni, u64 dnum, ntfschar *longname) outsize = fn->file_name_length; memcpy(longname,fn->file_name,outsize*sizeof(ntfschar)); } + } + if (namecount > 1) { + ntfs_attr_put_search_ctx(ctx); + errno = EMLINK; + return -1; } /* if not found search for POSIX names */ if (!outsize) { @@ -2324,7 +2451,7 @@ int ntfs_get_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, */ static int set_namespace(ntfs_inode *ni, ntfs_inode *dir_ni, - ntfschar *name, int len, + const ntfschar *name, int len, FILE_NAME_TYPE_FLAGS nametype) { ntfs_attr_search_ctx *actx; @@ -2398,9 +2525,9 @@ static int set_namespace(ntfs_inode *ni, ntfs_inode *dir_ni, */ static int set_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, - ntfschar *shortname, int shortlen, - ntfschar *longname, int longlen, - ntfschar *deletename, int deletelen, BOOL existed) + const ntfschar *shortname, int shortlen, + const ntfschar *longname, int longlen, + const ntfschar *deletename, int deletelen, BOOL existed) { unsigned int linkcount; ntfs_volume *vol; @@ -2568,7 +2695,8 @@ int ntfs_set_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, res = -1; } else { res = -1; - errno = ENOENT; + if (!longlen) + errno = ENOENT; } free(shortname); if (!closed) { @@ -2644,7 +2772,8 @@ int ntfs_remove_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni) } } } else { - errno = ENOENT; + if (!longlen) + errno = ENOENT; res = -1; } if (!deleted) { diff --git a/source/dir.h b/source/dir.h index 56e76fe..a99c1ae 100644 --- a/source/dir.h +++ b/source/dir.h @@ -68,17 +68,18 @@ extern void ntfs_inode_update_mbsname(ntfs_inode *dir_ni, const char *name, extern ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent, const char *pathname); extern ntfs_inode *ntfs_create(ntfs_inode *dir_ni, le32 securid, - ntfschar *name, u8 name_len, mode_t type); + const ntfschar *name, u8 name_len, mode_t type); extern ntfs_inode *ntfs_create_device(ntfs_inode *dir_ni, le32 securid, - ntfschar *name, u8 name_len, mode_t type, dev_t dev); + const ntfschar *name, u8 name_len, mode_t type, dev_t dev); extern ntfs_inode *ntfs_create_symlink(ntfs_inode *dir_ni, le32 securid, - ntfschar *name, u8 name_len, ntfschar *target, int target_len); + const ntfschar *name, u8 name_len, const ntfschar *target, + int target_len); extern int ntfs_check_empty_dir(ntfs_inode *ni); extern int ntfs_delete(ntfs_volume *vol, const char *path, - ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, + ntfs_inode *ni, ntfs_inode *dir_ni, const ntfschar *name, u8 name_len); -extern int ntfs_link(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, +extern int ntfs_link(ntfs_inode *ni, ntfs_inode *dir_ni, const ntfschar *name, u8 name_len); /* diff --git a/source/layout.h b/source/layout.h index c167135..427f152 100644 --- a/source/layout.h +++ b/source/layout.h @@ -2398,18 +2398,19 @@ typedef enum { IO_REPARSE_TAG_RESERVED_ONE = const_cpu_to_le32(0x00000001), IO_REPARSE_TAG_RESERVED_RANGE = const_cpu_to_le32(0x00000001), - IO_REPARSE_TAG_NSS = const_cpu_to_le32(0x68000005), - IO_REPARSE_TAG_NSS_RECOVER = const_cpu_to_le32(0x68000006), - IO_REPARSE_TAG_SIS = const_cpu_to_le32(0x68000007), - IO_REPARSE_TAG_DFS = const_cpu_to_le32(0x68000008), + IO_REPARSE_TAG_CSV = const_cpu_to_le32(0x80000009), + IO_REPARSE_TAG_DEDUP = const_cpu_to_le32(0x80000013), + IO_REPARSE_TAG_DFS = const_cpu_to_le32(0x8000000A), + IO_REPARSE_TAG_DFSR = const_cpu_to_le32(0x80000012), + IO_REPARSE_TAG_HSM = const_cpu_to_le32(0xC0000004), + IO_REPARSE_TAG_HSM2 = const_cpu_to_le32(0x80000006), + IO_REPARSE_TAG_MOUNT_POINT = const_cpu_to_le32(0xA0000003), + IO_REPARSE_TAG_NFS = const_cpu_to_le32(0x80000014), + 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_MOUNT_POINT = const_cpu_to_le32(0x88000003), - - IO_REPARSE_TAG_HSM = const_cpu_to_le32(0xa8000004), - - IO_REPARSE_TAG_SYMBOLIC_LINK = const_cpu_to_le32(0xe8000000), - - IO_REPARSE_TAG_VALID_VALUES = const_cpu_to_le32(0xe000ffff), + IO_REPARSE_TAG_VALID_VALUES = const_cpu_to_le32(0xf000ffff), } PREDEFINED_REPARSE_TAGS; /** diff --git a/source/logfile.c b/source/logfile.c index 53d8d68..214d577 100644 --- a/source/logfile.c +++ b/source/logfile.c @@ -84,13 +84,21 @@ static BOOL ntfs_check_restart_page_header(RESTART_PAGE_HEADER *rp, s64 pos) "position in $LogFile.\n"); return FALSE; } - /* We only know how to handle version 1.1. */ - if (sle16_to_cpu(rp->major_ver) != 1 || - sle16_to_cpu(rp->minor_ver) != 1) { + /* + * We only know how to handle version 1.1 and 2.0, though + * version 2.0 is probably related to cached metadata in + * 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)))) { ntfs_log_error("$LogFile version %i.%i is not " - "supported. (This driver supports version " - "1.1 only.)\n", (int)sle16_to_cpu(rp->major_ver), - (int)sle16_to_cpu(rp->minor_ver)); + "supported.\n (This driver supports version " + "1.1 and 2.0 only.)\n", + (int)sle16_to_cpu(rp->major_ver), + (int)sle16_to_cpu(rp->minor_ver)); return FALSE; } /* diff --git a/source/ntfs.c b/source/ntfs.c index 5c03a3a..115c8b8 100644 --- a/source/ntfs.c +++ b/source/ntfs.c @@ -475,20 +475,20 @@ bool ntfsMount (const char *name, const DISC_INTERFACE *interface, sec_t startSe // Build the mount flags if (flags & NTFS_READ_ONLY) - vd->flags |= MS_RDONLY; + vd->flags |= NTFS_MNT_RDONLY; else { if (!(interface->features & FEATURE_MEDIUM_CANWRITE)) - vd->flags |= MS_RDONLY; + vd->flags |= NTFS_MNT_RDONLY; if ((interface->features & FEATURE_MEDIUM_CANREAD) && (interface->features & FEATURE_MEDIUM_CANWRITE)) - vd->flags |= MS_EXCLUSIVE; + vd->flags |= NTFS_MNT_EXCLUSIVE; } if (flags & NTFS_RECOVER) - vd->flags |= MS_RECOVER; + vd->flags |= NTFS_MNT_RECOVER; if (flags & NTFS_IGNORE_HIBERFILE) - vd->flags |= MS_IGNORE_HIBERFILE; + vd->flags |= NTFS_MNT_IGNORE_HIBERFILE; - if (vd->flags & MS_RDONLY) + if (vd->flags & NTFS_MNT_RDONLY) ntfs_log_debug("Mounting \"%s\" as read-only\n", name); // Mount the device diff --git a/source/ntfsinternal.c b/source/ntfsinternal.c index c2de094..58f35c1 100644 --- a/source/ntfsinternal.c +++ b/source/ntfsinternal.c @@ -670,7 +670,7 @@ cleanup: // Unlock ntfsUnlock(vd); - return 0; + return res; } int ntfsSync (ntfs_vd *vd, ntfs_inode *ni) diff --git a/source/object_id.c b/source/object_id.c index 059e882..299357e 100644 --- a/source/object_id.c +++ b/source/object_id.c @@ -46,6 +46,7 @@ #include #endif +#include "compat.h" #include "types.h" #include "debug.h" #include "attrib.h" diff --git a/source/param.h b/source/param.h index c3675b7..b4ef326 100644 --- a/source/param.h +++ b/source/param.h @@ -44,12 +44,18 @@ enum { */ /* default option for compression */ -#define DEFAULT_COMPRESSION FALSE +#define DEFAULT_COMPRESSION 0 /* (log2 of) number of clusters in a compression block for new files */ #define STANDARD_COMPRESSION_UNIT 4 /* maximum cluster size for allowing compression for new files */ #define MAX_COMPRESSION_CLUSTER_SIZE 4096 +/* + * Parameters for default options + */ + +#define DEFAULT_DMTIME 60 /* default 1mn for delay_mtime */ + /* * Use of big write buffers * @@ -109,7 +115,11 @@ enum { * 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 #define HPERMSCONFIG 1 +#endif #if defined(FUSE_INTERNAL) || !defined(FUSE_VERSION) || (FUSE_VERSION < 28) #define LPERMSCONFIG 5 #else diff --git a/source/reparse.c b/source/reparse.c index 05490bf..dc44bcd 100644 --- a/source/reparse.c +++ b/source/reparse.c @@ -3,7 +3,7 @@ * * This module is part of ntfs-3g library * - * Copyright (c) 2008-2009 Jean-Pierre Andre + * Copyright (c) 2008-2012 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 @@ -46,8 +46,10 @@ #include #endif +#include "compat.h" #include "types.h" #include "debug.h" +#include "layout.h" #include "attrib.h" #include "inode.h" #include "dir.h" @@ -59,18 +61,6 @@ #include "misc.h" #include "reparse.h" -/* the definitions in layout.h are wrong, we use names defined in - http://msdn.microsoft.com/en-us/library/aa365740(VS.85).aspx -*/ - -#define IO_REPARSE_TAG_DFS const_cpu_to_le32(0x8000000A) -#define IO_REPARSE_TAG_DFSR const_cpu_to_le32(0x80000012) -#define IO_REPARSE_TAG_HSM const_cpu_to_le32(0xC0000004) -#define IO_REPARSE_TAG_HSM2 const_cpu_to_le32(0x80000006) -#define IO_REPARSE_TAG_MOUNT_POINT const_cpu_to_le32(0xA0000003) -#define IO_REPARSE_TAG_SIS const_cpu_to_le32(0x80000007) -#define IO_REPARSE_TAG_SYMLINK const_cpu_to_le32(0xA000000C) - struct MOUNT_POINT_REPARSE_DATA { /* reparse data for junctions */ le16 subst_name_offset; le16 subst_name_length; @@ -235,6 +225,17 @@ static char *search_absolute(ntfs_volume *vol, ntfschar *path, ni = ntfs_inode_open(vol, (MFT_REF)FILE_root); if (ni) { start = 0; + /* + * Examine and translate the path, until we reach either + * - the end, + * - an unknown item + * - a non-directory + * - another reparse point, + * A reparse point is not dereferenced, it will be + * examined later when the translated path is dereferenced, + * however the final part of the path will not be adjusted + * to correct case. + */ do { len = 0; while (((start + len) < count) @@ -252,9 +253,11 @@ static char *search_absolute(ntfs_volume *vol, ntfschar *path, } } while (ni && (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + && !(ni->flags & FILE_ATTR_REPARSE_POINT) && (start < count)); if (ni - && (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ? isdir : !isdir)) + && ((ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ? isdir : !isdir) + || (ni->flags & FILE_ATTR_REPARSE_POINT))) if (ntfs_ucstombs(path, count, &target, 0) < 0) { if (target) { free(target); @@ -288,12 +291,25 @@ static char *search_relative(ntfs_inode *ni, ntfschar *path, int count) int pos; int lth; BOOL ok; + BOOL morelinks; int max = 32; /* safety */ pos = 0; ok = TRUE; + morelinks = FALSE; curni = ntfs_dir_parent_inode(ni); - while (curni && ok && (pos < (count - 1)) && --max) { + /* + * Examine and translate the path, until we reach either + * - the end, + * - an unknown item + * - a non-directory + * - another reparse point, + * A reparse point is not dereferenced, it will be + * examined later when the translated path is dereferenced, + * however the final part of the path will not be adjusted + * to correct case. + */ + while (curni && ok && !morelinks && (pos < (count - 1)) && --max) { if ((count >= (pos + 2)) && (path[pos] == const_cpu_to_le16('.')) && (path[pos+1] == const_cpu_to_le16('\\'))) { @@ -331,12 +347,18 @@ static char *search_relative(ntfs_inode *ni, ntfschar *path, int count) if (!curni) ok = FALSE; else { + if (curni->flags & FILE_ATTR_REPARSE_POINT) + morelinks = TRUE; if (ok && ((pos + lth) < count)) { path[pos + lth] = const_cpu_to_le16('/'); pos += lth + 1; + if (morelinks + && ntfs_inode_close(curni)) + ok = FALSE; } else { pos += lth; - if ((ni->mrec->flags ^ curni->mrec->flags) + if (!morelinks + && (ni->mrec->flags ^ curni->mrec->flags) & MFT_RECORD_IS_DIRECTORY) ok = FALSE; if (ntfs_inode_close(curni)) diff --git a/source/security.c b/source/security.c index 5e70c21..57e7331 100644 --- a/source/security.c +++ b/source/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-2010 Jean-Pierre Andre + * Copyright (c) 2007-2012 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 @@ -52,6 +52,7 @@ #include #include +#include "compat.h" #include "param.h" #include "types.h" #include "layout.h" @@ -1128,10 +1129,86 @@ static BOOL staticgroupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid return (ingroup); } +#if defined(__sun) && defined (__SVR4) /* * Check whether current thread owner is member of file group + * Solaris/OpenIndiana version + * Should not be called for user root, however the group may be root * + * The group list is available in "/proc/$PID/cred" + * + */ + +static BOOL groupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid) +{ + typedef struct prcred { + uid_t pr_euid; /* effective user id */ + uid_t pr_ruid; /* real user id */ + uid_t pr_suid; /* saved user id (from exec) */ + gid_t pr_egid; /* effective group id */ + gid_t pr_rgid; /* real group id */ + gid_t pr_sgid; /* saved group id (from exec) */ + int pr_ngroups; /* number of supplementary groups */ + gid_t pr_groups[1]; /* array of supplementary groups */ + } prcred_t; + enum { readset = 16 }; + + prcred_t basecreds; + gid_t groups[readset]; + char filename[64]; + int fd; + int k; + int cnt; + gid_t *p; + BOOL ismember; + int got; + pid_t tid; + + if (scx->vol->secure_flags & (1 << SECURITY_STATICGRPS)) + ismember = staticgroupmember(scx, uid, gid); + else { + ismember = FALSE; /* default return */ + tid = scx->tid; + sprintf(filename,"/proc/%u/cred",tid); + fd = open(filename,O_RDONLY); + if (fd >= 0) { + got = read(fd, &basecreds, sizeof(prcred_t)); + if (got == sizeof(prcred_t)) { + if (basecreds.pr_egid == gid) + ismember = TRUE; + p = basecreds.pr_groups; + cnt = 1; + k = 0; + while (!ismember + && (k < basecreds.pr_ngroups) + && (cnt > 0) + && (*p != gid)) { + k++; + cnt--; + p++; + if (cnt <= 0) { + got = read(fd, groups, + readset*sizeof(gid_t)); + cnt = got/sizeof(gid_t); + p = groups; + } + } + if ((cnt > 0) + && (k < basecreds.pr_ngroups)) + ismember = TRUE; + } + close(fd); + } + } + return (ismember); +} + +#else /* defined(__sun) && defined (__SVR4) */ + +/* + * Check whether current thread owner is member of file group + * Linux version * Should not be called for user root, however the group may be root * * As indicated by Miklos Szeredi : @@ -1234,6 +1311,8 @@ static BOOL groupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid) return (ismember); } +#endif /* defined(__sun) && defined (__SVR4) */ + /* * Cacheing is done two-way : * - from uid, gid and perm to securid (CACHED_SECURID) @@ -1797,7 +1876,7 @@ static int access_check_posix(struct SECURITY_CONTEXT *scx, if (!scx->uid) { /* root access if owner or other execution */ if (perms & 0101) - perms = 07777; + perms |= 01777; else { /* root access if some group execution */ groupperms = 0; @@ -2213,7 +2292,7 @@ static int ntfs_get_perm(struct SECURITY_CONTEXT *scx, if (!scx->uid) { /* root access and execution */ if (perm & 0111) - perm = 07777; + perm |= 01777; else perm = 0; } else @@ -3316,6 +3395,56 @@ int ntfs_allowed_access(struct SECURITY_CONTEXT *scx, return (allow); } +/* + * Check whether user can create a file (or directory) + * + * Returns TRUE if access is allowed, + * Also returns the gid and dsetgid applicable to the created file + */ + +int ntfs_allowed_create(struct SECURITY_CONTEXT *scx, + ntfs_inode *dir_ni, gid_t *pgid, mode_t *pdsetgid) +{ + int perm; + int res; + int allow; + struct stat stbuf; + + /* + * Always allow for root. + * Also always allow if no mapping has been defined + */ + if (!scx->mapping[MAPUSERS]) + perm = 0777; + else + perm = ntfs_get_perm(scx, dir_ni, S_IWRITE + S_IEXEC); + if (!scx->mapping[MAPUSERS] + || !scx->uid) { + allow = 1; + } else { + perm = ntfs_get_perm(scx, dir_ni, S_IWRITE + S_IEXEC); + if (perm >= 0) { + res = EACCES; + allow = ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0) + && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0); + if (!allow) + errno = res; + } else + allow = 0; + } + *pgid = scx->gid; + *pdsetgid = 0; + /* return directory group if S_ISGID is set */ + if (allow && (perm & S_ISGID)) { + if (ntfs_get_owner_mode(scx, dir_ni, &stbuf) >= 0) { + *pdsetgid = stbuf.st_mode & S_ISGID; + if (perm & S_ISGID) + *pgid = stbuf.st_gid; + } + } + return (allow); +} + #if 0 /* not needed any more */ /* @@ -3467,10 +3596,12 @@ int ntfs_set_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, uid = fileuid; if ((int)gid < 0) gid = filegid; +#if !defined(__sun) || !defined (__SVR4) /* clear setuid and setgid if owner has changed */ /* unless request originated by root */ if (uid && (fileuid != uid)) mode &= 01777; +#endif #if POSIXACLS res = ntfs_set_owner_mode(scx, ni, uid, gid, mode, pxdesc); @@ -3679,7 +3810,9 @@ static le32 build_inherited_id(struct SECURITY_CONTEXT *scx, pnhead = (SECURITY_DESCRIPTOR_RELATIVE*)newattr; pnhead->revision = SECURITY_DESCRIPTOR_REVISION; pnhead->alignment = 0; - pnhead->control = SE_SELF_RELATIVE; + pnhead->control = (pphead->control + & (SE_DACL_AUTO_INHERITED | SE_SACL_AUTO_INHERITED)) + | SE_SELF_RELATIVE; pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE); /* * locate and inherit DACL @@ -3690,7 +3823,9 @@ static le32 build_inherited_id(struct SECURITY_CONTEXT *scx, offpacl = le32_to_cpu(pphead->dacl); ppacl = (const ACL*)&parentattr[offpacl]; pnacl = (ACL*)&newattr[pos]; - aclsz = ntfs_inherit_acl(ppacl, pnacl, usid, gsid, fordir); + aclsz = ntfs_inherit_acl(ppacl, pnacl, usid, gsid, + fordir, pphead->control + & SE_DACL_AUTO_INHERITED); if (aclsz) { pnhead->dacl = cpu_to_le32(pos); pos += aclsz; @@ -3705,7 +3840,9 @@ static le32 build_inherited_id(struct SECURITY_CONTEXT *scx, offpacl = le32_to_cpu(pphead->sacl); ppacl = (const ACL*)&parentattr[offpacl]; pnacl = (ACL*)&newattr[pos]; - aclsz = ntfs_inherit_acl(ppacl, pnacl, usid, gsid, fordir); + aclsz = ntfs_inherit_acl(ppacl, pnacl, usid, gsid, + fordir, pphead->control + & SE_SACL_AUTO_INHERITED); if (aclsz) { pnhead->sacl = cpu_to_le32(pos); pos += aclsz; @@ -3723,7 +3860,7 @@ static le32 build_inherited_id(struct SECURITY_CONTEXT *scx, */ memcpy(&newattr[pos],gsid,gsidsz); pnhead->group = cpu_to_le32(pos); - pos += usidsz; + pos += gsidsz; securid = setsecurityattr(scx->vol, (SECURITY_DESCRIPTOR_RELATIVE*)newattr, pos); free(newattr); diff --git a/source/security.h b/source/security.h index 9d7dd33..8875c9c 100644 --- a/source/security.h +++ b/source/security.h @@ -255,6 +255,8 @@ int ntfs_set_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, mode_t mode); BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni); int ntfs_allowed_access(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, int accesstype); +int ntfs_allowed_create(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, gid_t *pgid, mode_t *pdsetgid); BOOL old_ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx, const char *path, int accesstype); diff --git a/source/unistr.c b/source/unistr.c index c0ac342..9a23801 100644 --- a/source/unistr.c +++ b/source/unistr.c @@ -710,9 +710,9 @@ static int utf8_to_unicode(u32 *wc, const char *s) | ((u32)(s[1] & 0x3F) << 12) | ((u32)(s[2] & 0x3F) << 6) | ((u32)(s[3] & 0x3F)); - /* Check valid ranges */ - if ((*wc <= 0x10ffff) && (*wc >= 0x10000)) - return 4; + /* Check valid ranges */ + if ((*wc <= 0x10ffff) && (*wc >= 0x10000)) + return 4; } goto fail; } diff --git a/source/volume.c b/source/volume.c index a18109f..5bb266f 100644 --- a/source/volume.c +++ b/source/volume.c @@ -54,6 +54,10 @@ #include #endif +#if defined(__sun) && defined (__SVR4) +#include +#endif + #include "param.h" #include "compat.h" #include "volume.h" @@ -89,13 +93,9 @@ static const char *corrupt_volume_msg = "for more details.\n"; static const char *hibernated_volume_msg = -"The NTFS partition is hibernated. Please resume and shutdown Windows\n" -"properly, or mount the volume read-only with the 'ro' mount option, or\n" -"mount the volume read-write with the 'remove_hiberfile' mount option.\n" -"For example type on the command line:\n" -"\n" -" mount -t ntfs-3g -o remove_hiberfile %s %s\n" -"\n"; +"The NTFS partition is in an unsafe state. Please resume and shutdown\n" +"Windows fully (no hibernation or fast restarting), or mount the volume\n" +"read-only with the 'ro' mount option.\n"; static const char *unclean_journal_msg = "Write access is denied because the disk wasn't safely powered\n" @@ -466,7 +466,8 @@ error_exit: * Return the allocated volume structure on success and NULL on error with * errno set to the error code. */ -ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, unsigned long flags) +ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, + ntfs_mount_flags flags) { LCN mft_zone_size, mft_lcn; s64 br; @@ -508,13 +509,25 @@ ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, unsigned long flags) #else NVolClearCompression(vol); #endif - if (flags & MS_RDONLY) + 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)) { - ntfs_log_perror("Error opening '%s'", dev->d_name); - goto error_exit; + if (!NVolReadOnly(vol) && (errno == EROFS)) { + if ((dev->d_ops->open)(dev, O_RDONLY)) { + ntfs_log_perror("Error opening read-only '%s'", + dev->d_name); + goto error_exit; + } else { + ntfs_log_info("Can only open '%s' as read-only\n", + dev->d_name); + NVolSetReadOnly(vol); + } + } else { + ntfs_log_perror("Error opening '%s'", dev->d_name); + goto error_exit; + } } /* Attach the device to the volume. */ vol->dev = dev; @@ -649,6 +662,24 @@ static int ntfs_volume_check_logfile(ntfs_volume *vol) if (!ntfs_check_logfile(na, &rp) || !ntfs_is_logfile_clean(na, rp)) err = EOPNOTSUPP; + /* + * If the latest restart page was identified as version + * 2.0, then Windows may have kept a cached copy of + * metadata for fast restarting, and we should not mount. + * Hibernation will be seen the same way on a non + * Windows-system partition, so we have to use the same + * error code (EPERM). + * The restart page may also be identified as version 2.0 + * when access to the file system is terminated abruptly + * by unplugging or power cut, so mounting is also rejected + * after such an event. + */ + if (rp + && (rp->major_ver == const_cpu_to_le16(2)) + && (rp->minor_ver == const_cpu_to_le16(0))) { + ntfs_log_error("Metadata kept in Windows cache, refused to mount.\n"); + err = EPERM; + } free(rp); ntfs_attr_close(na); out: @@ -762,7 +793,8 @@ int ntfs_volume_check_hiberfile(ntfs_volume *vol, int verbose) errno = EPERM; goto out; } - if (memcmp(buf, "hibr", 4) == 0) { + if ((memcmp(buf, "hibr", 4) == 0) + || (memcmp(buf, "HIBR", 4) == 0)) { if (verbose) ntfs_log_error("Windows is hibernated, refused to mount.\n"); errno = EPERM; @@ -856,7 +888,7 @@ static int fix_txf_data(ntfs_volume *vol) * @flags is an optional second parameter. The same flags are used as for * the mount system call (man 2 mount). Currently only the following flag * is implemented: - * MS_RDONLY - mount volume read-only + * NTFS_MNT_RDONLY - mount volume read-only * * The function opens the device @dev and verifies that it contains a valid * bootsector. Then, it allocates an ntfs_volume structure and initializes @@ -867,7 +899,7 @@ static int fix_txf_data(ntfs_volume *vol) * Return the allocated volume structure on success and NULL on error with * errno set to the error code. */ -ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) +ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags) { s64 l; ntfs_volume *vol; @@ -1195,12 +1227,13 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) * Check for dirty logfile and hibernated Windows. * We care only about read-write mounts. */ - if (!(flags & (MS_RDONLY | MS_FORENSIC))) { - if (!(flags & MS_IGNORE_HIBERFILE) && + if (!(flags & (NTFS_MNT_RDONLY | NTFS_MNT_FORENSIC))) { + if (!(flags & NTFS_MNT_IGNORE_HIBERFILE) && ntfs_volume_check_hiberfile(vol, 1) < 0) goto error_exit; if (ntfs_volume_check_logfile(vol) < 0) { - if (!(flags & MS_RECOVER)) + /* 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"); @@ -1289,7 +1322,7 @@ int ntfs_set_ignore_case(ntfs_volume *vol) * @flags is an optional second parameter. The same flags are used as for * the mount system call (man 2 mount). Currently only the following flags * is implemented: - * MS_RDONLY - mount volume read-only + * NTFS_MNT_RDONLY - mount volume read-only * * The function opens the device or file @name and verifies that it contains a * valid bootsector. Then, it allocates an ntfs_volume structure and initializes @@ -1304,7 +1337,7 @@ int ntfs_set_ignore_case(ntfs_volume *vol) * soon as the function returns. */ ntfs_volume *ntfs_mount(const char *name __attribute__((unused)), - unsigned long flags __attribute__((unused))) + ntfs_mount_flags flags __attribute__((unused))) { #ifndef NO_NTFS_DEVICE_DEFAULT_IO_OPS struct ntfs_device *dev; @@ -1429,6 +1462,60 @@ exit: } return 0; } + +#else /* HAVE_MNTENT_H */ + +#if defined(__sun) && defined (__SVR4) + +static int ntfs_mntent_check(const char *file, unsigned long *mnt_flags) +{ + struct mnttab *mnt = NULL; + char *real_file = NULL, *real_fsname = NULL; + FILE *f; + int err = 0; + + real_file = (char*)ntfs_malloc(PATH_MAX + 1); + if (!real_file) + return -1; + real_fsname = (char*)ntfs_malloc(PATH_MAX + 1); + mnt = (struct mnttab*)ntfs_malloc(MNT_LINE_MAX + 1); + if (!real_fsname || !mnt) { + err = errno; + goto exit; + } + if (!ntfs_realpath_canonicalize(file, real_file)) { + err = errno; + goto exit; + } + if (!(f = fopen(MNTTAB, "r"))) { + err = errno; + goto exit; + } + while (!getmntent(f, mnt)) { + if (!ntfs_realpath_canonicalize(mnt->mnt_special, real_fsname)) + continue; + if (!strcmp(real_file, real_fsname)) { + *mnt_flags = NTFS_MF_MOUNTED; + if (!strcmp(mnt->mnt_mountp, "/")) + *mnt_flags |= NTFS_MF_ISROOT; + if (hasmntopt(mnt, "ro") && !hasmntopt(mnt, "rw")) + *mnt_flags |= NTFS_MF_READONLY; + break; + } + } + fclose(f); +exit: + free(mnt); + free(real_file); + free(real_fsname); + if (err) { + errno = err; + return -1; + } + return 0; +} + +#endif /* defined(__sun) && defined (__SVR4) */ #endif /* HAVE_MNTENT_H */ /** @@ -1460,7 +1547,7 @@ int ntfs_check_if_mounted(const char *file __attribute__((unused)), unsigned long *mnt_flags) { *mnt_flags = 0; -#ifdef HAVE_MNTENT_H +#if defined(HAVE_MNTENT_H) || (defined(__sun) && defined (__SVR4)) return ntfs_mntent_check(file, mnt_flags); #else return 0; @@ -1642,6 +1729,10 @@ int ntfs_volume_error(int err) ret = NTFS_VOLUME_CORRUPT; break; case EPERM: + /* + * Hibernation and fast restarting are seen the + * same way on a non Windows-system partition. + */ ret = NTFS_VOLUME_HIBERNATED; break; case EOPNOTSUPP: @@ -1742,7 +1833,7 @@ int ntfs_volume_get_free_space(ntfs_volume *vol) * * Change the label on the volume @vol to @label. */ -int ntfs_volume_rename(ntfs_volume *vol, ntfschar *label, int label_len) +int ntfs_volume_rename(ntfs_volume *vol, const ntfschar *label, int label_len) { ntfs_attr *na; char *old_vol_name; @@ -1777,7 +1868,7 @@ int ntfs_volume_rename(ntfs_volume *vol, ntfschar *label, int label_len) /* The volume name attribute does not exist. Need to add it. */ if (ntfs_attr_add(vol->vol_ni, AT_VOLUME_NAME, AT_UNNAMED, 0, - (u8*) label, label_len)) + (const u8*) label, label_len)) { err = errno; ntfs_log_perror("Encountered error while adding " diff --git a/source/volume.h b/source/volume.h index 9d62d66..a181ccc 100644 --- a/source/volume.h +++ b/source/volume.h @@ -43,23 +43,6 @@ #include #endif -/* - * Under Cygwin, DJGPP and FreeBSD we do not have MS_RDONLY, - * so we define them ourselves. - */ -#ifndef MS_RDONLY -#define MS_RDONLY 1 -#endif - -#define MS_EXCLUSIVE 0x08000000 - -#ifndef MS_RECOVER -#define MS_RECOVER 0x10000000 -#endif - -#define MS_IGNORE_HIBERFILE 0x20000000 -#define MS_FORENSIC 0x04000000 /* No modification during mount */ - /* Forward declaration */ typedef struct _ntfs_volume ntfs_volume; @@ -74,13 +57,29 @@ typedef struct _ntfs_volume ntfs_volume; /** * enum ntfs_mount_flags - * + * Flags for the ntfs_mount() function. + */ +enum { + NTFS_MNT_NONE = 0x00000000, + NTFS_MNT_RDONLY = 0x00000001, + NTFS_MNT_FORENSIC = 0x04000000, /* No modification during + * mount. */ + NTFS_MNT_EXCLUSIVE = 0x08000000, + NTFS_MNT_RECOVER = 0x10000000, + NTFS_MNT_IGNORE_HIBERFILE = 0x20000000, +}; +typedef unsigned long ntfs_mount_flags; + +/** + * enum ntfs_mounted_flags - + * * Flags returned by the ntfs_check_if_mounted() function. */ typedef enum { NTFS_MF_MOUNTED = 1, /* Device is mounted. */ NTFS_MF_ISROOT = 2, /* Device is mounted as system root. */ NTFS_MF_READONLY = 4, /* Device is mounted read-only. */ -} ntfs_mount_flags; +} ntfs_mounted_flags; extern int ntfs_check_if_mounted(const char *file, unsigned long *mnt_flags); @@ -284,12 +283,12 @@ extern const char *ntfs_home; extern ntfs_volume *ntfs_volume_alloc(void); extern ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, - unsigned long flags); + ntfs_mount_flags flags); extern ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, - unsigned long flags); + ntfs_mount_flags flags); -extern ntfs_volume *ntfs_mount(const char *name, unsigned long flags); +extern ntfs_volume *ntfs_mount(const char *name, ntfs_mount_flags flags); extern int ntfs_umount(ntfs_volume *vol, const BOOL force); extern int ntfs_version_is_supported(ntfs_volume *vol); @@ -302,7 +301,8 @@ extern int ntfs_volume_error(int err); extern void ntfs_mount_error(const char *vol, const char *mntpoint, int err); extern int ntfs_volume_get_free_space(ntfs_volume *vol); -extern int ntfs_volume_rename(ntfs_volume *vol, ntfschar *label, int label_len); +extern int ntfs_volume_rename(ntfs_volume *vol, const ntfschar *label, + int label_len); extern int ntfs_set_shown_files(ntfs_volume *vol, BOOL show_sys_files, BOOL show_hid_files, BOOL hide_dot_files);