diff --git a/source/attrib.c b/source/attrib.c index 680eff3..e6f614f 100644 --- a/source/attrib.c +++ b/source/attrib.c @@ -5,7 +5,7 @@ * Copyright (c) 2002-2005 Richard Russon * Copyright (c) 2002-2008 Szabolcs Szakacsits * Copyright (c) 2004-2007 Yura Pakhuchiy - * Copyright (c) 2007-2010 Jean-Pierre Andre + * Copyright (c) 2007-2011 Jean-Pierre Andre * Copyright (c) 2010 Erik Larsson * * This program/include file is free software; you can redistribute it and/or @@ -603,6 +603,7 @@ static int ntfs_attr_map_partial_runlist(ntfs_attr *na, VCN vcn) VCN last_vcn; VCN highest_vcn; VCN needed; + VCN existing_vcn; runlist_element *rl; ATTR_RECORD *a; BOOL startseen; @@ -612,6 +613,8 @@ static int ntfs_attr_map_partial_runlist(ntfs_attr *na, VCN vcn) if (lcn >= 0 || lcn == LCN_HOLE || lcn == LCN_ENOENT) return 0; + existing_vcn = (na->rl ? na->rl->vcn : -1); + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); if (!ctx) return -1; @@ -643,6 +646,11 @@ static int ntfs_attr_map_partial_runlist(ntfs_attr *na, VCN vcn) needed = highest_vcn + 1; if (!a->lowest_vcn) startseen = TRUE; + /* reaching a previously allocated part ? */ + if ((existing_vcn >= 0) + && (needed >= existing_vcn)) { + needed = last_vcn; + } } } else rl = (runlist_element*)NULL; @@ -1182,12 +1190,27 @@ static int ntfs_attr_fill_zero(ntfs_attr *na, s64 pos, s64 count) rli++; } size = min(end - pos, NTFS_BUF_SIZE); - written = ntfs_rl_pwrite(vol, rli, ofsi, pos, size, buf); - if (written <= 0) { - ntfs_log_perror("Failed to zero space"); - goto err_free; + /* + * If the zeroed block is fully within a hole, + * we need not write anything, so advance as far + * as possible within the hole. + */ + if ((rli->lcn == (LCN)LCN_HOLE) + && (ofsi <= pos) + && (ofsi + (rli->length << vol->cluster_size_bits) + >= (pos + size))) { + size = min(end - pos, ofsi - pos + + (rli->length << vol->cluster_size_bits)); + pos += size; + } else { + written = ntfs_rl_pwrite(vol, rli, ofsi, pos, + size, buf); + if (written <= 0) { + ntfs_log_perror("Failed to zero space"); + goto err_free; + } + pos += written; } - pos += written; } ret = 0; @@ -1711,6 +1734,9 @@ static int borrow_from_hole(ntfs_attr *na, runlist_element **prl, return (compressed_part); } +static int ntfs_attr_truncate_i(ntfs_attr *na, const s64 newsize, + hole_type holes); + /** * ntfs_attr_pwrite - positioned write to an ntfs attribute * @na: ntfs attribute to write to @@ -1815,20 +1841,20 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) * 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... + * we avoid sparse testing until the data is filled in. */ - if (NAttrDataAppending(na)) - NAttrSetDelaySparsing(na); -#endif - if (ntfs_attr_truncate(na, pos + count)) { - NAttrClearDelaySparsing(na); + if (ntfs_attr_truncate_i(na, pos + count, + (NAttrDataAppending(na) ? + HOLES_DELAY : HOLES_OK))) { ntfs_log_perror("Failed to enlarge attribute"); goto errno_set; } - NAttrClearDelaySparsing(na); +#else + if (ntfs_attr_truncate(na, pos + count)) { + ntfs_log_perror("Failed to enlarge attribute"); + goto errno_set; + } +#endif /* resizing may change the compression mode */ compressed = (na->data_flags & ATTR_COMPRESSION_MASK) != const_cpu_to_le16(0); @@ -5348,7 +5374,7 @@ static int ntfs_attr_make_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx) * update allocated and compressed size. */ static int ntfs_attr_update_meta(ATTR_RECORD *a, ntfs_attr *na, MFT_RECORD *m, - ntfs_attr_search_ctx *ctx) + hole_type holes, ntfs_attr_search_ctx *ctx) { int sparse, ret = 0; @@ -5361,7 +5387,7 @@ 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, unless this is an intermediate state */ - if (NAttrDelaySparsing(na)) + if (holes == HOLES_DELAY) sparse = (a->flags & ATTR_IS_SPARSE) != const_cpu_to_le16(0); else { sparse = ntfs_rl_sparse(na->rl); @@ -5372,7 +5398,7 @@ static int ntfs_attr_update_meta(ATTR_RECORD *a, ntfs_attr *na, MFT_RECORD *m, } /* Check whether attribute becomes sparse, unless check is delayed. */ - if (!NAttrDelaySparsing(na) + if ((holes != HOLES_DELAY) && sparse && !(a->flags & (ATTR_IS_SPARSE | ATTR_IS_COMPRESSED))) { /* @@ -5474,7 +5500,8 @@ error: ret = -3; goto out; /** * ntfs_attr_update_mapping_pairs_i - see ntfs_attr_update_mapping_pairs */ -static int ntfs_attr_update_mapping_pairs_i(ntfs_attr *na, VCN from_vcn) +static int ntfs_attr_update_mapping_pairs_i(ntfs_attr *na, VCN from_vcn, + hole_type holes) { ntfs_attr_search_ctx *ctx; ntfs_inode *ni, *base_ni; @@ -5510,7 +5537,7 @@ retry: * 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) + if ((holes != HOLES_DELAY) && (!NAttrFullyMapped(na) || from_vcn) && !(na->data_flags & ATTR_IS_COMPRESSED)) { BOOL changed; @@ -5616,7 +5643,7 @@ retry: continue; } - switch (ntfs_attr_update_meta(a, na, m, ctx)) { + switch (ntfs_attr_update_meta(a, na, m, holes, ctx)) { case -1: return -1; case -2: goto retry; case -3: goto put_err_out; @@ -5751,34 +5778,6 @@ 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) { @@ -5912,7 +5911,7 @@ int ntfs_attr_update_mapping_pairs(ntfs_attr *na, VCN from_vcn) int ret; ntfs_log_enter("Entering\n"); - ret = ntfs_attr_update_mapping_pairs_i(na, from_vcn); + ret = ntfs_attr_update_mapping_pairs_i(na, from_vcn, HOLES_OK); ntfs_log_leave("\n"); return ret; } @@ -6082,7 +6081,8 @@ put_err_out: * ERANGE - @newsize is not valid for the attribute type of @na. * ENOSPC - There is no enough space in base mft to resize $ATTRIBUTE_LIST. */ -static int ntfs_non_resident_attr_expand_i(ntfs_attr *na, const s64 newsize) +static int ntfs_non_resident_attr_expand_i(ntfs_attr *na, const s64 newsize, + hole_type holes) { LCN lcn_seek_from; VCN first_free_vcn; @@ -6146,7 +6146,8 @@ static int ntfs_non_resident_attr_expand_i(ntfs_attr *na, const s64 newsize) * If we extend $DATA attribute on NTFS 3+ volume, we can add * sparse runs instead of real allocation of clusters. */ - if (na->type == AT_DATA && vol->major_ver >= 3) { + if ((na->type == AT_DATA) && (vol->major_ver >= 3) + && (holes != HOLES_NO)) { rl = ntfs_malloc(0x1000); if (!rl) return -1; @@ -6217,7 +6218,7 @@ static int ntfs_non_resident_attr_expand_i(ntfs_attr *na, const s64 newsize) 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(na, start_update)) { + if (ntfs_attr_update_mapping_pairs_i(na, start_update, holes)) { #else if (ntfs_attr_update_mapping_pairs(na, 0)) { #endif @@ -6306,12 +6307,13 @@ put_err_out: } -static int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize) +static int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize, + hole_type holes) { int ret; ntfs_log_enter("Entering\n"); - ret = ntfs_non_resident_attr_expand_i(na, newsize); + ret = ntfs_non_resident_attr_expand_i(na, newsize, holes); ntfs_log_leave("\n"); return ret; } @@ -6320,6 +6322,7 @@ static int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize) * ntfs_attr_truncate - resize an ntfs attribute * @na: open ntfs attribute to resize * @newsize: new size (in bytes) to which to resize the attribute + * @holes: how to create a hole if expanding * * Change the size of an open ntfs attribute @na to @newsize bytes. If the * attribute is made bigger and the attribute is resident the newly @@ -6336,7 +6339,8 @@ static int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize) * EOPNOTSUPP - The desired resize is not implemented yet. * EACCES - Encrypted attribute. */ -int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize) +static int ntfs_attr_truncate_i(ntfs_attr *na, const s64 newsize, + hole_type holes) { int ret = STATUS_ERROR; s64 fullsize; @@ -6401,7 +6405,8 @@ int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize) else fullsize = newsize; if (fullsize > na->data_size) - ret = ntfs_non_resident_attr_expand(na, fullsize); + ret = ntfs_non_resident_attr_expand(na, fullsize, + holes); else ret = ntfs_non_resident_attr_shrink(na, fullsize); } else @@ -6410,7 +6415,25 @@ out: ntfs_log_leave("Return status %d\n", ret); return ret; } - + +/* + * Resize an attribute, creating a hole if relevant + */ + +int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize) +{ + return (ntfs_attr_truncate_i(na, newsize, HOLES_OK)); +} + +/* + * Resize an attribute, avoiding hole creation + */ + +int ntfs_attr_truncate_solid(ntfs_attr *na, const s64 newsize) +{ + return (ntfs_attr_truncate_i(na, newsize, HOLES_NO)); +} + /* * Stuff a hole in a compressed file * @@ -6471,7 +6494,7 @@ static int stuff_hole(ntfs_attr *na, const s64 pos) if (!ret && ((na->initialized_size + end_size) < pos) && ntfs_non_resident_attr_expand(na, - pos - end_size)) + pos - end_size, HOLES_OK)) ret = -1; else na->initialized_size diff --git a/source/attrib.h b/source/attrib.h index 47c4084..67fb957 100644 --- a/source/attrib.h +++ b/source/attrib.h @@ -58,6 +58,12 @@ typedef enum { LCN_EIO = -5, } ntfs_lcn_special_values; +typedef enum { /* ways of processing holes when expanding */ + HOLES_NO, + HOLES_OK, + HOLES_DELAY +} hole_type; + /** * struct ntfs_attr_search_ctx - search context used in attribute search functions * @mrec: buffer containing mft record to search @@ -203,7 +209,6 @@ typedef enum { 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; @@ -231,10 +236,6 @@ typedef enum { #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) @@ -343,6 +344,7 @@ extern int ntfs_attr_record_move_away(ntfs_attr_search_ctx *ctx, int extra); extern int ntfs_attr_update_mapping_pairs(ntfs_attr *na, VCN from_vcn); extern int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize); +extern int ntfs_attr_truncate_solid(ntfs_attr *na, const s64 newsize); /** * get_attribute_value_length - return the length of the value of an attribute diff --git a/source/compress.c b/source/compress.c index e257534..fe6bb66 100644 --- a/source/compress.c +++ b/source/compress.c @@ -5,7 +5,7 @@ * Copyright (c) 2004-2005 Anton Altaparmakov * Copyright (c) 2004-2006 Szabolcs Szakacsits * Copyright (c) 2005 Yura Pakhuchiy - * Copyright (c) 2009-2010 Jean-Pierre Andre + * Copyright (c) 2009-2011 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 @@ -1251,6 +1251,7 @@ static int ntfs_compress_overwr_free(ntfs_attr *na, runlist_element *rl, case 1 : /* there is a single hole, may have to merge */ freerl->vcn = freevcn; + freerl->length = freecnt; if (freerl[1].lcn == LCN_HOLE) { freerl->length += freerl[1].length; erl = freerl; diff --git a/source/config.h b/source/config.h index fcf89f0..9a7b0dc 100644 --- a/source/config.h +++ b/source/config.h @@ -1,11 +1,19 @@ /* config.h.in. Generated from configure.ac by autoheader. */ +/* Define this to 1 if you want to enable support of encrypted files in + libntfs and utilities. */ +#undef ENABLE_CRYPTO + /* Define to 1 if debug should be enabled */ #undef ENABLE_DEBUG /* Define to 1 if the nfconv patch should be enabled */ #undef ENABLE_NFCONV +/* Define this to 1 if you want to enable generation of DCE compliant UUIDs. + */ +#undef ENABLE_UUID + /* Define to 1 if using internal fuse */ #undef FUSE_INTERNAL @@ -120,6 +128,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_MNTENT_H +/* Define to 1 if you have the header file. */ +#define HAVE_PWD_H 1 + /* Define to 1 if you have the `realpath' function. */ #undef HAVE_REALPATH diff --git a/source/gekko_io.c b/source/gekko_io.c index 8ee8260..7ac24c7 100644 --- a/source/gekko_io.c +++ b/source/gekko_io.c @@ -112,7 +112,7 @@ static int ntfs_device_gekko_io_open(struct ntfs_device *dev, int flags) } // Check that there is a valid NTFS boot sector at the start of the device - NTFS_BOOT_SECTOR *boot = (NTFS_BOOT_SECTOR *) ntfs_malloc(MAX_SECTOR_SIZE); + NTFS_BOOT_SECTOR *boot = (NTFS_BOOT_SECTOR *) ntfs_alloc(MAX_SECTOR_SIZE); if(boot == NULL) { errno = ENOMEM; return -1; diff --git a/source/index.c b/source/index.c index 7df0dee..01438a0 100644 --- a/source/index.c +++ b/source/index.c @@ -1121,6 +1121,7 @@ static int ntfs_ir_reparent(ntfs_index_context *icx) INDEX_ENTRY *ie; INDEX_BLOCK *ib = NULL; VCN new_ib_vcn; + int ix_root_size; int ret = STATUS_ERROR; ntfs_log_trace("Entering\n"); @@ -1150,6 +1151,7 @@ static int ntfs_ir_reparent(ntfs_index_context *icx) if (ntfs_ib_write(icx, ib)) goto clear_bmp; +retry : ir = ntfs_ir_lookup(icx->ni, icx->name, icx->name_len, &ctx); if (!ir) goto clear_bmp; @@ -1164,12 +1166,32 @@ static int ntfs_ir_reparent(ntfs_index_context *icx) ir->index.index_length = cpu_to_le32(le32_to_cpu(ir->index.entries_offset) + le16_to_cpu(ie->length)); ir->index.allocated_size = ir->index.index_length; - + ix_root_size = sizeof(INDEX_ROOT) - sizeof(INDEX_HEADER) + + le32_to_cpu(ir->index.allocated_size); if (ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr, - sizeof(INDEX_ROOT) - sizeof(INDEX_HEADER) + - le32_to_cpu(ir->index.allocated_size))) + ix_root_size)) { + /* + * When there is no space to build a non-resident + * index, we may have to move the root to an extent + */ + if ((errno == ENOSPC) + && !ctx->al_entry + && !ntfs_inode_add_attrlist(icx->ni)) { + ntfs_attr_put_search_ctx(ctx); + ctx = (ntfs_attr_search_ctx*)NULL; + ir = ntfs_ir_lookup(icx->ni, icx->name, icx->name_len, + &ctx); + if (ir + && !ntfs_attr_record_move_away(ctx, ix_root_size + - le32_to_cpu(ctx->attr->value_length))) { + ntfs_attr_put_search_ctx(ctx); + ctx = (ntfs_attr_search_ctx*)NULL; + goto retry; + } + } /* FIXME: revert index root */ goto clear_bmp; + } /* * FIXME: do it earlier if we have enough space in IR (should always), * so in error case we wouldn't lose the IB. diff --git a/source/layout.h b/source/layout.h index 8670557..daff97d 100644 --- a/source/layout.h +++ b/source/layout.h @@ -311,6 +311,7 @@ typedef enum { #define MFT_REF_MASK_LE const_cpu_to_le64(MFT_REF_MASK_CPU) typedef u64 MFT_REF; +typedef le64 leMFT_REF; /* a little-endian MFT_MREF */ #define MK_MREF(m, s) ((MFT_REF)(((MFT_REF)(s) << 48) | \ ((MFT_REF)(m) & MFT_REF_MASK_CPU))) diff --git a/source/reparse.c b/source/reparse.c index 0f6360e..05490bf 100644 --- a/source/reparse.c +++ b/source/reparse.c @@ -200,8 +200,13 @@ static u64 ntfs_fix_file_name(ntfs_inode *dir_ni, ntfschar *uname, */ lemref = entry->indexed_file; mref = le64_to_cpu(lemref); - for (i=0; ifile_name_length; i++) - uname[i] = found->file_name[i]; + if (NVolCaseSensitive(vol) || !vol->locase) { + for (i=0; ifile_name_length; i++) + uname[i] = found->file_name[i]; + } else { + for (i=0; ifile_name_length; i++) + uname[i] = vol->locase[found->file_name[i]]; + } } } ntfs_index_ctx_put(icx); diff --git a/source/security.c b/source/security.c index 0203ecc..b0bbe6b 100644 --- a/source/security.c +++ b/source/security.c @@ -5030,7 +5030,7 @@ int ntfs_get_group(struct SECURITY_API *scapi, const SID *gsid) */ struct SECURITY_API *ntfs_initialize_file_security(const char *device, - int flags) + unsigned long flags) { ntfs_volume *vol; unsigned long mntflag; diff --git a/source/security.h b/source/security.h index 8612dc2..9d7dd33 100644 --- a/source/security.h +++ b/source/security.h @@ -137,6 +137,7 @@ struct PERMISSIONS_CACHE { enum { SECURITY_DEFAULT, /* rely on fuse for permissions checking */ SECURITY_RAW, /* force same ownership/permissions on files */ + SECURITY_ACL, /* enable Posix ACLs (when compiled in) */ SECURITY_ADDSECURIDS, /* upgrade old security descriptors */ SECURITY_STATICGRPS, /* use static groups for access control */ SECURITY_WANTED /* a security related option was present */ @@ -349,7 +350,7 @@ INDEX_ENTRY *ntfs_read_sii(struct SECURITY_API *scapi, INDEX_ENTRY *ntfs_read_sdh(struct SECURITY_API *scapi, INDEX_ENTRY *entry); struct SECURITY_API *ntfs_initialize_file_security(const char *device, - int flags); + unsigned long flags); BOOL ntfs_leave_file_security(struct SECURITY_API *scx); int ntfs_get_usid(struct SECURITY_API *scapi, uid_t uid, char *buf); diff --git a/source/unistr.c b/source/unistr.c index 180f61a..ffaabe0 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-2010 Jean-Pierre Andre + * Copyright (c) 2008-2011 Jean-Pierre Andre * Copyright (c) 2008 Bernhard Kaindl * * This program/include file is free software; you can redistribute it and/or @@ -1128,6 +1128,69 @@ char *ntfs_uppercase_mbs(const char *low, */ void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len) { +#if 1 /* Vista */ + /* + * This is the table as defined by Vista + */ + /* + * "Start" is inclusive and "End" is exclusive, every value has the + * value of "Add" added to it. + */ + static int uc_run_table[][3] = { /* Start, End, Add */ + {0x0061, 0x007b, -32}, {0x00e0, 0x00f7, -32}, {0x00f8, 0x00ff, -32}, + {0x0256, 0x0258, -205}, {0x028a, 0x028c, -217}, {0x037b, 0x037e, 130}, + {0x03ac, 0x03ad, -38}, {0x03ad, 0x03b0, -37}, {0x03b1, 0x03c2, -32}, + {0x03c2, 0x03c3, -31}, {0x03c3, 0x03cc, -32}, {0x03cc, 0x03cd, -64}, + {0x03cd, 0x03cf, -63}, {0x0430, 0x0450, -32}, {0x0450, 0x0460, -80}, + {0x0561, 0x0587, -48}, {0x1f00, 0x1f08, 8}, {0x1f10, 0x1f16, 8}, + {0x1f20, 0x1f28, 8}, {0x1f30, 0x1f38, 8}, {0x1f40, 0x1f46, 8}, + {0x1f51, 0x1f52, 8}, {0x1f53, 0x1f54, 8}, {0x1f55, 0x1f56, 8}, + {0x1f57, 0x1f58, 8}, {0x1f60, 0x1f68, 8}, {0x1f70, 0x1f72, 74}, + {0x1f72, 0x1f76, 86}, {0x1f76, 0x1f78, 100}, {0x1f78, 0x1f7a, 128}, + {0x1f7a, 0x1f7c, 112}, {0x1f7c, 0x1f7e, 126}, {0x1f80, 0x1f88, 8}, + {0x1f90, 0x1f98, 8}, {0x1fa0, 0x1fa8, 8}, {0x1fb0, 0x1fb2, 8}, + {0x1fb3, 0x1fb4, 9}, {0x1fcc, 0x1fcd, -9}, {0x1fd0, 0x1fd2, 8}, + {0x1fe0, 0x1fe2, 8}, {0x1fe5, 0x1fe6, 7}, {0x1ffc, 0x1ffd, -9}, + {0x2170, 0x2180, -16}, {0x24d0, 0x24ea, -26}, {0x2c30, 0x2c5f, -48}, + {0x2d00, 0x2d26, -7264}, {0xff41, 0xff5b, -32}, {0} + }; + /* + * "Start" is exclusive and "End" is inclusive, every second value is + * decremented by one. + */ + static int uc_dup_table[][2] = { /* Start, End */ + {0x0100, 0x012f}, {0x0132, 0x0137}, {0x0139, 0x0149}, {0x014a, 0x0178}, + {0x0179, 0x017e}, {0x01a0, 0x01a6}, {0x01b3, 0x01b7}, {0x01cd, 0x01dd}, + {0x01de, 0x01ef}, {0x01f4, 0x01f5}, {0x01f8, 0x01f9}, {0x01fa, 0x0220}, + {0x0222, 0x0234}, {0x023b, 0x023c}, {0x0241, 0x0242}, {0x0246, 0x024f}, + {0x03d8, 0x03ef}, {0x03f7, 0x03f8}, {0x03fa, 0x03fb}, {0x0460, 0x0481}, + {0x048a, 0x04bf}, {0x04c1, 0x04c4}, {0x04c5, 0x04c8}, {0x04c9, 0x04ce}, + {0x04ec, 0x04ed}, {0x04d0, 0x04eb}, {0x04ee, 0x04f5}, {0x04f6, 0x0513}, + {0x1e00, 0x1e95}, {0x1ea0, 0x1ef9}, {0x2183, 0x2184}, {0x2c60, 0x2c61}, + {0x2c67, 0x2c6c}, {0x2c75, 0x2c76}, {0x2c80, 0x2ce3}, {0} + }; + /* + * Set the Unicode character at offset "Offset" to "Value". Note, + * "Value" is host endian. + */ + static int uc_byte_table[][2] = { /* Offset, Value */ + {0x00ff, 0x0178}, {0x0180, 0x0243}, {0x0183, 0x0182}, {0x0185, 0x0184}, + {0x0188, 0x0187}, {0x018c, 0x018b}, {0x0192, 0x0191}, {0x0195, 0x01f6}, + {0x0199, 0x0198}, {0x019a, 0x023d}, {0x019e, 0x0220}, {0x01a8, 0x01a7}, + {0x01ad, 0x01ac}, {0x01b0, 0x01af}, {0x01b9, 0x01b8}, {0x01bd, 0x01bc}, + {0x01bf, 0x01f7}, {0x01c6, 0x01c4}, {0x01c9, 0x01c7}, {0x01cc, 0x01ca}, + {0x01dd, 0x018e}, {0x01f3, 0x01f1}, {0x023a, 0x2c65}, {0x023e, 0x2c66}, + {0x0253, 0x0181}, {0x0254, 0x0186}, {0x0259, 0x018f}, {0x025b, 0x0190}, + {0x0260, 0x0193}, {0x0263, 0x0194}, {0x0268, 0x0197}, {0x0269, 0x0196}, + {0x026b, 0x2c62}, {0x026f, 0x019c}, {0x0272, 0x019d}, {0x0275, 0x019f}, + {0x027d, 0x2c64}, {0x0280, 0x01a6}, {0x0283, 0x01a9}, {0x0288, 0x01ae}, + {0x0289, 0x0244}, {0x028c, 0x0245}, {0x0292, 0x01b7}, {0x03f2, 0x03f9}, + {0x04cf, 0x04c0}, {0x1d7d, 0x2c63}, {0x214e, 0x2132}, {0} + }; +#else /* Vista */ + /* + * This is the table as defined by Windows XP + */ static int uc_run_table[][3] = { /* Start, End, Add */ {0x0061, 0x007B, -32}, {0x0451, 0x045D, -80}, {0x1F70, 0x1F72, 74}, {0x00E0, 0x00F7, -32}, {0x045E, 0x0460, -80}, {0x1F72, 0x1F76, 86}, @@ -1164,6 +1227,7 @@ void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len) {0x01A8, 0x01A7}, {0x01DD, 0x018E}, {0x0268, 0x0197}, {0} }; +#endif /* Vista */ int i, r; int k, off; @@ -1187,6 +1251,27 @@ void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len) } } +/* + * Allocate and build the default upcase table + * + * Returns the number of entries + * 0 if failed + */ + +#define UPCASE_LEN 65536 /* default number of entries in upcase */ + +u32 ntfs_upcase_build_default(ntfschar **upcase) +{ + u32 upcase_len; + + *upcase = (ntfschar*)ntfs_malloc(UPCASE_LEN*2); + if (*upcase) { + ntfs_upcase_table_build(*upcase, UPCASE_LEN*2); + upcase_len = UPCASE_LEN; + } + return (upcase_len); +} + /* * Build a table for converting to lower case * diff --git a/source/unistr.h b/source/unistr.h index 639c503..8cadc3c 100644 --- a/source/unistr.h +++ b/source/unistr.h @@ -61,6 +61,7 @@ extern char *ntfs_uppercase_mbs(const char *low, const ntfschar *upcase, u32 upcase_len); extern void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len); +extern u32 ntfs_upcase_build_default(ntfschar **upcase); extern ntfschar *ntfs_locase_table_build(const ntfschar *uc, u32 uc_cnt); extern ntfschar *ntfs_str2ucs(const char *s, int *len); diff --git a/source/volume.c b/source/volume.c index a30a33a..28e4c90 100644 --- a/source/volume.c +++ b/source/volume.c @@ -70,7 +70,7 @@ #include "misc.h" const char *ntfs_home = -"Ntfs-3g news, support and information: http://ntfs-3g.org\n"; +"News, support and information: http://tuxera.com\n"; static const char *invalid_ntfs_msg = "The device '%s' doesn't seem to have a valid NTFS.\n" @@ -113,7 +113,7 @@ static const char *fakeraid_msg = static const char *access_denied_msg = "Please check '%s' and the ntfs-3g binary permissions,\n" "and the mounting user ID. More explanation is provided at\n" -"http://ntfs-3g.org/support.html#unprivileged\n"; +"http://tuxera.com/community/ntfs-3g-faq/#unprivileged\n"; /** * ntfs_volume_alloc - Create an NTFS volume object and initialise it @@ -482,13 +482,10 @@ ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, unsigned long flags) goto error_exit; /* Create the default upcase table. */ - vol->upcase_len = 65536; - vol->upcase = ntfs_malloc(vol->upcase_len * sizeof(ntfschar)); - if (!vol->upcase) + vol->upcase_len = ntfs_upcase_build_default(&vol->upcase); + if (!vol->upcase_len || !vol->upcase) goto error_exit; - - ntfs_upcase_table_build(vol->upcase, - vol->upcase_len * sizeof(ntfschar)); + /* Default with no locase table and case sensitive file names */ vol->locase = (ntfschar*)NULL; NVolSetCaseSensitive(vol); @@ -1184,7 +1181,7 @@ 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)) { + if (!(flags & (MS_RDONLY | MS_FORENSIC))) { if (!(flags & MS_IGNORE_HIBERFILE) && ntfs_volume_check_hiberfile(vol, 1) < 0) goto error_exit; @@ -1196,10 +1193,10 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) if (ntfs_logfile_reset(vol)) goto error_exit; } - } /* make $TXF_DATA resident if present on the root directory */ - if (!NVolReadOnly(vol) && fix_txf_data(vol)) - goto error_exit; + if (fix_txf_data(vol)) + goto error_exit; + } return vol; io_error_exit: diff --git a/source/volume.h b/source/volume.h index 9c8ab14..b3f47bf 100644 --- a/source/volume.h +++ b/source/volume.h @@ -58,6 +58,7 @@ #endif #define MS_IGNORE_HIBERFILE 0x20000000 +#define MS_FORENSIC 0x04000000 /* No modification during mount */ /* Forward declaration */ typedef struct _ntfs_volume ntfs_volume; diff --git a/source/xattrs.c b/source/xattrs.c new file mode 100644 index 0000000..5be2c06 --- /dev/null +++ b/source/xattrs.c @@ -0,0 +1,791 @@ +/** + * xattrs.c : common functions to deal with 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 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_SETXATTR /* extended attributes support required */ + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif + +#include "types.h" +#include "param.h" +#include "layout.h" +#include "attrib.h" +#include "index.h" +#include "dir.h" +#include "security.h" +#include "acls.h" +#include "efs.h" +#include "reparse.h" +#include "object_id.h" +#include "misc.h" +#include "logging.h" +#include "xattrs.h" + +#if POSIXACLS +#if __BYTE_ORDER == __BIG_ENDIAN + +/* + * Posix ACL structures + */ + +struct LE_POSIX_ACE { + le16 tag; + le16 perms; + le32 id; +} __attribute__((__packed__)); + +struct LE_POSIX_ACL { + u8 version; + u8 flags; + le16 filler; + struct LE_POSIX_ACE ace[0]; +} __attribute__((__packed__)); + +#endif +#endif + +static const char xattr_ntfs_3g[] = "ntfs-3g."; +static const char nf_ns_user_prefix[] = "user."; +static const int nf_ns_user_prefix_len = sizeof(nf_ns_user_prefix) - 1; + +static const char nf_ns_xattr_ntfs_acl[] = "system.ntfs_acl"; +static const char nf_ns_xattr_attrib[] = "system.ntfs_attrib"; +static const char nf_ns_xattr_attrib_be[] = "system.ntfs_attrib_be"; +static const char nf_ns_xattr_efsinfo[] = "system.ntfs_efsinfo"; +static const char nf_ns_xattr_reparse[] = "system.ntfs_reparse_data"; +static const char nf_ns_xattr_object_id[] = "system.ntfs_object_id"; +static const char nf_ns_xattr_dos_name[] = "system.ntfs_dos_name"; +static const char nf_ns_xattr_times[] = "system.ntfs_times"; +static const char nf_ns_xattr_times_be[] = "system.ntfs_times_be"; +static const char nf_ns_xattr_crtime[] = "system.ntfs_crtime"; +static const char nf_ns_xattr_crtime_be[] = "system.ntfs_crtime_be"; +static const char nf_ns_xattr_posix_access[] = "system.posix_acl_access"; +static const char nf_ns_xattr_posix_default[] = "system.posix_acl_default"; + +static const char nf_ns_alt_xattr_efsinfo[] = "user.ntfs.efsinfo"; + +struct XATTRNAME { + enum SYSTEMXATTRS xattr; + const char *name; +} ; + +static struct XATTRNAME nf_ns_xattr_names[] = { + { XATTR_NTFS_ACL, nf_ns_xattr_ntfs_acl }, + { XATTR_NTFS_ATTRIB, nf_ns_xattr_attrib }, + { XATTR_NTFS_ATTRIB_BE, nf_ns_xattr_attrib_be }, + { XATTR_NTFS_EFSINFO, nf_ns_xattr_efsinfo }, + { XATTR_NTFS_REPARSE_DATA, nf_ns_xattr_reparse }, + { XATTR_NTFS_OBJECT_ID, nf_ns_xattr_object_id }, + { XATTR_NTFS_DOS_NAME, nf_ns_xattr_dos_name }, + { XATTR_NTFS_TIMES, nf_ns_xattr_times }, + { XATTR_NTFS_TIMES_BE, nf_ns_xattr_times_be }, + { XATTR_NTFS_CRTIME, nf_ns_xattr_crtime }, + { XATTR_NTFS_CRTIME_BE, nf_ns_xattr_crtime_be }, + { XATTR_POSIX_ACC, nf_ns_xattr_posix_access }, + { XATTR_POSIX_DEF, nf_ns_xattr_posix_default }, + { XATTR_UNMAPPED, (char*)NULL } /* terminator */ +}; + +/* + * Make an integer big-endian + * + * Swap bytes on a small-endian computer and does nothing on a + * big-endian computer. + */ + +static void fix_big_endian(char *p, int size) +{ +#if __BYTE_ORDER == __LITTLE_ENDIAN + int i,j; + int c; + + i = 0; + j = size - 1; + while (i < j) { + c = p[i]; + p[i++] = p[j]; + p[j--] = c; + } +#endif +} + +#if POSIXACLS +#if __BYTE_ORDER == __BIG_ENDIAN + +/* + * Make a Posix ACL CPU endian + */ + +static int le_acl_to_cpu(const struct LE_POSIX_ACL *le_acl, size_t size, + struct POSIX_ACL *acl) +{ + int i; + int cnt; + + acl->version = le_acl->version; + acl->flags = le_acl->flags; + acl->filler = 0; + cnt = (size - sizeof(struct LE_POSIX_ACL)) / sizeof(struct LE_POSIX_ACE); + for (i=0; iace[i].tag = le16_to_cpu(le_acl->ace[i].tag); + acl->ace[i].perms = le16_to_cpu(le_acl->ace[i].perms); + acl->ace[i].id = le32_to_cpu(le_acl->ace[i].id); + } + return (0); +} + +/* + * Make a Posix ACL little endian + */ + +int cpu_to_le_acl(const struct POSIX_ACL *acl, size_t size, + struct LE_POSIX_ACL *le_acl) +{ + int i; + int cnt; + + le_acl->version = acl->version; + le_acl->flags = acl->flags; + le_acl->filler = const_cpu_to_le16(0); + cnt = (size - sizeof(struct POSIX_ACL)) / sizeof(struct POSIX_ACE); + for (i=0; iace[i].tag = cpu_to_le16(acl->ace[i].tag); + le_acl->ace[i].perms = cpu_to_le16(acl->ace[i].perms); + le_acl->ace[i].id = cpu_to_le32(acl->ace[i].id); + } + return (0); +} + +#endif +#endif + +/* + * Determine whether an extended attribute is mapped to + * internal data (original name in system namespace, or renamed) + */ + +enum SYSTEMXATTRS ntfs_xattr_system_type(const char *name, + ntfs_volume *vol) +{ + struct XATTRNAME *p; + enum SYSTEMXATTRS ret; +#ifdef XATTR_MAPPINGS + const struct XATTRMAPPING *q; +#endif /* XATTR_MAPPINGS */ + + p = nf_ns_xattr_names; + while (p->name && strcmp(p->name,name)) + p++; + ret = p->xattr; +#ifdef XATTR_MAPPINGS + if (!p->name && vol && vol->xattr_mapping) { + q = vol->xattr_mapping; + while (q && strcmp(q->name,name)) + q = q->next; + if (q) + ret = q->xattr; + } +#else /* XATTR_MAPPINGS */ + if (!p->name + && vol + && vol->efs_raw + && !strcmp(nf_ns_alt_xattr_efsinfo,name)) + ret = XATTR_NTFS_EFSINFO; +#endif /* XATTR_MAPPINGS */ + return (ret); +} + +#ifdef XATTR_MAPPINGS + +/* + * Basic read from a user mapping file on another volume + */ + +static int basicread(void *fileid, char *buf, size_t size, off_t offs __attribute__((unused))) +{ + return (read(*(int*)fileid, buf, size)); +} + + +/* + * Read from a user mapping file on current NTFS partition + */ + +static int localread(void *fileid, char *buf, size_t size, off_t offs) +{ + return (ntfs_attr_data_read((ntfs_inode*)fileid, + AT_UNNAMED, 0, buf, size, offs)); +} + +/* + * Get a single mapping item from buffer + * + * Always reads a full line, truncating long lines + * Refills buffer when exhausted + * Returns pointer to item, or NULL when there is no more + * Note : errors are logged, but not returned +// TODO partially share with acls.c + */ + +static struct XATTRMAPPING *getmappingitem(FILEREADER reader, void *fileid, + off_t *poffs, char *buf, int *psrc, s64 *psize) +{ + int src; + int dst; + char *pe; + char *ps; + char *pu; + enum SYSTEMXATTRS xattr; + int gotend; + char maptext[LINESZ]; + struct XATTRMAPPING *item; + + src = *psrc; + dst = 0; + do { + gotend = 0; + while ((src < *psize) + && (buf[src] != '\n')) { + /* ignore spaces */ + if ((dst < LINESZ) + && (buf[src] != '\r') + && (buf[src] != '\t') + && (buf[src] != ' ')) + maptext[dst++] = buf[src]; + src++; + } + if (src >= *psize) { + *poffs += *psize; + *psize = reader(fileid, buf, (size_t)BUFSZ, *poffs); + src = 0; + } else { + gotend = 1; + src++; + maptext[dst] = '\0'; + dst = 0; + } + } while (*psize && ((maptext[0] == '#') || !gotend)); + item = (struct XATTRMAPPING*)NULL; + if (gotend) { + /* decompose into system name and user name */ + ps = maptext; + pu = strchr(maptext,':'); + if (pu) { + *pu++ = 0; + pe = strchr(pu,':'); + if (pe) + *pe = 0; + /* check name validity */ + if ((strlen(pu) < 6) || strncmp(pu,"user.",5)) + pu = (char*)NULL; + xattr = ntfs_xattr_system_type(ps, + (ntfs_volume*)NULL); + if (xattr == XATTR_UNMAPPED) + pu = (char*)NULL; + } + if (pu) { + item = (struct XATTRMAPPING*)ntfs_malloc( + sizeof(struct XATTRMAPPING) + + strlen(pu)); + if (item) { + item->xattr = xattr; + strcpy(item->name,pu); + item->next = (struct XATTRMAPPING*)NULL; + } + } else { + ntfs_log_early_error("Bad xattr mapping item, aborting\n"); + } + } + *psrc = src; + return (item); +} + +/* + * Read xattr mapping file and split into their attribute. + * Parameters are kept in a chained list. + * Returns the head of list, if any + * Errors are logged, but not returned + * + * If an absolute path is provided, the mapping file is assumed + * to be located in another mounted file system, and plain read() + * are used to get its contents. + * If a relative path is provided, the mapping file is assumed + * to be located on the current file system, and internal IO + * have to be used since we are still mounting and we have not + * entered the fuse loop yet. + */ + +static struct XATTRMAPPING *ntfs_read_xattr_mapping(FILEREADER reader, + void *fileid) +{ + char buf[BUFSZ]; + struct XATTRMAPPING *item; + struct XATTRMAPPING *current; + struct XATTRMAPPING *firstitem; + struct XATTRMAPPING *lastitem; + BOOL duplicated; + int src; + off_t offs; + s64 size; + + firstitem = (struct XATTRMAPPING*)NULL; + lastitem = (struct XATTRMAPPING*)NULL; + offs = 0; + size = reader(fileid, buf, (size_t)BUFSZ, (off_t)0); + if (size > 0) { + src = 0; + do { + item = getmappingitem(reader, fileid, &offs, + buf, &src, &size); + if (item) { + /* check no double mapping */ + duplicated = FALSE; + for (current=firstitem; current; current=current->next) + if ((current->xattr == item->xattr) + || !strcmp(current->name,item->name)) + duplicated = TRUE; + if (duplicated) { + free(item); + ntfs_log_early_error("Conflicting xattr mapping ignored\n"); + } else { + item->next = (struct XATTRMAPPING*)NULL; + if (lastitem) + lastitem->next = item; + else + firstitem = item; + lastitem = item; + } + } + } while (item); + } + return (firstitem); +} + +/* + * Build the extended attribute mappings to user namespace + * + * Note : no error is returned. If we refused mounting when there + * is an error it would be too difficult to fix the offending file + */ + +struct XATTRMAPPING *ntfs_xattr_build_mapping(ntfs_volume *vol, + const char *xattrmap_path) +{ + struct XATTRMAPPING *firstmapping; + struct XATTRMAPPING *mapping; + BOOL user_efs; + BOOL notfound; + ntfs_inode *ni; + int fd; + + firstmapping = (struct XATTRMAPPING*)NULL; + notfound = FALSE; + if (!xattrmap_path) + xattrmap_path = XATTRMAPPINGFILE; + if (xattrmap_path[0] == '/') { + fd = open(xattrmap_path,O_RDONLY); + if (fd > 0) { + firstmapping = ntfs_read_xattr_mapping(basicread, (void*)&fd); + close(fd); + } else + notfound = TRUE; + } else { + ni = ntfs_pathname_to_inode(vol, NULL, xattrmap_path); + if (ni) { + firstmapping = ntfs_read_xattr_mapping(localread, ni); + ntfs_inode_close(ni); + } else + notfound = TRUE; + } + if (notfound && strcmp(xattrmap_path, XATTRMAPPINGFILE)) { + ntfs_log_early_error("Could not open \"%s\"\n",xattrmap_path); + } + if (vol->efs_raw) { + user_efs = TRUE; + for (mapping=firstmapping; mapping; mapping=mapping->next) + if (mapping->xattr == XATTR_NTFS_EFSINFO) + user_efs = FALSE; + } else + user_efs = FALSE; + if (user_efs) { + mapping = (struct XATTRMAPPING*)ntfs_malloc( + sizeof(struct XATTRMAPPING) + + strlen(nf_ns_alt_xattr_efsinfo)); + if (mapping) { + mapping->next = firstmapping; + mapping->xattr = XATTR_NTFS_EFSINFO; + strcpy(mapping->name,nf_ns_alt_xattr_efsinfo); + firstmapping = mapping; + } + } + return (firstmapping); +} + +void ntfs_xattr_free_mapping(struct XATTRMAPPING *mapping) +{ + struct XATTRMAPPING *p, *q; + + p = mapping; + while (p) { + q = p->next; + free(p); + p = q; + } +} + +#endif /* XATTR_MAPPINGS */ + + +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 res; + int i; +#if POSIXACLS +#if __BYTE_ORDER == __BIG_ENDIAN + struct POSIX_ACL *acl; +#endif +#endif + + /* + * the returned value is the needed + * size. If it is too small, no copy + * is done, and the caller has to + * issue a new call with correct size. + */ + switch (attr) { + case XATTR_NTFS_ACL : + res = ntfs_get_ntfs_acl(scx, ni, value, size); + break; +#if POSIXACLS +#if __BYTE_ORDER == __BIG_ENDIAN + case XATTR_POSIX_ACC : + acl = (struct POSIX_ACL*)ntfs_malloc(size); + if (acl) { + res = ntfs_get_posix_acl(scx, ni, + nf_ns_xattr_posix_access, (char*)acl, size); + if (res > 0) { + if (cpu_to_le_acl(acl,res, + (struct LE_POSIX_ACL*)value)) + res = -errno; + } + free(acl); + } else + res = -errno; + break; + case XATTR_POSIX_DEF : + acl = (struct POSIX_ACL*)ntfs_malloc(size); + if (acl) { + res = ntfs_get_posix_acl(scx, ni, + nf_ns_xattr_posix_default, (char*)acl, size); + if (res > 0) { + if (cpu_to_le_acl(acl,res, + (struct LE_POSIX_ACL*)value)) + res = -errno; + } + free(acl); + } else + res = -errno; + break; +#else + case XATTR_POSIX_ACC : + res = ntfs_get_posix_acl(scx, ni, nf_ns_xattr_posix_access, + value, size); + break; + case XATTR_POSIX_DEF : + res = ntfs_get_posix_acl(scx, ni, nf_ns_xattr_posix_default, + value, size); + break; +#endif +#endif + case XATTR_NTFS_ATTRIB : + res = ntfs_get_ntfs_attrib(ni, value, size); + break; + case XATTR_NTFS_ATTRIB_BE : + res = ntfs_get_ntfs_attrib(ni, value, size); + if ((res == 4) && value) { + if (size >= 4) + fix_big_endian(value,4); + else + res = -EINVAL; + } + break; + case XATTR_NTFS_EFSINFO : + if (ni->vol->efs_raw) + res = ntfs_get_efs_info(ni, value, size); + else + res = -EPERM; + break; + case XATTR_NTFS_REPARSE_DATA : + res = ntfs_get_ntfs_reparse_data(ni, value, size); + break; + case XATTR_NTFS_OBJECT_ID : + res = ntfs_get_ntfs_object_id(ni, value, size); + break; + case XATTR_NTFS_DOS_NAME: + if (dir_ni) + res = ntfs_get_ntfs_dos_name(ni, dir_ni, value, size); + else + res = -errno; + break; + case XATTR_NTFS_TIMES: + res = ntfs_inode_get_times(ni, value, size); + break; + case XATTR_NTFS_TIMES_BE: + res = ntfs_inode_get_times(ni, value, size); + if ((res > 0) && value) { + for (i=0; (i+1)*sizeof(u64)<=(unsigned int)res; i++) + fix_big_endian(&value[i*sizeof(u64)], + sizeof(u64)); + } + break; + case XATTR_NTFS_CRTIME: + res = ntfs_inode_get_times(ni, value, + (size >= sizeof(u64) ? sizeof(u64) : size)); + break; + case XATTR_NTFS_CRTIME_BE: + res = ntfs_inode_get_times(ni, value, + (size >= sizeof(u64) ? sizeof(u64) : size)); + if ((res >= (int)sizeof(u64)) && value) + fix_big_endian(value,sizeof(u64)); + break; + default : + errno = EOPNOTSUPP; + res = -errno; + break; + } + return (res); +} + +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 res; + int i; + char buf[4*sizeof(u64)]; +#if POSIXACLS +#if __BYTE_ORDER == __BIG_ENDIAN + struct POSIX_ACL *acl; +#endif +#endif + + switch (attr) { + case XATTR_NTFS_ACL : + res = ntfs_set_ntfs_acl(scx, ni, value, size, flags); + break; +#if POSIXACLS +#if __BYTE_ORDER == __BIG_ENDIAN + case XATTR_POSIX_ACC : + acl = (struct POSIX_ACL*)ntfs_malloc(size); + if (acl) { + if (!le_acl_to_cpu((const struct LE_POSIX_ACL*)value, + size, acl)) { + res = ntfs_set_posix_acl(scx ,ni , + nf_ns_xattr_posix_access, + (char*)acl, size, flags); + } else + res = -errno; + free(acl); + } else + res = -errno; + break; + case XATTR_POSIX_DEF : + acl = (struct POSIX_ACL*)ntfs_malloc(size); + if (acl) { + if (!le_acl_to_cpu((const struct LE_POSIX_ACL*)value, + size, acl)) { + res = ntfs_set_posix_acl(scx ,ni , + nf_ns_xattr_posix_default, + (char*)acl, size, flags); + } else + res = -errno; + free(acl); + } else + res = -errno; + break; +#else + case XATTR_POSIX_ACC : + res = ntfs_set_posix_acl(scx ,ni , nf_ns_xattr_posix_access, + value, size, flags); + break; + case XATTR_POSIX_DEF : + res = ntfs_set_posix_acl(scx, ni, nf_ns_xattr_posix_default, + value, size, flags); + break; +#endif +#endif + case XATTR_NTFS_ATTRIB : + res = ntfs_set_ntfs_attrib(ni, value, size, flags); + break; + case XATTR_NTFS_ATTRIB_BE : + if (value && (size >= 4)) { + memcpy(buf,value,4); + fix_big_endian(buf,4); + res = ntfs_set_ntfs_attrib(ni, buf, 4, flags); + } else + res = ntfs_set_ntfs_attrib(ni, value, size, flags); + break; + case XATTR_NTFS_EFSINFO : + if (ni->vol->efs_raw) + res = ntfs_set_efs_info(ni, value, size, flags); + else + res = -EPERM; + break; + case XATTR_NTFS_REPARSE_DATA : + res = ntfs_set_ntfs_reparse_data(ni, value, size, flags); + break; + case XATTR_NTFS_OBJECT_ID : + res = ntfs_set_ntfs_object_id(ni, value, size, flags); + break; + case XATTR_NTFS_DOS_NAME: + if (dir_ni) + /* warning : this closes both inodes */ + res = ntfs_set_ntfs_dos_name(ni, dir_ni, value, + size, flags); + else + res = -errno; + break; + case XATTR_NTFS_TIMES: + res = ntfs_inode_set_times(ni, value, size, flags); + break; + case XATTR_NTFS_TIMES_BE: + if (value && (size > 0) && (size <= 4*sizeof(u64))) { + memcpy(buf,value,size); + for (i=0; (i+1)*sizeof(u64)<=size; i++) + fix_big_endian(&buf[i*sizeof(u64)], + sizeof(u64)); + res = ntfs_inode_set_times(ni, buf, size, flags); + } else + res = ntfs_inode_set_times(ni, value, size, flags); + break; + case XATTR_NTFS_CRTIME: + res = ntfs_inode_set_times(ni, value, + (size >= sizeof(u64) ? sizeof(u64) : size), flags); + break; + case XATTR_NTFS_CRTIME_BE: + if (value && (size >= sizeof(u64))) { + memcpy(buf,value,sizeof(u64)); + fix_big_endian(buf,sizeof(u64)); + res = ntfs_inode_set_times(ni, buf, sizeof(u64), flags); + } else + res = ntfs_inode_set_times(ni, value, size, flags); + break; + default : + errno = EOPNOTSUPP; + res = -errno; + break; + } + return (res); +} + +int ntfs_xattr_system_removexattr(struct SECURITY_CONTEXT *scx, + enum SYSTEMXATTRS attr, + ntfs_inode *ni, ntfs_inode *dir_ni) +{ + int res; + + res = 0; + switch (attr) { + /* + * Removal of NTFS ACL, ATTRIB, EFSINFO or TIMES + * is never allowed + */ + case XATTR_NTFS_ACL : + case XATTR_NTFS_ATTRIB : + case XATTR_NTFS_ATTRIB_BE : + case XATTR_NTFS_EFSINFO : + case XATTR_NTFS_TIMES : + case XATTR_NTFS_TIMES_BE : + case XATTR_NTFS_CRTIME : + case XATTR_NTFS_CRTIME_BE : + res = -EPERM; + break; +#if POSIXACLS + case XATTR_POSIX_ACC : + case XATTR_POSIX_DEF : + if (ni) { + if (!ntfs_allowed_as_owner(scx, ni) + || ntfs_remove_posix_acl(scx, ni, + (attr == XATTR_POSIX_ACC ? + nf_ns_xattr_posix_access : + nf_ns_xattr_posix_default))) + res = -errno; + } else + res = -errno; + break; +#endif + case XATTR_NTFS_REPARSE_DATA : + if (ni) { + if (!ntfs_allowed_as_owner(scx, ni) + || ntfs_remove_ntfs_reparse_data(ni)) + res = -errno; + } else + res = -errno; + break; + case XATTR_NTFS_OBJECT_ID : + if (ni) { + if (!ntfs_allowed_as_owner(scx, ni) + || ntfs_remove_ntfs_object_id(ni)) + res = -errno; + } else + res = -errno; + break; + case XATTR_NTFS_DOS_NAME: + if (ni && dir_ni) { + if (ntfs_remove_ntfs_dos_name(ni,dir_ni)) + res = -errno; + /* ni and dir_ni have been closed */ + } else + res = -errno; + break; + default : + errno = EOPNOTSUPP; + res = -errno; + break; + } + return (res); +} + +#endif /* HAVE_SETXATTR */ diff --git a/source/xattrs.h b/source/xattrs.h index 7ebd298..5158311 100644 --- a/source/xattrs.h +++ b/source/xattrs.h @@ -59,8 +59,6 @@ void ntfs_xattr_free_mapping(struct XATTRMAPPING*); 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,