diff --git a/source/acls.c b/source/acls.c index 76cc6ce..6328420 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-2009 Jean-Pierre Andre + * Copyright (c) 2007-2010 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 @@ -3956,33 +3956,6 @@ static SID *encodesid(const char *sidstr) return (sid); } -/* - * Early logging before the logs are redirected - * - * (not quite satisfactory : this appears before the ntfs-g banner, - * and with a different pid) - */ - -static void log_early_error(const char *format, ...) - __attribute__((format(printf, 1, 2))); - -static void log_early_error(const char *format, ...) -{ - va_list args; - - va_start(args, format); -#ifdef HAVE_SYSLOG_H - openlog("ntfs-3g", LOG_PID, LOG_USER); - ntfs_log_handler_syslog(NULL, NULL, 0, - NTFS_LOG_LEVEL_ERROR, NULL, - format, args); -#else - vfprintf(stderr,format,args); -#endif - va_end(args); -} - - /* * Get a single mapping item from buffer * @@ -4045,7 +4018,7 @@ static struct MAPLIST *getmappingitem(FILEREADER reader, void *fileid, if (pu && pg) *pu = *pg = '\0'; else { - log_early_error("Bad mapping item \"%s\"\n", + ntfs_log_early_error("Bad mapping item \"%s\"\n", item->maptext); free(item); item = (struct MAPLIST*)NULL; @@ -4174,7 +4147,7 @@ struct MAPPING *ntfs_do_user_mapping(struct MAPLIST *firstitem) if (pwd) uid = pwd->pw_uid; else - log_early_error("Invalid user \"%s\"\n", + ntfs_log_early_error("Invalid user \"%s\"\n", item->uidstr); } } @@ -4254,7 +4227,7 @@ struct MAPPING *ntfs_do_group_mapping(struct MAPLIST *firstitem) if (grp) gid = grp->gr_gid; else - log_early_error("Invalid group \"%s\"\n", + ntfs_log_early_error("Invalid group \"%s\"\n", item->gidstr); } } diff --git a/source/attrib.c b/source/attrib.c index 123c9a9..680eff3 100644 --- a/source/attrib.c +++ b/source/attrib.c @@ -402,7 +402,7 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, ntfs_attr *na = NULL; ntfschar *newname = NULL; ATTR_RECORD *a; - BOOL cs; + le16 cs; ntfs_log_enter("Entering for inode %lld, attr 0x%x.\n", (unsigned long long)ni->mft_no, type); @@ -588,6 +588,74 @@ int ntfs_attr_map_runlist(ntfs_attr *na, VCN vcn) return -1; } +#if PARTIAL_RUNLIST_UPDATING + +/* + * Map the runlist of an attribute from some point to the end + * + * Returns 0 if success, + * -1 if it failed (errno telling why) + */ + +static int ntfs_attr_map_partial_runlist(ntfs_attr *na, VCN vcn) +{ + LCN lcn; + VCN last_vcn; + VCN highest_vcn; + VCN needed; + runlist_element *rl; + ATTR_RECORD *a; + BOOL startseen; + ntfs_attr_search_ctx *ctx; + + lcn = ntfs_rl_vcn_to_lcn(na->rl, vcn); + if (lcn >= 0 || lcn == LCN_HOLE || lcn == LCN_ENOENT) + return 0; + + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) + return -1; + + /* Get the last vcn in the attribute. */ + last_vcn = na->allocated_size >> na->ni->vol->cluster_size_bits; + + needed = vcn; + highest_vcn = 0; + startseen = FALSE; + do { + /* 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); + 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; + } + needed = highest_vcn + 1; + if (!a->lowest_vcn) + startseen = TRUE; + } + } else + rl = (runlist_element*)NULL; + } while (rl && (needed < last_vcn)); + ntfs_attr_put_search_ctx(ctx); + /* mark fully mapped if we did so */ + if (rl && startseen) + NAttrSetFullyMapped(na); + return (rl ? 0 : -1); +} + +#endif + /** * ntfs_attr_map_whole_runlist - map the whole runlist of an ntfs attribute * @na: ntfs attribute for which to map the runlist @@ -1149,9 +1217,27 @@ static int ntfs_attr_fill_hole(ntfs_attr *na, s64 count, s64 *ofs, "%lld\n", (long long)count, (long long)cur_vcn, (long long)from_vcn, (long long)to_write, (long long)*ofs); - /* Map whole runlist to be able update mapping pairs later. */ + /* Map the runlist to be able to update mapping pairs later. */ +#if PARTIAL_RUNLIST_UPDATING + if ((!na->rl + || !NAttrDataAppending(na))) { + 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; + } + } +#else if (ntfs_attr_map_whole_runlist(na)) goto err_out; +#endif /* Restore @*rl, it probably get lost during runlist mapping. */ *rl = ntfs_attr_find_vcn(na, cur_vcn); @@ -1510,14 +1596,27 @@ static int borrow_from_hole(ntfs_attr *na, runlist_element **prl, if (undecided || nothole) { runlist_element *orl = na->rl; s64 olcn = (*prl)->lcn; +#if PARTIAL_RUNLIST_UPDATING + VCN prevblock; +#endif /* - * Map the full runlist (needed to compute the - * compressed size), unless the runlist has not - * yet been created (data just made non-resident) + * Map the runlist, unless it has not been created. + * If appending data, a partial mapping from the + * end of previous block will do. */ irl = *prl - na->rl; +#if PARTIAL_RUNLIST_UPDATING + prevblock = pos >> cluster_size_bits; + if (prevblock) + prevblock--; + if (!NAttrBeingNonResident(na) + && (NAttrDataAppending(na) + ? ntfs_attr_map_partial_runlist(na,prevblock) + : ntfs_attr_map_whole_runlist(na))) { +#else if (!NAttrBeingNonResident(na) && ntfs_attr_map_whole_runlist(na)) { +#endif rl = (runlist_element*)NULL; } else { /* @@ -1649,6 +1748,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 sparse; BOOL updatemap; ntfs_log_enter("Entering for inode %lld, attr 0x%x, pos 0x%llx, count " @@ -1703,16 +1803,38 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) fullcount = count; /* If the write reaches beyond the end, extend the attribute. */ old_data_size = na->data_size; + /* identify whether this is appending to a non resident data attribute */ + if ((na->type == AT_DATA) && (pos >= old_data_size) + && NAttrNonResident(na)) + NAttrSetDataAppending(na); if (pos + count > na->data_size) { +#if PARTIAL_RUNLIST_UPDATING + /* + * When appending data, the attribute is first extended + * before being filled with data. This may cause the + * attribute to be made temporarily sparse, which + * implies reformating the inode and reorganizing the + * full runlist. To avoid unnecessary reorganization, + * we delay sparse testing until the data is filled in. + * + * Note : should add a specific argument to truncate() + * instead of the hackish test of a flag... + */ + if (NAttrDataAppending(na)) + NAttrSetDelaySparsing(na); +#endif if (ntfs_attr_truncate(na, pos + count)) { + NAttrClearDelaySparsing(na); ntfs_log_perror("Failed to enlarge attribute"); goto errno_set; } + NAttrClearDelaySparsing(na); /* resizing may change the compression mode */ compressed = (na->data_flags & ATTR_COMPRESSION_MASK) != const_cpu_to_le16(0); need_to.undo_data_size = 1; } + sparse = (na->data_flags & ATTR_IS_SPARSE) != const_cpu_to_le16(0); /* * For compressed data, a single full block was allocated * to deal with compression, possibly in a previous call. @@ -1768,8 +1890,29 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) /* Handle writes beyond initialized_size. */ if (pos + count > na->initialized_size) { - if (ntfs_attr_map_whole_runlist(na)) - goto err_out; +#if PARTIAL_RUNLIST_UPDATING + /* + * When appending, we only need to map the end of the runlist, + * starting at the last previously allocated run, so that + * we are able a new one to it. + * 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) + block_begin &= -na->compression_block_clusters; + if (block_begin) + block_begin--; + if (ntfs_attr_map_partial_runlist(na, block_begin)) + goto err_out; + if ((update_from == -1) || (block_begin < update_from)) + update_from = block_begin; + } else +#endif + if (ntfs_attr_map_whole_runlist(na)) + goto err_out; /* * For a compressed attribute, we must be sure there is an * available entry, and, when reopening a compressed file, @@ -2046,8 +2189,12 @@ 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 (ntfs_attr_update_mapping_pairs(na, (update_from < 0 ? 0 : update_from))) { @@ -4864,6 +5011,15 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize, ntfs_attr_close(tna); continue; } + if ((tna->type == AT_DATA) && !tna->name_len) { + /* + * If we had to make the unnamed data attribute + * non-resident, propagate its new allocated size + * to all name attributes and directory indexes + */ + tna->ni->allocated_size = tna->allocated_size; + NInoFileNameSetDirty(tna->ni); + } if (((tna->data_flags & ATTR_COMPRESSION_MASK) == ATTR_IS_COMPRESSED) && ntfs_attr_pclose(tna)) { @@ -5204,15 +5360,21 @@ static int ntfs_attr_update_meta(ATTR_RECORD *a, ntfs_attr *na, MFT_RECORD *m, a->allocated_size = cpu_to_sle64(na->allocated_size); - /* Update sparse bit. */ - sparse = ntfs_rl_sparse(na->rl); - if (sparse == -1) { - errno = EIO; - goto error; + /* Update sparse bit, unless this is an intermediate state */ + if (NAttrDelaySparsing(na)) + sparse = (a->flags & ATTR_IS_SPARSE) != const_cpu_to_le16(0); + else { + sparse = ntfs_rl_sparse(na->rl); + if (sparse == -1) { + errno = EIO; + goto error; + } } - /* Attribute become sparse. */ - if (sparse && !(a->flags & (ATTR_IS_SPARSE | ATTR_IS_COMPRESSED))) { + /* Check whether attribute becomes sparse, unless check is delayed. */ + if (!NAttrDelaySparsing(na) + && sparse + && !(a->flags & (ATTR_IS_SPARSE | ATTR_IS_COMPRESSED))) { /* * Move attribute to another mft record, if attribute is too * small to add compressed_size field to it and we have no @@ -5245,6 +5407,7 @@ static int ntfs_attr_update_meta(ATTR_RECORD *a, ntfs_attr *na, MFT_RECORD *m, NAttrSetSparse(na); a->flags |= ATTR_IS_SPARSE; + na->data_flags = a->flags; a->compression_unit = STANDARD_COMPRESSION_UNIT; /* Windows set it so, even if attribute is not actually compressed. */ @@ -5278,7 +5441,8 @@ static int ntfs_attr_update_meta(ATTR_RECORD *a, ntfs_attr *na, MFT_RECORD *m, } /* Update compressed size if required. */ - if (sparse || (na->data_flags & ATTR_COMPRESSION_MASK)) { + if (NAttrFullyMapped(na) + && (sparse || (na->data_flags & ATTR_COMPRESSION_MASK))) { s64 new_compr_size; new_compr_size = ntfs_rl_get_compressed_size(na->ni->vol, na->rl); @@ -5338,6 +5502,59 @@ retry: return -1; } +#if PARTIAL_RUNLIST_UPDATING + /* + * For a file just been made sparse, we will have + * to reformat the first extent, so be sure the + * runlist is fully mapped and fully processed. + * Same if the file was sparse and is not any more. + * Note : not needed if the full runlist is to be processed + */ + if (!NAttrDelaySparsing(na) + && (!NAttrFullyMapped(na) || from_vcn) + && !(na->data_flags & ATTR_IS_COMPRESSED)) { + BOOL changed; + + if (!(na->data_flags & ATTR_IS_SPARSE)) { + int sparse; + runlist_element *xrl; + + /* + * If attribute was not sparse, we only + * have to check whether there is a hole + * in the updated region. + */ + xrl = na->rl; + if (xrl->lcn == LCN_RL_NOT_MAPPED) + xrl++; + sparse = ntfs_rl_sparse(xrl); + if (sparse < 0) { + ntfs_log_error("Could not check whether sparse\n"); + errno = EIO; + return (-1); + } + changed = sparse > 0; + } else { + /* + * If attribute was sparse, the compressed + * size has been maintained, and it gives + * and easy way to check whether the + * attribute is still sparse. + */ + changed = (((na->data_size - 1) + | (na->ni->vol->cluster_size - 1)) + 1) + == na->compressed_size; + } + if (changed) { + if (ntfs_attr_map_whole_runlist(na)) { + ntfs_log_error("Could not map whole for sparse change\n"); + errno = EIO; + return (-1); + } + from_vcn = 0; + } + } +#endif if (na->ni->nr_extents == -1) base_ni = na->ni->base_ni; else @@ -5534,6 +5751,34 @@ retry: goto put_err_out; } } + /* + * If the base extent was skipped in the above process, + * we still may have to update the sizes. + */ + if (!first_updated) { + le16 spcomp; + + ntfs_attr_reinit_search_ctx(ctx); + if (!ntfs_attr_lookup(na->type, na->name, na->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + a = ctx->attr; + a->allocated_size = cpu_to_sle64(na->allocated_size); + spcomp = na->data_flags + & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE); + if (spcomp) + a->compressed_size = cpu_to_sle64(na->compressed_size); + if ((na->type == AT_DATA) && (na->name == AT_UNNAMED)) { + na->ni->allocated_size + = (spcomp + ? na->compressed_size + : na->allocated_size); + NInoFileNameSetDirty(na->ni); + } + } else { + ntfs_log_error("Failed to update sizes in base extent\n"); + goto put_err_out; + } + } /* Deallocate not used attribute extents and return with success. */ if (finished_build) { @@ -5864,6 +6109,8 @@ static int ntfs_non_resident_attr_expand_i(ntfs_attr *na, const s64 newsize) return -1; } + if (na->type == AT_DATA) + NAttrSetDataAppending(na); /* Save for future use. */ org_alloc_size = na->allocated_size; /* The first cluster outside the new allocation. */ @@ -5874,10 +6121,26 @@ static int ntfs_non_resident_attr_expand_i(ntfs_attr *na, const s64 newsize) * clusters if there is a change. */ if ((na->allocated_size >> vol->cluster_size_bits) < first_free_vcn) { +#if PARTIAL_RUNLIST_UPDATING + s64 start_update; + + /* + * Update from the last previously allocated run, + * as we may have to expand an existing hole. + */ + start_update = na->allocated_size >> vol->cluster_size_bits; + if (start_update) + start_update--; + if (ntfs_attr_map_partial_runlist(na, start_update)) { + ntfs_log_perror("failed to map partial runlist"); + return -1; + } +#else if (ntfs_attr_map_whole_runlist(na)) { ntfs_log_perror("ntfs_attr_map_whole_runlist failed"); return -1; } +#endif /* * If we extend $DATA attribute on NTFS 3+ volume, we can add @@ -5953,8 +6216,11 @@ 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 (ntfs_attr_update_mapping_pairs(na, 0 /*na->allocated_size >> - vol->cluster_size_bits*/)) { +#if PARTIAL_RUNLIST_UPDATING + if (ntfs_attr_update_mapping_pairs(na, start_update)) { +#else + if (ntfs_attr_update_mapping_pairs(na, 0)) { +#endif err = errno; ntfs_log_perror("Mapping pairs update failed"); goto rollback; @@ -6285,6 +6551,89 @@ err_exit: return ret; } +/* + * Read some data from a data attribute + * + * Returns the amount of data read, negative if there was an error + */ + +int ntfs_attr_data_read(ntfs_inode *ni, + ntfschar *stream_name, int stream_name_len, + char *buf, size_t size, off_t offset) +{ + ntfs_attr *na = NULL; + int res, total = 0; + + na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len); + if (!na) { + res = -errno; + goto exit; + } + if ((size_t)offset < (size_t)na->data_size) { + if (offset + size > (size_t)na->data_size) + size = na->data_size - offset; + while (size) { + res = ntfs_attr_pread(na, offset, size, buf + total); + if ((off_t)res < (off_t)size) + ntfs_log_perror("ntfs_attr_pread partial read " + "(%lld : %lld <> %d)", + (long long)offset, + (long long)size, res); + if (res <= 0) { + res = -errno; + goto exit; + } + size -= res; + offset += res; + total += res; + } + } + res = total; +exit: + if (na) + ntfs_attr_close(na); + return res; +} + + +/* + * Write some data into a data attribute + * + * 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) +{ + ntfs_attr *na = NULL; + int res, total = 0; + + na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len); + if (!na) { + res = -errno; + goto exit; + } + while (size) { + res = ntfs_attr_pwrite(na, offset, size, buf + total); + if (res < (s64)size) + ntfs_log_perror("ntfs_attr_pwrite partial write (%lld: " + "%lld <> %d)", (long long)offset, + (long long)size, res); + if (res <= 0) { + res = -errno; + goto exit; + } + size -= res; + offset += res; + total += res; + } + res = total; +exit: + if (na) + ntfs_attr_close(na); + return res; +} int ntfs_attr_exist(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name, diff --git a/source/attrib.h b/source/attrib.h index 43ab7f5..47c4084 100644 --- a/source/attrib.h +++ b/source/attrib.h @@ -4,6 +4,7 @@ * Copyright (c) 2000-2004 Anton Altaparmakov * Copyright (c) 2004-2005 Yura Pakhuchiy * Copyright (c) 2006-2007 Szabolcs Szakacsits + * Copyright (c) 2010 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 @@ -201,6 +202,8 @@ typedef enum { NA_NonResident, /* 1: Attribute is not resident. */ NA_BeingNonResident, /* 1: Attribute is being made not resident. */ NA_FullyMapped, /* 1: Attribute has been fully mapped */ + NA_DataAppending, /* 1: Attribute is being appended to */ + NA_DelaySparsing, /* 1: Delay checking attribute being sparse */ NA_ComprClosing, /* 1: Compressed attribute is being closed */ } ntfs_attr_state_bits; @@ -224,6 +227,14 @@ typedef enum { #define NAttrSetFullyMapped(na) set_nattr_flag(na, FullyMapped) #define NAttrClearFullyMapped(na) clear_nattr_flag(na, FullyMapped) +#define NAttrDataAppending(na) test_nattr_flag(na, DataAppending) +#define NAttrSetDataAppending(na) set_nattr_flag(na, DataAppending) +#define NAttrClearDataAppending(na) clear_nattr_flag(na, DataAppending) + +#define NAttrDelaySparsing(na) test_nattr_flag(na, DelaySparsing) +#define NAttrSetDelaySparsing(na) set_nattr_flag(na, DelaySparsing) +#define NAttrClearDelaySparsing(na) clear_nattr_flag(na, DelaySparsing) + #define NAttrComprClosing(na) test_nattr_flag(na, ComprClosing) #define NAttrSetComprClosing(na) set_nattr_flag(na, ComprClosing) #define NAttrClearComprClosing(na) clear_nattr_flag(na, ComprClosing) @@ -370,6 +381,12 @@ extern int ntfs_attr_exist(ntfs_inode *ni, const ATTR_TYPES type, extern int ntfs_attr_remove(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name, u32 name_len); extern s64 ntfs_attr_get_free_bits(ntfs_attr *na); +extern int ntfs_attr_data_read(ntfs_inode *ni, + ntfschar *stream_name, int stream_name_len, + 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); #endif /* defined _NTFS_ATTRIB_H */ diff --git a/source/cache.c b/source/cache.c index dd14767..43ff151 100644 --- a/source/cache.c +++ b/source/cache.c @@ -1,7 +1,7 @@ /** * cache.c : deal with LRU caches * - * Copyright (c) 2008-2009 Jean-Pierre Andre + * Copyright (c) 2008-2010 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 @@ -298,7 +298,7 @@ struct CACHED_GENERIC *ntfs_enter_cache(struct CACHE_HEADER *cache, if (cache->most_recent_entry) cache->most_recent_entry->previous = current; cache->most_recent_entry = current; - memcpy(current->fixed, item->fixed, cache->fixed_size); + memcpy(current->payload, item->payload, cache->fixed_size); if (item->varsize) { if (current->variable) { memcpy(current->variable, diff --git a/source/cache.h b/source/cache.h index 67e4f9d..be63b1a 100644 --- a/source/cache.h +++ b/source/cache.h @@ -1,7 +1,7 @@ /* * cache.h : deal with indexed LRU caches * - * Copyright (c) 2008-2009 Jean-Pierre Andre + * Copyright (c) 2008-2010 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 @@ -29,11 +29,7 @@ struct CACHED_GENERIC { struct CACHED_GENERIC *previous; void *variable; size_t varsize; - union { - /* force alignment for pointers and u64 */ - u64 u64align; - void *ptralign; - } fixed[0]; + union ALIGNMENT payload[0]; } ; struct CACHED_INODE { @@ -41,6 +37,7 @@ struct CACHED_INODE { struct CACHED_INODE *previous; const char *pathname; size_t varsize; + union ALIGNMENT payload[0]; /* above fields must match "struct CACHED_GENERIC" */ u64 inum; } ; @@ -50,6 +47,7 @@ struct CACHED_NIDATA { struct CACHED_NIDATA *previous; const char *pathname; /* not used */ size_t varsize; /* not used */ + union ALIGNMENT payload[0]; /* above fields must match "struct CACHED_GENERIC" */ u64 inum; ntfs_inode *ni; @@ -60,6 +58,7 @@ struct CACHED_LOOKUP { struct CACHED_LOOKUP *previous; const char *name; size_t namesize; + union ALIGNMENT payload[0]; /* above fields must match "struct CACHED_GENERIC" */ u64 parent; u64 inum; diff --git a/source/compress.c b/source/compress.c index fbd30ba..e257534 100644 --- a/source/compress.c +++ b/source/compress.c @@ -62,6 +62,10 @@ #include "logging.h" #include "misc.h" +#undef le16_to_cpup +/* the standard le16_to_cpup() crashes for unaligned data on some processors */ +#define le16_to_cpup(p) (*(u8*)(p) + (((u8*)(p))[1] << 8)) + /** * enum ntfs_compression_constants - constants used in the compression code */ diff --git a/source/config.h b/source/config.h index ba0168e..fcf89f0 100644 --- a/source/config.h +++ b/source/config.h @@ -298,13 +298,13 @@ #define PACKAGE_NAME "ntfs-3g" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "ntfs-3g 2010.8.8" +#define PACKAGE_STRING "ntfs-3g 2011.1.15" /* 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 "2010.8.8" +#define PACKAGE_VERSION "2011.1.15" /* POSIX ACL support */ #undef POSIXACLS @@ -347,6 +347,9 @@ first (like Intel and VAX, unlike Motorola and SPARC). */ #undef WORDS_LITTLEENDIAN +/* system extended attributes mappings */ +#undef XATTR_MAPPINGS + /* Number of bits in a file offset, on hosts where this is settable. */ #define _FILE_OFFSET_BITS 64 diff --git a/source/device.c b/source/device.c index c77d8f9..db84010 100644 --- a/source/device.c +++ b/source/device.c @@ -3,6 +3,7 @@ * * Copyright (c) 2004-2006 Anton Altaparmakov * Copyright (c) 2004-2006 Szabolcs Szakacsits + * Copyright (c) 2010 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 @@ -154,6 +155,25 @@ int ntfs_device_free(struct ntfs_device *dev) return 0; } +/* + * Sync the device + * + * returns zero if successful. + */ + +int ntfs_device_sync(struct ntfs_device *dev) +{ + int ret; + struct ntfs_device_operations *dops; + + if (NDevDirty(dev)) { + dops = dev->d_ops; + ret = dops->sync(dev); + } else + ret = 0; + return ret; +} + /** * ntfs_pread - positioned read from disk * @dev: device to read from @@ -260,6 +280,9 @@ s64 ntfs_pwrite(struct ntfs_device *dev, const s64 pos, s64 count, total = written; break; } + if (NDevSync(dev) && total && dops->sync(dev)) { + total--; /* on sync error, return partially written */ + } ret = total; out: return ret; diff --git a/source/device.h b/source/device.h index a19d29c..ad34ac5 100644 --- a/source/device.h +++ b/source/device.h @@ -41,6 +41,7 @@ typedef enum { ND_ReadOnly, /* 1: Device is read-only. */ ND_Dirty, /* 1: Device is dirty, needs sync. */ ND_Block, /* 1: Device is a block device. */ + ND_Sync, /* 1: Device is mounted with "-o sync" */ } ntfs_device_state_bits; #define test_ndev_flag(nd, flag) test_bit(ND_##flag, (nd)->d_state) @@ -63,6 +64,10 @@ typedef enum { #define NDevSetBlock(nd) set_ndev_flag(nd, Block) #define NDevClearBlock(nd) clear_ndev_flag(nd, Block) +#define NDevSync(nd) test_ndev_flag(nd, Sync) +#define NDevSetSync(nd) set_ndev_flag(nd, Sync) +#define NDevClearSync(nd) clear_ndev_flag(nd, Sync) + /** * struct ntfs_device - * @@ -102,6 +107,7 @@ struct ntfs_device_operations { extern struct ntfs_device *ntfs_device_alloc(const char *name, const long state, struct ntfs_device_operations *dops, void *priv_data); extern int ntfs_device_free(struct ntfs_device *dev); +extern int ntfs_device_sync(struct ntfs_device *dev); extern s64 ntfs_pread(struct ntfs_device *dev, const s64 pos, s64 count, void *b); diff --git a/source/dir.c b/source/dir.c index 2372f3f..198bc29 100644 --- a/source/dir.c +++ b/source/dir.c @@ -356,7 +356,7 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, ntfs_attr_put_search_ctx(ctx); if (mref) return mref; - ntfs_log_debug("Entry not found.\n"); + ntfs_log_debug("Entry not found - between root entries.\n"); errno = ENOENT; return -1; } /* Child node present, descend into it. */ @@ -1812,7 +1812,7 @@ search: while (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE, 0, NULL, 0, actx)) { char *s; - BOOL case_sensitive = IGNORE_CASE; + IGNORE_CASE_BOOL case_sensitive = IGNORE_CASE; errno = 0; fn = (FILE_NAME_ATTR*)((u8*)actx->attr + @@ -1844,8 +1844,9 @@ search: (long long unsigned)MREF_LE(fn->parent_directory)); continue; } - - if (fn->file_name_type == FILE_NAME_POSIX || case_sensitive_match) + if (case_sensitive_match + || ((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, diff --git a/source/inode.c b/source/inode.c index 6f3fa06..c51e523 100644 --- a/source/inode.c +++ b/source/inode.c @@ -481,6 +481,9 @@ ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref) } else { ni = ntfs_inode_real_open(vol, mref); } + if (!ni) { + debug_double_inode(item.inum, 0); + } #else ni = ntfs_inode_real_open(vol, mref); #endif @@ -842,6 +845,12 @@ static int ntfs_inode_sync_file_name(ntfs_inode *ni, ntfs_inode *dir_ni) else { fnx->allocated_size = cpu_to_sle64(ni->allocated_size); fnx->data_size = cpu_to_sle64(ni->data_size); + /* + * The file name record has also to be fixed if some + * attribute update implied the unnamed data to be + * made non-resident + */ + fn->allocated_size = fnx->allocated_size; } /* update or clear the reparse tag in the index */ fnx->reparse_point_tag = reparse_tag; diff --git a/source/logging.c b/source/logging.c index 385bcaa..ccccbb8 100644 --- a/source/logging.c +++ b/source/logging.c @@ -3,6 +3,7 @@ * * Copyright (c) 2005 Richard Russon * Copyright (c) 2005-2008 Szabolcs Szakacsits + * Copyright (c) 2010 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 @@ -388,6 +389,29 @@ out: } #endif +/* + * Early logging before the logs are redirected + * + * (not quite satisfactory : this appears before the ntfs-g banner, + * and with a different pid) + */ + +void ntfs_log_early_error(const char *format, ...) +{ + va_list args; + + va_start(args, format); +#ifdef HAVE_SYSLOG_H + openlog("ntfs-3g", LOG_PID, LOG_USER); + ntfs_log_handler_syslog(NULL, NULL, 0, + NTFS_LOG_LEVEL_ERROR, NULL, + format, args); +#else + vfprintf(stderr,format,args); +#endif + va_end(args); +} + /** * ntfs_log_handler_fprintf - Basic logging handler * @function: Function in which the log line occurred diff --git a/source/logging.h b/source/logging.h index 401f5c9..82f39fe 100644 --- a/source/logging.h +++ b/source/logging.h @@ -114,5 +114,8 @@ int ntfs_log_redirect(const char *function, const char *file, int line, #define ntfs_log_leave(FORMAT, ARGS...)do {} while (0) #endif /* DEBUG */ +void ntfs_log_early_error(const char *format, ...) + __attribute__((format(printf, 1, 2))); + #endif /* _LOGGING_H_ */ diff --git a/source/object_id.c b/source/object_id.c index 555dd13..8799ddb 100644 --- a/source/object_id.c +++ b/source/object_id.c @@ -107,7 +107,11 @@ */ typedef struct { - GUID object_id; + union { + /* alignment may be needed to evaluate collations */ + u32 alignment; + GUID guid; + } object_id; } OBJECT_ID_INDEX_KEY; typedef struct { diff --git a/source/param.h b/source/param.h index f21bd65..57d122e 100644 --- a/source/param.h +++ b/source/param.h @@ -1,7 +1,7 @@ /* * param.h - Parameter values for ntfs-3g * - * Copyright (c) 2009 Jean-Pierre Andre + * Copyright (c) 2009-2010 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 @@ -50,6 +50,20 @@ enum { /* maximum cluster size for allowing compression for new files */ #define MAX_COMPRESSION_CLUSTER_SIZE 4096 +/* + * Parameters for runlists + */ + + /* only update the final extent of a runlist when appending data */ +#define PARTIAL_RUNLIST_UPDATING 1 + +/* + * Parameters for user and xattr mappings + */ + +#define XATTRMAPPINGFILE ".NTFS-3G/XattrMapping" /* default mapping file */ + + /* * Permission checking modes for high level and low level * @@ -58,7 +72,8 @@ enum { * * Stick to the recommended values unless you understand the consequences * on protection and performances. Use of cacheing is good for - * performances, but bad on security. + * performances, but bad on security with internal fuse or external + * fuse older than 2.8 * * Possible values for high level : * 1 : no cache, kernel control (recommended) @@ -67,7 +82,7 @@ enum { * * Possible values for low level : * 2 : no cache, kernel control - * 3 : use kernel/fuse cache, kernel control + * 3 : use kernel/fuse cache, kernel control (external fuse >= 2.8) * 5 : no cache, file system control (recommended) * 8 : no cache, kernel control for ACLs * @@ -77,6 +92,10 @@ enum { */ #define HPERMSCONFIG 1 +#if defined(FUSE_INTERNAL) || !defined(FUSE_VERSION) || (FUSE_VERSION < 28) #define LPERMSCONFIG 5 +#else +#define LPERMSCONFIG 3 +#endif #endif /* defined _NTFS_PARAM_H */ diff --git a/source/runlist.c b/source/runlist.c index cea2467..383a80b 100644 --- a/source/runlist.c +++ b/source/runlist.c @@ -5,7 +5,7 @@ * Copyright (c) 2002-2005 Richard Russon * Copyright (c) 2002-2008 Szabolcs Szakacsits * Copyright (c) 2004 Yura Pakhuchiy - * Copyright (c) 2007-2009 Jean-Pierre Andre + * Copyright (c) 2007-2010 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 @@ -1418,28 +1418,18 @@ int ntfs_write_significant_bytes(u8 *dst, const u8 *dst_max, const s64 n) { s64 l = n; int i; - s8 j; i = 0; - do { + if (dst > dst_max) + goto err_out; + *dst++ = l; + i++; + while ((l > 0x7f) || (l < -0x80)) { if (dst > dst_max) goto err_out; - *dst++ = l & 0xffLL; l >>= 8; + *dst++ = l; i++; - } while (l != 0LL && l != -1LL); - j = (n >> 8 * (i - 1)) & 0xff; - /* If the sign bit is wrong, we need an extra byte. */ - if (n < 0LL && j >= 0) { - if (dst > dst_max) - goto err_out; - i++; - *dst = (u8)-1; - } else if (n > 0LL && j < 0) { - if (dst > dst_max) - goto err_out; - i++; - *dst = 0; } return i; err_out: diff --git a/source/security.c b/source/security.c index 138764b..0203ecc 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-2009 Jean-Pierre Andre + * Copyright (c) 2007-2010 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 @@ -404,91 +404,6 @@ le32 ntfs_security_hash(const SECURITY_DESCRIPTOR_RELATIVE *sd, const u32 len) return cpu_to_le32(hash); } -/* - * Internal read - * copied and pasted from ntfs_fuse_read() and made independent - * of fuse context - */ - -static int ntfs_local_read(ntfs_inode *ni, - ntfschar *stream_name, int stream_name_len, - char *buf, size_t size, off_t offset) -{ - ntfs_attr *na = NULL; - int res, total = 0; - - na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len); - if (!na) { - res = -errno; - goto exit; - } - if ((size_t)offset < (size_t)na->data_size) { - if (offset + size > (size_t)na->data_size) - size = na->data_size - offset; - while (size) { - res = ntfs_attr_pread(na, offset, size, buf); - if ((off_t)res < (off_t)size) - ntfs_log_perror("ntfs_attr_pread partial read " - "(%lld : %lld <> %d)", - (long long)offset, - (long long)size, res); - if (res <= 0) { - res = -errno; - goto exit; - } - size -= res; - offset += res; - total += res; - } - } - res = total; -exit: - if (na) - ntfs_attr_close(na); - return res; -} - - -/* - * Internal write - * copied and pasted from ntfs_fuse_write() and made independent - * of fuse context - */ - -static int ntfs_local_write(ntfs_inode *ni, - ntfschar *stream_name, int stream_name_len, - char *buf, size_t size, off_t offset) -{ - ntfs_attr *na = NULL; - int res, total = 0; - - na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len); - if (!na) { - res = -errno; - goto exit; - } - while (size) { - res = ntfs_attr_pwrite(na, offset, size, buf); - if (res < (s64)size) - ntfs_log_perror("ntfs_attr_pwrite partial write (%lld: " - "%lld <> %d)", (long long)offset, - (long long)size, res); - if (res <= 0) { - res = -errno; - goto exit; - } - size -= res; - offset += res; - total += res; - } - res = total; -exit: - if (na) - ntfs_attr_close(na); - return res; -} - - /* * Get the first entry of current index block * cut and pasted form ntfs_ie_get_first() in index.c @@ -531,7 +446,7 @@ static int entersecurity_stuff(ntfs_volume *vol, off_t offs) if (stuff) { memset(stuff, 0, STUFFSZ); do { - written = ntfs_local_write(vol->secure_ni, + written = ntfs_attr_data_write(vol->secure_ni, STREAM_SDS, 4, stuff, STUFFSZ, offs); if (written == STUFFSZ) { total += STUFFSZ; @@ -589,10 +504,10 @@ static int entersecurity_data(ntfs_volume *vol, phsds->security_id = keyid; phsds->offset = cpu_to_le64(offs); phsds->length = cpu_to_le32(fullsz - gap); - written1 = ntfs_local_write(vol->secure_ni, + written1 = ntfs_attr_data_write(vol->secure_ni, STREAM_SDS, 4, fullattr, fullsz, offs - gap); - written2 = ntfs_local_write(vol->secure_ni, + written2 = ntfs_attr_data_write(vol->secure_ni, STREAM_SDS, 4, fullattr, fullsz, offs - gap + ALIGN_SDS_BLOCK); if ((written1 == fullsz) @@ -950,7 +865,7 @@ static le32 setsecurityattr(ntfs_volume *vol, + sizeof(SECURITY_DESCRIPTOR_HEADER); oldattr = (char*)ntfs_malloc(size); if (oldattr) { - rdsize = ntfs_local_read( + rdsize = ntfs_attr_data_read( vol->secure_ni, STREAM_SDS, 4, oldattr, size, offs); @@ -1772,7 +1687,7 @@ static char *retrievesecurityattr(ntfs_volume *vol, SII_INDEX_KEY id) securattr = (char*)ntfs_malloc(size); if (securattr) { - rdsize = ntfs_local_read( + rdsize = ntfs_attr_data_read( ni, STREAM_SDS, 4, securattr, size, offs); if ((rdsize != size) @@ -4097,7 +4012,7 @@ static int basicread(void *fileid, char *buf, size_t size, off_t offs __attribut static int localread(void *fileid, char *buf, size_t size, off_t offs) { - return (ntfs_local_read((ntfs_inode*)fileid, + return (ntfs_attr_data_read((ntfs_inode*)fileid, AT_UNNAMED, 0, buf, size, offs)); } @@ -4919,7 +4834,7 @@ int ntfs_read_sds(struct SECURITY_API *scapi, got = -1; /* default return */ if (scapi && (scapi->magic == MAGIC_API)) { if (scapi->security.vol->secure_ni) - got = ntfs_local_read(scapi->security.vol->secure_ni, + got = ntfs_attr_data_read(scapi->security.vol->secure_ni, STREAM_SDS, 4, buf, size, offset); else errno = EOPNOTSUPP; diff --git a/source/security.h b/source/security.h index 83d7c0e..8612dc2 100644 --- a/source/security.h +++ b/source/security.h @@ -4,7 +4,7 @@ * * Copyright (c) 2004 Anton Altaparmakov * Copyright (c) 2005-2006 Szabolcs Szakacsits - * Copyright (c) 2007-2008 Jean-Pierre Andre + * Copyright (c) 2007-2010 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 @@ -85,6 +85,7 @@ struct CACHED_PERMISSIONS_LEGACY { struct CACHED_PERMISSIONS_LEGACY *previous; void *variable; size_t varsize; + union ALIGNMENT payload[0]; /* above fields must match "struct CACHED_GENERIC" */ u64 mft_no; struct CACHED_PERMISSIONS perm; @@ -99,6 +100,7 @@ struct CACHED_SECURID { struct CACHED_SECURID *previous; void *variable; size_t varsize; + union ALIGNMENT payload[0]; /* above fields must match "struct CACHED_GENERIC" */ uid_t uid; gid_t gid; @@ -181,6 +183,7 @@ struct POSIX_SECURITY { int defcnt; int firstdef; u16 tagsset; + s32 alignment[0]; struct POSIX_ACL acl; } ; diff --git a/source/types.h b/source/types.h index 3fafe8a..77758ea 100644 --- a/source/types.h +++ b/source/types.h @@ -129,5 +129,13 @@ typedef enum { #define STATUS_KEEP_SEARCHING (-3) #define STATUS_NOT_FOUND (-4) +/* + * Force alignment in a struct if required by processor + */ +union ALIGNMENT { + u64 u64align; + void *ptralign; +} ; + #endif /* defined _NTFS_TYPES_H */ diff --git a/source/unistr.c b/source/unistr.c index eb72f0f..180f61a 100644 --- a/source/unistr.c +++ b/source/unistr.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2004 Anton Altaparmakov * Copyright (c) 2002-2009 Szabolcs Szakacsits - * Copyright (c) 2008-2009 Jean-Pierre Andre + * Copyright (c) 2008-2010 Jean-Pierre Andre * Copyright (c) 2008 Bernhard Kaindl * * This program/include file is free software; you can redistribute it and/or @@ -166,24 +166,22 @@ int ntfs_names_full_collate(const ntfschar *name1, const u32 name1_len, cnt = min(name1_len, name2_len); if (cnt > 0) { if (ic == CASE_SENSITIVE) { - do { - c1 = le16_to_cpu(*name1); + while (--cnt && (*name1 == *name2)) { name1++; - c2 = le16_to_cpu(*name2); name2++; - } while (--cnt && (c1 == c2)); - u1 = c1; - u2 = c2; + } + u1 = c1 = le16_to_cpu(*name1); + u2 = c2 = le16_to_cpu(*name2); if (u1 < upcase_len) u1 = le16_to_cpu(upcase[u1]); if (u2 < upcase_len) u2 = le16_to_cpu(upcase[u2]); if ((u1 == u2) && cnt) do { - u1 = le16_to_cpu(*name1); name1++; - u2 = le16_to_cpu(*name2); + u1 = le16_to_cpu(*name1); name2++; + u2 = le16_to_cpu(*name2); if (u1 < upcase_len) u1 = le16_to_cpu(upcase[u1]); if (u2 < upcase_len) diff --git a/source/volume.c b/source/volume.c index 629ec92..a30a33a 100644 --- a/source/volume.c +++ b/source/volume.c @@ -868,6 +868,7 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) VOLUME_INFORMATION *vinf; ntfschar *vname; int i, j, eo; + unsigned int k; u32 u; vol = ntfs_volume_startup(dev, flags); @@ -1025,6 +1026,17 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) ntfs_log_perror("Failed to close $UpCase"); goto error_exit; } + /* Consistency check of $UpCase, restricted to plain ASCII chars */ + k = 0x20; + while ((k < vol->upcase_len) + && (k < 0x7f) + && (le16_to_cpu(vol->upcase[k]) + == ((k < 'a') || (k > 'z') ? k : k + 'A' - 'a'))) + k++; + if (k < 0x7f) { + ntfs_log_error("Corrupted file $UpCase\n"); + goto io_error_exit; + } /* * Now load $Volume and set the version information and flags in the diff --git a/source/volume.h b/source/volume.h index 79193c5..9c8ab14 100644 --- a/source/volume.h +++ b/source/volume.h @@ -5,6 +5,7 @@ * Copyright (c) 2004-2005 Richard Russon * Copyright (c) 2005-2006 Yura Pakhuchiy * Copyright (c) 2005-2009 Szabolcs Szakacsits + * Copyright (c) 2010 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 @@ -251,7 +252,9 @@ struct _ntfs_volume { s64 free_mft_records; /* Same for free mft records (see above) */ BOOL efs_raw; /* volume is mounted for raw access to efs-encrypted files */ - +#ifdef XATTR_MAPPINGS + struct XATTRMAPPING *xattr_mapping; +#endif /* XATTR_MAPPINGS */ #if CACHE_INODE_SIZE struct CACHE_HEADER *xinode_cache; #endif diff --git a/source/xattrs.h b/source/xattrs.h new file mode 100644 index 0000000..7ebd298 --- /dev/null +++ b/source/xattrs.h @@ -0,0 +1,77 @@ +/* + * xattrs.h : definitions related to system extended attributes + * + * Copyright (c) 2010 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 + */ + +#ifndef _NTFS_XATTR_H_ +#define _NTFS_XATTR_H_ + +/* + * Identification of data mapped to the system name space + */ + +enum SYSTEMXATTRS { + XATTR_UNMAPPED, + XATTR_NTFS_ACL, + XATTR_NTFS_ATTRIB, + XATTR_NTFS_ATTRIB_BE, + XATTR_NTFS_EFSINFO, + XATTR_NTFS_REPARSE_DATA, + XATTR_NTFS_OBJECT_ID, + XATTR_NTFS_DOS_NAME, + XATTR_NTFS_TIMES, + XATTR_NTFS_TIMES_BE, + XATTR_NTFS_CRTIME, + XATTR_NTFS_CRTIME_BE, + XATTR_POSIX_ACC, + XATTR_POSIX_DEF +} ; + +struct XATTRMAPPING { + struct XATTRMAPPING *next; + enum SYSTEMXATTRS xattr; + char name[1]; /* variable length */ +} ; + +#ifdef XATTR_MAPPINGS + +struct XATTRMAPPING *ntfs_xattr_build_mapping(ntfs_volume *vol, + const char *path); +void ntfs_xattr_free_mapping(struct XATTRMAPPING*); + +#endif /* XATTR_MAPPINGS */ + +enum SYSTEMXATTRS ntfs_xattr_system_type(const char *name, + ntfs_volume *vol); +int ntfs_xattr_listxattr(ntfs_inode *ni, ntfs_attr_search_ctx *actx, + char *list, size_t size, BOOL prefixing); + +int ntfs_xattr_system_getxattr(struct SECURITY_CONTEXT *scx, + enum SYSTEMXATTRS attr, + ntfs_inode *ni, ntfs_inode *dir_ni, + char *value, size_t size); +int ntfs_xattr_system_setxattr(struct SECURITY_CONTEXT *scx, + enum SYSTEMXATTRS attr, + ntfs_inode *ni, ntfs_inode *dir_ni, + const char *value, size_t size, int flags); +int ntfs_xattr_system_removexattr(struct SECURITY_CONTEXT *scx, + enum SYSTEMXATTRS attr, + ntfs_inode *ni, ntfs_inode *dir_ni); + +#endif /* _NTFS_XATTR_H_ */