diff --git a/src/add-ons/kernel/file_systems/ntfs/fs_func.c b/src/add-ons/kernel/file_systems/ntfs/fs_func.c index 82736afb99..43dea4595b 100644 --- a/src/add-ons/kernel/file_systems/ntfs/fs_func.c +++ b/src/add-ons/kernel/file_systems/ntfs/fs_func.c @@ -161,7 +161,7 @@ fs_identify_partition(int fd, partition_data *partition, void **_cookie) } // try mount - ntVolume = utils_mount_volume(devpath, MS_RDONLY, true); + ntVolume = utils_mount_volume(devpath, MS_RDONLY | MS_RECOVER); if (!ntVolume) { ERROR("fs_identify_partition: mount failed\n"); return -1; @@ -257,7 +257,7 @@ fs_mount(fs_volume *_vol, const char *device, ulong flags, const char *args, ns->flags |= B_FS_IS_READONLY; } - ns->ntvol = utils_mount_volume(device, mountFlags, true); + ns->ntvol = utils_mount_volume(device, mountFlags | MS_RECOVER); if (ns->ntvol != NULL) result = B_NO_ERROR; else @@ -530,7 +530,7 @@ fs_read_vnode(fs_volume *_vol, ino_t vnid, fs_vnode *_node, int *_type, goto exit; newNode->vnid = vnid; - newNode->parent_vnid = ntfs_get_parent_ref(ni); + newNode->parent_vnid = ntfs_mft_get_parent_ref(ni); if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) set_mime(newNode, ".***"); diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/Jamfile b/src/add-ons/kernel/file_systems/ntfs/libntfs/Jamfile index a82fcf941e..3cc7cd8413 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/Jamfile +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/Jamfile @@ -31,4 +31,6 @@ KernelStaticLibrary libntfs.a : unistr.c unix_io.c volume.c + realpath.c + xattrs.c ; diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/acls.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/acls.c index 1591fa2570..788ed0387b 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/acls.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/acls.c @@ -4,7 +4,7 @@ * This module is part of ntfs-3g library, but may also be * integrated in tools running over Linux or Windows * - * Copyright (c) 2007-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 @@ -528,9 +528,7 @@ gid_t ntfs_find_group(const struct MAPPING* groupmapping, const SID * gsid) { gid_t gid; const struct MAPPING *p; - int gsidsz; - gsidsz = ntfs_sid_size(gsid); p = groupmapping; while (p && p->xid && !ntfs_same_sid(gsid, p->sid)) p = p->next; @@ -1907,7 +1905,6 @@ static int buildacls_posix(struct MAPPING* const mapping[], const SID *sid; int acecnt; int usidsz; - int gsidsz; int wsidsz; int asidsz; int ssidsz; @@ -1915,7 +1912,6 @@ static int buildacls_posix(struct MAPPING* const mapping[], le32 grants; usidsz = ntfs_sid_size(usid); - gsidsz = ntfs_sid_size(gsid); wsidsz = ntfs_sid_size(worldsid); asidsz = ntfs_sid_size(adminsid); ssidsz = ntfs_sid_size(systemsid); @@ -3138,7 +3134,6 @@ static int norm_ownadmin_permissions_posix(struct POSIX_SECURITY *posix_desc, u16 tag; u16 tagsset; struct POSIX_ACE *pxace; - int acccnt; mode_t denywrld; mode_t allow; mode_t deny; @@ -3147,7 +3142,6 @@ static int norm_ownadmin_permissions_posix(struct POSIX_SECURITY *posix_desc, mode = 0; pxace = posix_desc->acl.ace; - acccnt = posix_desc->acccnt; tagsset = 0; denywrld = 0; /* @@ -3887,12 +3881,10 @@ struct POSIX_SECURITY *ntfs_build_permissions_posix( int ntfs_build_permissions(const char *securattr, const SID *usid, const SID *gsid, BOOL isdir) { - const SECURITY_DESCRIPTOR_RELATIVE *phead; int perm; BOOL adminowns; BOOL groupowns; - phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; adminowns = ntfs_same_sid(usid,adminsid) || ntfs_same_sid(gsid,adminsid); groupowns = !adminowns && ntfs_same_sid(gsid,usid); @@ -3922,6 +3914,7 @@ static unsigned long atoul(const char *p) } #endif + /* * Build an internal representation of a SID * Returns a copy in allocated memory if it succeeds @@ -3964,35 +3957,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, ...) -{ -#ifndef __HAIKU__ - 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); -#endif -} - - /* * Get a single mapping item from buffer * @@ -4006,7 +3970,6 @@ static struct MAPLIST *getmappingitem(FILEREADER reader, void *fileid, { int src; int dst; - char *p; char *q; char *pu; char *pg; @@ -4040,7 +4003,6 @@ static struct MAPLIST *getmappingitem(FILEREADER reader, void *fileid, if (gotend) { pu = pg = (char*)NULL; /* decompose into uid, gid and sid */ - p = item->maptext; item->uidstr = item->maptext; item->gidstr = strchr(item->uidstr, ':'); if (item->gidstr) { @@ -4055,7 +4017,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; @@ -4184,7 +4146,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); } } @@ -4264,7 +4226,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/src/add-ons/kernel/file_systems/ntfs/libntfs/attrib.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/attrib.c index 123c9a9147..281a620515 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/attrib.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/attrib.c @@ -5,7 +5,7 @@ * Copyright (c) 2002-2005 Richard Russon * Copyright (c) 2002-2008 Szabolcs Szakacsits * Copyright (c) 2004-2007 Yura Pakhuchiy - * Copyright (c) 2007-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 @@ -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); @@ -475,9 +475,10 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, } cs = a->flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE); - + + /* a file may be sparse though its unnamed data is not (cf $UsnJrnl) */ if (na->type == AT_DATA && na->name == AT_UNNAMED && - ((!(a->flags & ATTR_IS_SPARSE) != !NAttrSparse(na)) || + (((a->flags & ATTR_IS_SPARSE) && !NAttrSparse(na)) || (!(a->flags & ATTR_IS_ENCRYPTED) != !NAttrEncrypted(na)))) { errno = EIO; ntfs_log_perror("Inode %lld has corrupt attribute flags " @@ -588,6 +589,82 @@ 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; + VCN existing_vcn; + 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; + + existing_vcn = (na->rl ? na->rl->vcn : -1); + + 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; + /* reaching a previously allocated part ? */ + if ((existing_vcn >= 0) + && (needed >= existing_vcn)) { + needed = last_vcn; + } + } + } else + rl = (runlist_element*)NULL; + } while (rl && (needed < last_vcn)); + 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 @@ -1114,12 +1191,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; @@ -1149,9 +1241,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 +1620,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 { /* @@ -1612,6 +1735,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 @@ -1703,11 +1829,32 @@ 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 (ntfs_attr_truncate(na, pos + count)) { +#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 avoid sparse testing until the data is filled in. + */ + if (ntfs_attr_truncate_i(na, pos + count, + (NAttrDataAppending(na) ? + HOLES_DELAY : HOLES_OK))) { ntfs_log_perror("Failed to enlarge attribute"); goto errno_set; } +#else + if (ntfs_attr_truncate_i(na, pos + count, HOLES_OK)) { + 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); @@ -1768,8 +1915,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,9 +2214,13 @@ 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); - if (updatemap) +#endif + if (updatemap) { if (ntfs_attr_update_mapping_pairs(na, (update_from < 0 ? 0 : update_from))) { /* @@ -2058,6 +2230,10 @@ done: total = -1; goto out; } + if (!wasnonresident) + NAttrClearBeingNonResident(na); + NAttrClearDataAppending(na); + } out: ntfs_log_leave("\n"); return total; @@ -2119,7 +2295,8 @@ err_out: if (updatemap) ntfs_attr_update_mapping_pairs(na, 0); /* Restore original data_size if needed. */ - if (need_to.undo_data_size && ntfs_attr_truncate(na, old_data_size)) + if (need_to.undo_data_size + && ntfs_attr_truncate_i(na, old_data_size, HOLES_OK)) ntfs_log_perror("Failed to restore data_size"); errno = eo; errno_set: @@ -2137,7 +2314,6 @@ int ntfs_attr_pclose(ntfs_attr *na) ntfs_attr_search_ctx *ctx = NULL; runlist_element *rl; int eo; - s64 hole; int compressed_part; BOOL compressed; @@ -2249,7 +2425,6 @@ int ntfs_attr_pclose(ntfs_attr *na) goto rl_err_out; } if (rl->lcn < (LCN)0) { - hole = rl->vcn + rl->length; if (rl->lcn != (LCN)LCN_HOLE) { errno = EIO; ntfs_log_perror("%s: Unexpected LCN (%lld)", @@ -2302,6 +2477,7 @@ retry: goto out; } out: + NAttrClearComprClosing(na); ntfs_log_leave("\n"); return (!ok); rl_err_out: @@ -2358,6 +2534,7 @@ s64 ntfs_attr_mst_pread(ntfs_attr *na, const s64 pos, const s64 bk_cnt, { s64 br; u8 *end; + BOOL warn; ntfs_log_trace("Entering for inode 0x%llx, attr type 0x%x, pos 0x%llx.\n", (unsigned long long)na->ni->mft_no, na->type, @@ -2371,9 +2548,11 @@ s64 ntfs_attr_mst_pread(ntfs_attr *na, const s64 pos, const s64 bk_cnt, if (br <= 0) return br; br /= bk_size; + /* log errors unless silenced */ + warn = !na->ni || !na->ni->vol || !NVolNoFixupWarn(na->ni->vol); for (end = (u8*)dst + br * bk_size; (u8*)dst < end; dst = (u8*)dst + bk_size) - ntfs_mst_post_read_fixup((NTFS_RECORD*)dst, bk_size); + ntfs_mst_post_read_fixup_warn((NTFS_RECORD*)dst, bk_size, warn); /* Finally, return the number of blocks read. */ return br; } @@ -3371,6 +3550,14 @@ int ntfs_attr_size_bounds_check(const ntfs_volume *vol, const ATTR_TYPES type, min_size = sle64_to_cpu(ad->min_size); max_size = sle64_to_cpu(ad->max_size); + + /* The $AttrDef generated by Windows specifies 2 as min_size for the + * volume name attribute, but in reality Windows sets it to 0 when + * clearing the volume name. If we want to be able to clear the volume + * name we must also accept 0 as min_size, despite the $AttrDef + * definition. */ + if(type == AT_VOLUME_NAME) + min_size = 0; if ((min_size && (size < min_size)) || ((max_size > 0) && (size > max_size))) { @@ -4123,7 +4310,7 @@ add_non_resident: goto rm_attr_err_out; } /* Resize and set attribute value. */ - if (ntfs_attr_truncate(na, size) || + if (ntfs_attr_truncate_i(na, size, HOLES_OK) || (val && (ntfs_attr_pwrite(na, 0, size, val) != size))) { err = errno; ntfs_log_perror("Failed to initialize just added attribute"); @@ -4704,6 +4891,7 @@ cluster_free_err_out: ntfs_log_trace("Eeek! Failed to release allocated clusters in error " "code path. Leaving inconsistent metadata...\n"); NAttrClearNonResident(na); + NAttrClearFullyMapped(na); na->allocated_size = na->data_size; na->rl = NULL; free(rl); @@ -4827,7 +5015,7 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize, return (ret); } /* Resize non-resident attribute */ - return ntfs_attr_truncate(na, newsize); + return ntfs_attr_truncate_i(na, newsize, HOLES_OK); } else if (errno != ENOSPC && errno != EPERM) { err = errno; ntfs_log_perror("Failed to make attribute non-resident"); @@ -4864,6 +5052,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)) { @@ -5166,7 +5363,6 @@ static int ntfs_attr_make_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx) * record is in a transiently corrupted state at this moment in time. */ if (ntfs_cluster_free(vol, na, 0, -1) < 0) { - err = errno; ntfs_log_perror("Eeek! Failed to release allocated clusters"); ntfs_log_trace("Ignoring error and leaving behind wasted " "clusters.\n"); @@ -5178,6 +5374,7 @@ static int ntfs_attr_make_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx) /* Update in-memory struct ntfs_attr. */ NAttrClearNonResident(na); + NAttrClearFullyMapped(na); NAttrClearSparse(na); NAttrClearEncrypted(na); na->initialized_size = na->data_size; @@ -5192,7 +5389,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; @@ -5204,15 +5401,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 (holes == HOLES_DELAY) + 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 ((holes != HOLES_DELAY) + && 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 +5448,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. */ @@ -5264,6 +5468,7 @@ static int ntfs_attr_update_meta(ATTR_RECORD *a, ntfs_attr *na, MFT_RECORD *m, NAttrClearSparse(na); a->flags &= ~ATTR_IS_SPARSE; + na->data_flags = a->flags; a->compression_unit = 0; memmove((u8*)a + le16_to_cpu(a->name_offset) - 8, @@ -5278,7 +5483,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); @@ -5310,7 +5516,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; @@ -5338,6 +5545,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 ((holes != HOLES_DELAY) + && (!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 @@ -5399,7 +5659,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; @@ -5667,7 +5927,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; } @@ -5837,7 +6097,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; @@ -5864,6 +6125,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,16 +6137,33 @@ 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 * 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; @@ -5953,8 +6233,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_i(na, start_update, holes)) { +#else + if (ntfs_attr_update_mapping_pairs(na, 0)) { +#endif err = errno; ntfs_log_perror("Mapping pairs update failed"); goto rollback; @@ -6040,12 +6323,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; } @@ -6054,6 +6338,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 @@ -6070,7 +6355,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; @@ -6135,7 +6421,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 @@ -6144,7 +6431,30 @@ 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) +{ + int r; + + r = ntfs_attr_truncate_i(na, newsize, HOLES_OK); + NAttrClearDataAppending(na); + NAttrClearBeingNonResident(na); + return (r); +} + +/* + * 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 * @@ -6205,7 +6515,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 @@ -6285,6 +6595,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/src/add-ons/kernel/file_systems/ntfs/libntfs/attrib.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/attrib.h index 43ab7f53cc..67fb957d05 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/attrib.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/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 @@ -57,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 @@ -201,6 +208,7 @@ 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_ComprClosing, /* 1: Compressed attribute is being closed */ } ntfs_attr_state_bits; @@ -224,6 +232,10 @@ 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 NAttrComprClosing(na) test_nattr_flag(na, ComprClosing) #define NAttrSetComprClosing(na) set_nattr_flag(na, ComprClosing) #define NAttrClearComprClosing(na) clear_nattr_flag(na, ComprClosing) @@ -332,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 @@ -370,6 +383,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/src/add-ons/kernel/file_systems/ntfs/libntfs/cache.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/cache.c index dd147672c0..2ad8d359e1 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/cache.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/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, @@ -376,7 +376,6 @@ int ntfs_invalidate_cache(struct CACHE_HEADER *cache, int flags) { struct CACHED_GENERIC *current; - struct CACHED_GENERIC *previous; struct CACHED_GENERIC *next; struct HASH_ENTRY *link; int count; @@ -412,7 +411,6 @@ int ntfs_invalidate_cache(struct CACHE_HEADER *cache, * Search sequentially in LRU list */ current = cache->most_recent_entry; - previous = (struct CACHED_GENERIC*)NULL; while (current) { if (!compare(current, item)) { next = current->next; @@ -423,7 +421,6 @@ int ntfs_invalidate_cache(struct CACHE_HEADER *cache, current = next; count++; } else { - previous = current; current = current->next; } } diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/cache.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/cache.h index 67e4f9da41..be63b1a4bd 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/cache.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/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/src/add-ons/kernel/file_systems/ntfs/libntfs/compress.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/compress.c index fbd30ba9bb..aeb082d5e8 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/compress.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/compress.c @@ -5,7 +5,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 @@ -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 */ @@ -77,259 +81,215 @@ typedef enum { NTFS_SB_IS_COMPRESSED = 0x8000, } ntfs_compression_constants; -#define THRESHOLD 3 /* minimal match length for compression */ -#define NIL NTFS_SB_SIZE /* End of tree's node */ - struct COMPRESS_CONTEXT { const unsigned char *inbuf; - unsigned int len; - unsigned int nbt; - int match_position; - unsigned int match_length; - u16 lson[NTFS_SB_SIZE + 1]; - u16 rson[NTFS_SB_SIZE + 257]; - u16 dad[NTFS_SB_SIZE + 1]; + int bufsize; + int size; + int rel; + int mxsz; + s16 head[256]; + s16 lson[NTFS_SB_SIZE]; + s16 rson[NTFS_SB_SIZE]; } ; /* - * Initialize the match tree - */ - -static void ntfs_init_compress_tree(struct COMPRESS_CONTEXT *pctx) -{ - int i; - - for (i = NTFS_SB_SIZE + 1; i <= NTFS_SB_SIZE + 256; i++) - pctx->rson[i] = NIL; /* root */ - for (i = 0; i < NTFS_SB_SIZE; i++) - pctx->dad[i] = NIL; /* node */ -} - -/* - * Insert a new node into match tree for quickly locating - * further similar strings - */ - -static void ntfs_new_node (struct COMPRESS_CONTEXT *pctx, - unsigned int r) -{ - unsigned int pp; - BOOL less; - BOOL done; - const unsigned char *key; - int c; - unsigned long mxi; - unsigned int mxl; - - mxl = (1 << (16 - pctx->nbt)) + 2; - less = FALSE; - done = FALSE; - key = &pctx->inbuf[r]; - pp = NTFS_SB_SIZE + 1 + key[0]; - pctx->rson[r] = pctx->lson[r] = NIL; - pctx->match_length = 0; - do { - if (!less) { - if (pctx->rson[pp] != NIL) - pp = pctx->rson[pp]; - else { - pctx->rson[pp] = r; - pctx->dad[r] = pp; - done = TRUE; - } - } else { - if (pctx->lson[pp] != NIL) - pp = pctx->lson[pp]; - else { - pctx->lson[pp] = r; - pctx->dad[r] = pp; - done = TRUE; - } - } - if (!done) { - register unsigned long i; - register const unsigned char *p1,*p2; - - i = 1; - mxi = NTFS_SB_SIZE - r; - if (mxi < 2) - less = FALSE; - else { - p1 = key; - p2 = &pctx->inbuf[pp]; - /* this loop has a significant impact on performances */ - do { - } while ((p1[i] == p2[i]) && (++i < mxi)); - less = (i < mxi) && (p1[i] < p2[i]); - } - if (i >= THRESHOLD) { - if (i > pctx->match_length) { - pctx->match_position = - r - pp + 2*NTFS_SB_SIZE - 1; - if ((pctx->match_length = i) > mxl) { - i = pctx->rson[pp]; - pctx->rson[r] = i; - pctx->dad[i] = r; - i = pctx->lson[pp]; - pctx->lson[r] = i; - pctx->dad[i] = r; - i = pctx->dad[pp]; - pctx->dad[r] = i; - if (pctx->rson[i] == pp) - pctx->rson[i] = r; - else - pctx->lson[i] = r; - /* remove pp */ - pctx->dad[pp] = NIL; - done = TRUE; - pctx->match_length = mxl; - } - } else - if ((i == pctx->match_length) - && ((c = (r - pp + 2*NTFS_SB_SIZE - 1)) - < pctx->match_position)) - pctx->match_position = c; - } - } - } while (!done); -} - -/* - * Search for the longest previous string matching the - * current one + * Search for the longest sequence matching current position * - * Returns the end of the longest current string which matched - * or zero if there was a bug + * A binary tree is maintained to locate all previously met sequences, + * and this function has to be called for all of them. + * + * This function is heavily used, it has to be optimized carefully + * + * Returns the size of the longest match, + * zero if no match is found. */ -static unsigned int ntfs_nextmatch(struct COMPRESS_CONTEXT *pctx, - unsigned int rr, int dd) +static int ntfs_best_match(struct COMPRESS_CONTEXT *pctx, int i) { - unsigned int bestlen = 0; + s16 *prev; + int node; + register long j; + long maxpos; + long startj; + long bestj; + int bufsize; + int bestnode; + register const unsigned char *p1,*p2; - do { - rr++; - if (pctx->match_length > 0) - pctx->match_length--; - if (!pctx->len) { - ntfs_log_error("compress bug : void run\n"); - goto bug; - } - if (--pctx->len) { - if (rr >= NTFS_SB_SIZE) { - ntfs_log_error("compress bug : buffer overflow\n"); - goto bug; - } - if (((rr + bestlen) < NTFS_SB_SIZE)) { - while ((unsigned int)(1 << pctx->nbt) - <= (rr - 1)) - pctx->nbt++; - ntfs_new_node(pctx,rr); - if (pctx->match_length > bestlen) - bestlen = pctx->match_length; - } else - if (dd > 0) { - rr += dd; - if ((int)pctx->match_length > dd) - pctx->match_length -= dd; - else - pctx->match_length = 0; - if ((int)pctx->len < dd) { - ntfs_log_error("compress bug : run overflows\n"); - goto bug; + p1 = pctx->inbuf; + node = pctx->head[p1[i] & 255]; + if (node >= 0) { + /* search the best match at current position */ + bestnode = node; + bufsize = pctx->bufsize; + /* restrict matches to the longest allowed sequence */ + maxpos = bufsize; + if ((i + pctx->mxsz) < maxpos) + maxpos = i + pctx->mxsz; + startj = i + 1 - maxpos; + bestj = startj; + /* make indexes relative to end of allowed position */ + p1 = &p1[maxpos]; + if (startj < 0) { + do { + /* indexes are negative */ + p2 = &p1[node - i]; + /* no need to compare the first byte */ + j = startj; + /* the second byte cannot lead to useful compression */ + if (p1[j] == p2[j]) { + j++; + if (j < 0) { + do { + } while ((p1[j] == p2[j]) + && (++j < 0)); + } + /* remember the match, if better */ + if (j > bestj) { + bestj = j; + bestnode = node; } - pctx->len -= dd; - dd = 0; } + /* walk in the tree in the right direction */ + if ((j < 0) && (p1[j] < p2[j])) + prev = &pctx->lson[node]; + else + prev = &pctx->rson[node]; + node = *prev; + /* stop if reaching a leaf or maximum length */ + } while ((node >= 0) && (j < 0)); + /* put the node into the tree if we reached a leaf */ + if (node < 0) + *prev = i; } - } while (dd-- > 0); - return (rr); -bug : - return (0); + /* done, return the best match */ + pctx->size = bestj + maxpos - i; + pctx->rel = bestnode - i; + } else { + pctx->head[p1[i] & 255] = i; + pctx->size = 0; + pctx->rel = 0; + } + return (pctx->size); } /* - * Compress an input block + * Compress a 4096-byte block * - * Returns the size of the compressed block (including header) - * or zero if there was an error + * Returns a header of two bytes followed by the compressed data. + * If compression is not effective, the header and an uncompressed + * block is returned. + * + * Note : two bytes may be output before output buffer overflow + * is detected, so a 4100-bytes output buffer must be reserved. + * + * Returns the size of the compressed block, including the + * header (minimal size is 2, maximum size is 4098) + * 0 if an error has been met. */ -static unsigned int ntfs_compress_block(const char *inbuf, - unsigned int size, char *outbuf) +static unsigned int ntfs_compress_block(const char *inbuf, int bufsize, + char *outbuf) { struct COMPRESS_CONTEXT *pctx; - char *ptag; - int dd; - unsigned int rr; - unsigned int last_match_length; - unsigned int q; + int i; /* current position */ + int j; /* end of best match from current position */ + int k; /* end of best match from next position */ + int offs; /* offset to best match */ + int n; + int bp; /* bits to store offset */ + int mxoff; /* max match offset : 1 << bp */ + int mxsz2; unsigned int xout; - unsigned int ntag; + unsigned int q; /* aggregated offset and size */ + int done; + char *ptag; /* location reserved for a tag */ + int tag; /* current value of tag */ + int ntag; /* count of bits still undefined in tag */ - pctx = (struct COMPRESS_CONTEXT*)malloc(sizeof(struct COMPRESS_CONTEXT)); + pctx = (struct COMPRESS_CONTEXT*)ntfs_malloc(sizeof(struct COMPRESS_CONTEXT)); if (pctx) { + for (n=0; nlson[n] = pctx->rson[n] = -1; + for (n=0; n<256; n++) + pctx->head[n] = -1; pctx->inbuf = (const unsigned char*)inbuf; - ntfs_init_compress_tree(pctx); + pctx->bufsize = bufsize; xout = 2; - ntag = 0; + n = 0; + i = 0; + bp = 4; + mxoff = 1 << bp; + pctx->mxsz = (1 << (16 - bp)) + 2; + tag = 0; + done = -1; + ntag = 8; ptag = &outbuf[xout++]; - *ptag = 0; - rr = 0; - pctx->nbt = 4; - pctx->len = size; - pctx->match_length = 0; - ntfs_new_node(pctx,0); - do { - if (pctx->match_length > pctx->len) - pctx->match_length = pctx->len; - if (pctx->match_length < THRESHOLD) { - pctx->match_length = 1; - if (ntag >= 8) { - ntag = 0; - ptag = &outbuf[xout++]; - *ptag = 0; + while ((i < bufsize) && (xout < (NTFS_SB_SIZE + 2))) { + /* adjust the longest match we can output */ + while (mxoff < i) { + bp++; + mxoff <<= 1; + pctx->mxsz = (pctx->mxsz + 2) >> 1; + } + /* search the best match at current position */ + if (done < i) + do { + ntfs_best_match(pctx,++done); + } while (done < i); + j = i + pctx->size; + if ((j - i) > pctx->mxsz) + j = i + pctx->mxsz; + + if ((j - i) > 2) { + offs = pctx->rel; + /* check whether there is a better run at i+1 */ + ntfs_best_match(pctx,i+1); + done = i+1; + k = i + 1 + pctx->size; + mxsz2 = pctx->mxsz; + if (mxoff <= i) + mxsz2 = (pctx->mxsz + 2) >> 1; + if ((k - i) > mxsz2) + k = i + mxsz2; + if (k > (j + 1)) { + /* issue a single byte */ + outbuf[xout++] = inbuf[i]; + i++; + } else { + q = (~offs << (16 - bp)) + + (j - i - 3); + outbuf[xout++] = q & 255; + outbuf[xout++] = (q >> 8) & 255; + tag |= (1 << (8 - ntag)); + i = j; } - outbuf[xout++] = inbuf[rr]; - ntag++; } else { - while ((unsigned int)(1 << pctx->nbt) - <= (rr - 1)) - pctx->nbt++; - q = (pctx->match_position << (16 - pctx->nbt)) - + pctx->match_length - THRESHOLD; - if (ntag >= 8) { - ntag = 0; - ptag = &outbuf[xout++]; - *ptag = 0; - } - *ptag |= 1 << ntag++; - outbuf[xout++] = q & 255; - outbuf[xout++] = (q >> 8) & 255; + outbuf[xout++] = inbuf[i]; + i++; } - last_match_length = pctx->match_length; - dd = last_match_length; - if (dd-- > 0) { - rr = ntfs_nextmatch(pctx,rr,dd); - if (!rr) - goto bug; + /* store the tag if fully used */ + if (!--ntag) { + *ptag = tag; + ntag = 8; + ptag = &outbuf[xout++]; + tag = 0; } - /* - * stop if input is exhausted or output has exceeded - * the maximum size. Two extra bytes have to be - * reserved in output buffer, as 3 bytes may be - * output in a loop. - */ - } while ((pctx->len > 0) - && (rr < size) && (xout < (NTFS_SB_SIZE + 2))); - /* uncompressed must be full size, so accept if better */ - if (xout < (NTFS_SB_SIZE + 2)) { + } + /* store the last tag, if partially used */ + if (ntag == 8) + xout--; + else + *ptag = tag; + /* uncompressed must be full size, accept if better */ + if ((i >= bufsize) && (xout < (NTFS_SB_SIZE + 2))) { outbuf[0] = (xout - 3) & 255; outbuf[1] = 0xb0 + (((xout - 3) >> 8) & 15); } else { - memcpy(&outbuf[2],inbuf,size); - if (size < NTFS_SB_SIZE) - memset(&outbuf[size+2],0,NTFS_SB_SIZE - size); + memcpy(&outbuf[2],inbuf,bufsize); + if (bufsize < NTFS_SB_SIZE) + memset(&outbuf[bufsize+2], 0, + NTFS_SB_SIZE - bufsize); outbuf[0] = 0xff; outbuf[1] = 0x3f; xout = NTFS_SB_SIZE + 2; @@ -339,9 +299,7 @@ static unsigned int ntfs_compress_block(const char *inbuf, xout = 0; errno = ENOMEM; } - return (xout); /* 0 for an error, > size if cannot compress */ -bug : - return (0); + return (xout); } /** @@ -1247,6 +1205,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; @@ -1507,12 +1466,12 @@ static int ntfs_read_append(ntfs_attr *na, const runlist_element *rl, * or -1 if there were an irrecoverable error (errno set) */ -static int ntfs_flush(ntfs_attr *na, runlist_element *rl, s64 offs, +static s32 ntfs_flush(ntfs_attr *na, runlist_element *rl, s64 offs, const char *outbuf, s32 count, BOOL compress, BOOL appending, VCN *update_from) { - int rounded; - int written; + s32 rounded; + s32 written; int clsz; if (compress) { @@ -1650,7 +1609,7 @@ s64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *wrl, s64 wpos, * (we are reopening an existing file to append to it) * Decompress the data and append */ - compsz = compressed_part << vol->cluster_size_bits; + compsz = (s32)compressed_part << vol->cluster_size_bits; outbuf = (char*)ntfs_malloc(na->compression_block_size); if (outbuf) { if (appending) { diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/debug.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/debug.h index cf39b62570..f7f3c6fb4e 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/debug.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/debug.h @@ -38,10 +38,10 @@ static __inline__ void ntfs_debug_runlist_dump(const struct _runlist_element *rl #define NTFS_BUG(msg) \ { \ - int ___i; \ + int ___i = 1; \ ntfs_log_critical("Bug in %s(): %s\n", __FUNCTION__, msg); \ ntfs_log_debug("Forcing segmentation fault!"); \ - ___i = ((int*)NULL)[1]; \ + ___i = ((int*)NULL)[___i]; \ } #endif /* defined _NTFS_DEBUG_H */ diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/device.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/device.c index c77d8f9576..274abacb42 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/device.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/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 @@ -57,6 +58,9 @@ #ifdef HAVE_SYS_MOUNT_H #include #endif +#ifdef HAVE_SYS_DISK_H +#include +#endif #ifdef HAVE_LINUX_FD_H #include #endif @@ -154,6 +158,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 +283,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; @@ -533,6 +559,36 @@ s64 ntfs_device_size_get(struct ntfs_device *dev, int block_size) return (s64)this_floppy.size * 512 / block_size; } } +#endif +#ifdef DIOCGMEDIASIZE + { + /* FreeBSD */ + off_t size; + + if (dev->d_ops->ioctl(dev, DIOCGMEDIASIZE, &size) >= 0) { + ntfs_log_debug("DIOCGMEDIASIZE nr bytes = %llu (0x%llx)\n", + (unsigned long long)size, + (unsigned long long)size); + return (s64)size / block_size; + } + } +#endif +#ifdef DKIOCGETBLOCKCOUNT + { + /* Mac OS X */ + uint64_t blocks; + int sector_size; + + sector_size = ntfs_device_sector_size_get(dev); + if (sector_size >= 0 && dev->d_ops->ioctl(dev, + DKIOCGETBLOCKCOUNT, &blocks) >= 0) + { + ntfs_log_debug("DKIOCGETBLOCKCOUNT nr blocks = %llu (0x%llx)\n", + (unsigned long long) blocks, + (unsigned long long) blocks); + return blocks * sector_size / block_size; + } + } #endif /* * We couldn't figure it out by using a specialized ioctl, @@ -682,6 +738,28 @@ int ntfs_device_sector_size_get(struct ntfs_device *dev) return sect_size; } } +#elif defined(DIOCGSECTORSIZE) + { + /* FreeBSD */ + size_t sect_size = 0; + + if (!dev->d_ops->ioctl(dev, DIOCGSECTORSIZE, §_size)) { + ntfs_log_debug("DIOCGSECTORSIZE sector size = %d bytes\n", + (int) sect_size); + return sect_size; + } + } +#elif defined(DKIOCGETBLOCKSIZE) + { + /* Mac OS X */ + uint32_t sect_size = 0; + + if (!dev->d_ops->ioctl(dev, DKIOCGETBLOCKSIZE, §_size)) { + ntfs_log_debug("DKIOCGETBLOCKSIZE sector size = %d bytes\n", + (int) sect_size); + return sect_size; + } + } #else errno = EOPNOTSUPP; #endif diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/device.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/device.h index a19d29c48b..ad34ac565c 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/device.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/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/src/add-ons/kernel/file_systems/ntfs/libntfs/device_io.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/device_io.c deleted file mode 100644 index fc361b539b..0000000000 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/device_io.c +++ /dev/null @@ -1,38 +0,0 @@ -/* - * device_io.c - Default device io operations. Originated from the Linux-NTFS project. - * - * Copyright (c) 2003 Anton Altaparmakov - * - * This program/include file is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program/include file is distributed in the hope that it will be - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program (in the main directory of the NTFS-3G - * distribution in the file COPYING); if not, write to the Free Software - * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "config.h" - -#ifndef NO_NTFS_DEVICE_DEFAULT_IO_OPS - -#ifndef __CYGWIN32__ - -/* Not on Cygwin; use standard Unix style low level device operations. */ -#include "unix_io.c" - -#else /* __CYGWIN32__ */ - -/* On Cygwin; use Win32 low level device operations. */ -#include "win32_io.c" - -#endif /* __CYGWIN32__ */ - -#endif /* NO_NTFS_DEVICE_DEFAULT_IO_OPS */ diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/dir.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/dir.c index 2372f3f84f..ccae47c327 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/dir.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/dir.c @@ -257,7 +257,7 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, u8 *index_end; ntfs_attr *ia_na; int eo, rc; - u32 index_block_size, index_vcn_size; + u32 index_block_size; u8 index_vcn_size_bits; ntfs_log_trace("Entering\n"); @@ -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. */ @@ -378,11 +378,9 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, /* Determine the size of a vcn in the directory index. */ if (vol->cluster_size <= index_block_size) { - index_vcn_size = vol->cluster_size; index_vcn_size_bits = vol->cluster_size_bits; } else { - index_vcn_size = vol->sector_size; - index_vcn_size_bits = vol->sector_size_bits; + index_vcn_size_bits = NTFS_BLOCK_SIZE_BITS; } /* Get the starting vcn of the index_block holding the child node. */ @@ -1039,7 +1037,7 @@ int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, INDEX_ENTRY *ie; INDEX_ALLOCATION *ia = NULL; int rc, ir_pos, bmp_buf_size, bmp_buf_pos, eo; - u32 index_block_size, index_vcn_size; + u32 index_block_size; u8 index_block_size_bits, index_vcn_size_bits; ntfs_log_trace("Entering.\n"); @@ -1131,11 +1129,9 @@ int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, } index_block_size_bits = ffs(index_block_size) - 1; if (vol->cluster_size <= index_block_size) { - index_vcn_size = vol->cluster_size; index_vcn_size_bits = vol->cluster_size_bits; } else { - index_vcn_size = vol->sector_size; - index_vcn_size_bits = vol->sector_size_bits; + index_vcn_size_bits = NTFS_BLOCK_SIZE_BITS; } /* Are we jumping straight into the index allocation attribute? */ @@ -1517,7 +1513,7 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, else ir->clusters_per_index_block = ni->vol->indx_record_size >> - ni->vol->sector_size_bits; + NTFS_BLOCK_SIZE_BITS; ir->index.entries_offset = cpu_to_le32(sizeof(INDEX_HEADER)); ir->index.index_length = cpu_to_le32(index_len); ir->index.allocated_size = cpu_to_le32(index_len); @@ -1812,7 +1808,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 +1840,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, @@ -2507,24 +2504,24 @@ int ntfs_set_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, int res = 0; int longlen = 0; int shortlen = 0; - char newname[MAX_DOS_NAME_LENGTH + 1]; + char newname[3*MAX_DOS_NAME_LENGTH + 1]; ntfschar oldname[MAX_DOS_NAME_LENGTH]; int oldlen; - ntfs_volume *vol; - u64 fnum; u64 dnum; BOOL closed = FALSE; ntfschar *shortname = NULL; ntfschar longname[NTFS_MAX_NAME_LEN]; - vol = ni->vol; - fnum = ni->mft_no; - /* convert the string to the NTFS wide chars */ - if (size > MAX_DOS_NAME_LENGTH) - size = MAX_DOS_NAME_LENGTH; + /* copy the string to insert a null char, and truncate */ + if (size > 3*MAX_DOS_NAME_LENGTH) + size = 3*MAX_DOS_NAME_LENGTH; strncpy(newname, value, size); + /* a long name may be truncated badly and be untranslatable */ newname[size] = 0; + /* convert the string to the NTFS wide chars, and truncate */ shortlen = ntfs_mbstoucs(newname, &shortname); + if (shortlen > MAX_DOS_NAME_LENGTH) + shortlen = MAX_DOS_NAME_LENGTH; /* make sure the short name has valid chars */ if ((shortlen < 0) || ntfs_forbidden_chars(shortname,shortlen)) { ntfs_inode_close_in_dir(ni,dir_ni); diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/efs.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/efs.c index 6ccec20aed..7957005b6b 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/efs.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/efs.c @@ -139,7 +139,6 @@ static int fixup_loop(ntfs_inode *ni) ntfs_attr *na; ATTR_RECORD *a; BOOL restart; - BOOL first; int cnt; int maxcnt; int res = 0; @@ -200,7 +199,6 @@ static int fixup_loop(ntfs_inode *ni) if (na) ntfs_attr_close(na); } - first = FALSE; } while (restart && !res); if (ctx) ntfs_attr_put_search_ctx(ctx); diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/index.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/index.c index 7df0deec20..d498dde4b6 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/index.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/index.c @@ -701,7 +701,7 @@ int ntfs_index_lookup(const void *key, const int key_len, ntfs_index_context *ic if (ni->vol->cluster_size <= icx->block_size) icx->vcn_size_bits = ni->vol->cluster_size_bits; else - icx->vcn_size_bits = ni->vol->sector_size_bits; + icx->vcn_size_bits = NTFS_BLOCK_SIZE_BITS; /* get the appropriate collation function */ icx->collate = ntfs_get_collate_function(ir->collation_rule); if (!icx->collate) { @@ -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/src/add-ons/kernel/file_systems/ntfs/libntfs/inode.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/inode.c index 6f3fa0604a..a4a01348be 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/inode.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/inode.c @@ -437,13 +437,12 @@ static int idata_cache_compare(const struct CACHED_GENERIC *cached, void ntfs_inode_invalidate(ntfs_volume *vol, const MFT_REF mref) { struct CACHED_NIDATA item; - int count; item.inum = MREF(mref); item.ni = (ntfs_inode*)NULL; item.pathname = (const char*)NULL; item.varsize = 0; - count = ntfs_invalidate_cache(vol->nidata_cache, + ntfs_invalidate_cache(vol->nidata_cache, GENERIC(&item),idata_cache_compare,CACHE_FREE); } @@ -481,6 +480,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 @@ -571,6 +573,9 @@ int ntfs_inode_close(ntfs_inode *ni) ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni, const MFT_REF mref) { u64 mft_no = MREF_LE(mref); + VCN extent_vcn; + runlist_element *rl; + ntfs_volume *vol; ntfs_inode *ni = NULL; ntfs_inode **extent_nis; int i; @@ -585,6 +590,37 @@ ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni, const MFT_REF mref) (unsigned long long)mft_no, (unsigned long long)base_ni->mft_no); + if (!base_ni->mft_no) { + /* + * When getting extents of MFT, we must be sure + * they are in the MFT part which has already + * been mapped, otherwise we fall into an endless + * recursion. + * Situations have been met where extents locations + * are described in themselves. + * This is a severe error which chkdsk cannot fix. + */ + vol = base_ni->vol; + extent_vcn = mft_no << vol->mft_record_size_bits + >> vol->cluster_size_bits; + rl = vol->mft_na->rl; + if (rl) { + while (rl->length + && ((rl->vcn + rl->length) <= extent_vcn)) + rl++; + } + if (!rl || (rl->lcn < 0)) { + ntfs_log_error("MFT is corrupt, cannot read" + " its unmapped extent record %lld\n", + (long long)mft_no); + ntfs_log_error("Note : chkdsk cannot fix this," + " try ntfsfix\n"); + errno = EIO; + ni = (ntfs_inode*)NULL; + goto out; + } + } + /* Is the extent inode already open and attached to the base inode? */ if (base_ni->nr_extents > 0) { extent_nis = base_ni->extent_nis; @@ -842,6 +878,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/src/add-ons/kernel/file_systems/ntfs/libntfs/layout.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/layout.h index 8670557e16..c167135fab 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/layout.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/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))) @@ -2222,11 +2223,11 @@ typedef struct { /* The below field is NOT present for the quota defaults entry. */ SID sid; /* The SID of the user/object associated with this quota entry. If this field is missing - then the INDEX_ENTRY is padded with zeros - to multiply of 8 which are not counted in + then the INDEX_ENTRY is padded to a multiple + of 8 with zeros which are not counted in the data_length field. If the sid is present then this structure is padded with zeros to - multiply of 8 and the padding is counted in + a multiple of 8 and the padding is counted in the INDEX_ENTRY's data_length. */ } __attribute__((__packed__)) QUOTA_CONTROL_ENTRY; diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/logfile.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/logfile.c index 277ad1424b..a4f00d5226 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/logfile.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/logfile.c @@ -468,7 +468,7 @@ BOOL ntfs_check_logfile(ntfs_attr *log_na, RESTART_PAGE_HEADER **rp) u8 *kaddr = NULL; RESTART_PAGE_HEADER *rstr1_ph = NULL; RESTART_PAGE_HEADER *rstr2_ph = NULL; - int log_page_size, log_page_mask, err; + int log_page_size, err; BOOL logfile_is_empty = TRUE; u8 log_page_bits; @@ -481,7 +481,6 @@ BOOL ntfs_check_logfile(ntfs_attr *log_na, RESTART_PAGE_HEADER **rp) if (size > (s64)MaxLogFileSize) size = MaxLogFileSize; log_page_size = DefaultLogPageSize; - log_page_mask = log_page_size - 1; /* * Use generic_ffs() instead of ffs() to enable the compiler to * optimize log_page_size and log_page_bits into constants. diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/logging.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/logging.c index 02d98bc1a9..af672dc066 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/logging.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/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 @@ -297,7 +298,6 @@ void ntfs_log_set_handler(ntfs_log_handler *handler) ntfs_log.handler = ntfs_log_handler_null; } - /** * ntfs_log_redirect - Pass on the request to the real handler * @function: Function in which the log line occurred @@ -390,6 +390,31 @@ 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, ...) +{ +#ifndef __HAIKU__ + 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); +#endif +} + #ifndef __HAIKU__ /** * ntfs_log_handler_fprintf - Basic logging handler @@ -586,7 +611,6 @@ int ntfs_log_handler_stderr(const char *function, const char *file, #endif // __HAIKU__ - /** * ntfs_log_parse_option - Act upon command line options * @option: Option flag diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/logging.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/logging.h index dcf40f7082..c68cd4c2d9 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/logging.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/logging.h @@ -116,5 +116,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/src/add-ons/kernel/file_systems/ntfs/libntfs/mft.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/mft.c index e93c66465c..0640efe973 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/mft.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/mft.c @@ -216,8 +216,10 @@ int ntfs_mft_record_check(const ntfs_volume *vol, const MFT_REF mref, int ret = -1; if (!ntfs_is_file_record(m->magic)) { - ntfs_log_error("Record %llu has no FILE magic (0x%x)\n", - (unsigned long long)MREF(mref), *(le32 *)m); + if (!NVolNoFixupWarn(vol)) + ntfs_log_error("Record %llu has no FILE magic (0x%x)\n", + (unsigned long long)MREF(mref), + (int)le32_to_cpu(*(le32*)m)); goto err_out; } @@ -1190,7 +1192,7 @@ undo_alloc: static int ntfs_mft_record_init(ntfs_volume *vol, s64 size) { int ret = -1; - ntfs_attr *mft_na, *mftbmp_na; + ntfs_attr *mft_na; s64 old_data_initialized, old_data_size; ntfs_attr_search_ctx *ctx; @@ -1199,7 +1201,6 @@ static int ntfs_mft_record_init(ntfs_volume *vol, s64 size) /* NOTE: Caller must sanity check vol, vol->mft_na and vol->mftbmp_na */ mft_na = vol->mft_na; - mftbmp_na = vol->mftbmp_na; /* * The mft record is outside the initialized data. Extend the mft data @@ -1295,14 +1296,13 @@ undo_data_init: static int ntfs_mft_rec_init(ntfs_volume *vol, s64 size) { int ret = -1; - ntfs_attr *mft_na, *mftbmp_na; + ntfs_attr *mft_na; s64 old_data_initialized, old_data_size; ntfs_attr_search_ctx *ctx; ntfs_log_enter("Entering\n"); mft_na = vol->mft_na; - mftbmp_na = vol->mftbmp_na; if (size > mft_na->allocated_size || size > mft_na->initialized_size) { errno = EIO; diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/mst.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/mst.c index 470942d64b..b7937b7a41 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/mst.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/mst.c @@ -47,7 +47,8 @@ * EIO Multi sector transfer error was detected. Magic of the NTFS * record in @b will have been set to "BAAD". */ -int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size) +int ntfs_mst_post_read_fixup_warn(NTFS_RECORD *b, const u32 size, + BOOL warn) { u16 usa_ofs, usa_count, usn; u16 *usa_pos, *data_pos; @@ -63,9 +64,14 @@ int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size) (u32)(usa_ofs + (usa_count * 2)) > size || (size >> NTFS_BLOCK_SIZE_BITS) != usa_count) { errno = EINVAL; - ntfs_log_perror("%s: magic: 0x%08x size: %d usa_ofs: %d " - "usa_count: %d", __FUNCTION__, *(le32 *)b, - size, usa_ofs, usa_count); + if (warn) { + ntfs_log_perror("%s: magic: 0x%08lx size: %ld " + " usa_ofs: %d usa_count: %u", + __FUNCTION__, + (long)le32_to_cpu(*(le32 *)b), + (long)size, (int)usa_ofs, + (unsigned int)usa_count); + } return -1; } /* Position of usn in update sequence array. */ @@ -118,6 +124,16 @@ int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size) return 0; } +/* + * Deprotect multi sector transfer protected data + * with a warning if an error is found. + */ + +int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size) +{ + return (ntfs_mst_post_read_fixup_warn(b,size,TRUE)); +} + /** * ntfs_mst_pre_write_fixup - apply multi sector transfer protection * @b: pointer to the data to protect diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/mst.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/mst.h index ca81382165..d6ca6f2701 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/mst.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/mst.h @@ -25,8 +25,11 @@ #include "types.h" #include "layout.h" +#include "volume.h" extern int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size); +extern int ntfs_mst_post_read_fixup_warn(NTFS_RECORD *b, const u32 size, + BOOL warn); extern int ntfs_mst_pre_write_fixup(NTFS_RECORD *b, const u32 size); extern void ntfs_mst_post_write_fixup(NTFS_RECORD *b); diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/object_id.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/object_id.c index 555dd13773..059e882203 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/object_id.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/object_id.c @@ -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 { @@ -283,7 +287,6 @@ static int remove_object_id_index(ntfs_attr *na, ntfs_index_context *xo, if (size >= (s64)sizeof(GUID)) { memcpy(&key.object_id, &old_attr->object_id,sizeof(GUID)); - size = sizeof(GUID); if (!ntfs_index_lookup(&key, sizeof(OBJECT_ID_INDEX_KEY), xo)) { entry = (struct OBJECT_ID_INDEX*)xo->entry; @@ -296,7 +299,6 @@ static int remove_object_id_index(ntfs_attr *na, ntfs_index_context *xo, memcpy(&old_attr->domain_id, &entry->data.domain_id, sizeof(GUID)); - size = sizeof(OBJECT_ID_ATTR); if (ntfs_index_rm(xo)) ret = -1; } diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/param.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/param.h index f21bd653fa..5d96b1c29b 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/param.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/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 @@ -44,12 +44,44 @@ enum { */ /* default option for compression */ -#define DEFAULT_COMPRESSION FALSE +#define DEFAULT_COMPRESSION 1 /* (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 +/* + * Use of big write buffers + * + * With small volumes, the cluster allocator may fail to allocate + * enough clusters when the volume is nearly full. At most a run + * can be allocated per bitmap chunk. So, there is a danger when the + * number of chunks (capacity/(32768*clsiz)) is less than the number + * of clusters in the biggest write buffer (131072/clsiz). Hence + * a safe minimal capacity is 4GB + */ + +#define SAFE_CAPACITY_FOR_BIG_WRITES 0x100000000LL + +/* + * 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 */ + +/* + * Parameters for path canonicalization + */ + +#define MAPPERNAMELTH 256 + /* * Permission checking modes for high level and low level * @@ -58,7 +90,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 +100,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 +110,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/src/add-ons/kernel/file_systems/ntfs/libntfs/realpath.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/realpath.c new file mode 100644 index 0000000000..a93bc698ee --- /dev/null +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/realpath.c @@ -0,0 +1,103 @@ +/* + * realpath.c - realpath() aware of device mapper + * Originated from the util-linux project. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#ifdef HAVE_LIMITS_H +#include +#endif +#ifdef HAVE_CTYPE_H +#include +#endif + +#include "param.h" +#include "realpath.h" + +/* If there is no realpath() on the system, provide a dummy one. */ +#ifndef HAVE_REALPATH +char *ntfs_realpath(const char *path, char *resolved_path) +{ + strncpy(resolved_path, path, PATH_MAX); + resolved_path[PATH_MAX] = '\0'; + return resolved_path; +} +#endif + + +#ifdef linux + +/* + * Converts private "dm-N" names to "/dev/mapper/" + * + * Since 2.6.29 (patch 784aae735d9b0bba3f8b9faef4c8b30df3bf0128) kernel sysfs + * provides the real DM device names in /sys/block//dm/name + */ +static char * +canonicalize_dm_name(const char *ptname, char *canonical) +{ + FILE *f; + size_t sz; + char path[MAPPERNAMELTH + 24]; + char name[MAPPERNAMELTH + 16]; + char *res = NULL; + + snprintf(path, sizeof(path), "/sys/block/%s/dm/name", ptname); + if (!(f = fopen(path, "r"))) + return NULL; + + /* read "\n" from sysfs */ + if (fgets(name, sizeof(name), f) && (sz = strlen(name)) > 1) { + name[sz - 1] = '\0'; + snprintf(path, sizeof(path), "/dev/mapper/%s", name); + res = strcpy(canonical, path); + } + fclose(f); + return res; +} + +/* + * Canonicalize a device path + * + * Workaround from "basinilya" for fixing device mapper paths. + * + * Background (Phillip Susi, 2011-04-09) + * - ntfs-3g canonicalizes the device name so that if you mount with + * /dev/mapper/foo, the device name listed in mtab is /dev/dm-n, + * so you can not umount /dev/mapper/foo + * - umount won't even recognize and translate /dev/dm-n to the mount + * point, apparently because of the '-' involved. Editing mtab and + * removing the '-' allows you to umount /dev/dmn successfully. + * + * This code restores the devmapper name after canonicalization, + * until a proper fix is implemented. + */ + +char *ntfs_realpath_canonicalize(const char *path, char *canonical) +{ + char *p; + + if (path == NULL) + return NULL; + + if (!ntfs_realpath(path, canonical)) + return NULL; + + p = strrchr(canonical, '/'); + if (p && strncmp(p, "/dm-", 4) == 0 && isdigit(*(p + 4))) { + p = canonicalize_dm_name(p+1, canonical); + if (p) + return p; + } + + return canonical; +} + +#endif diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/realpath.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/realpath.h new file mode 100644 index 0000000000..970d2af8e8 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/realpath.h @@ -0,0 +1,24 @@ +/* + * realpath.h - realpath() aware of device mapper + */ + +#ifndef REALPATH_H +#define REALPATH_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_REALPATH +#define ntfs_realpath realpath +#else +extern char *ntfs_realpath(const char *path, char *resolved_path); +#endif + +#ifdef linux +extern char *ntfs_realpath_canonicalize(const char *path, char *resolved_path); +#else +#define ntfs_realpath_canonicalize ntfs_realpath +#endif + +#endif /* REALPATH_H */ diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/reparse.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/reparse.c index 0f6360e1e1..05490bf43f 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/reparse.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/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/src/add-ons/kernel/file_systems/ntfs/libntfs/runlist.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/runlist.c index cea246727d..7e158d443a 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/runlist.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/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 @@ -136,9 +136,10 @@ runlist_element *ntfs_rl_extend(ntfs_attr *na, runlist_element *rl, if (!newrl) { errno = ENOMEM; rl = (runlist_element*)NULL; - } else + } else { na->rl = newrl; rl = &newrl[irl]; + } } else { ntfs_log_error("Cannot extend unmapped runlist"); errno = EIO; @@ -1418,28 +1419,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: @@ -1633,7 +1624,7 @@ errno_set: int ntfs_rl_truncate(runlist **arl, const VCN start_vcn) { runlist *rl; - BOOL is_end = FALSE; + /* BOOL is_end = FALSE; */ if (!arl || !*arl) { errno = EINVAL; @@ -1676,8 +1667,10 @@ int ntfs_rl_truncate(runlist **arl, const VCN start_vcn) */ if (rl->length) { ++rl; +/* if (!rl->length) is_end = TRUE; +*/ rl->vcn = start_vcn; rl->length = 0; } @@ -1685,7 +1678,7 @@ int ntfs_rl_truncate(runlist **arl, const VCN start_vcn) /** * Reallocate memory if necessary. * FIXME: Below code is broken, because runlist allocations must be - * a multiply of 4096. The code caused crashes and corruptions. + * a multiple of 4096. The code caused crashes and corruptions. */ /* if (!is_end) { diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/security.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/security.c index dca3059cd7..7075ca212d 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/security.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/security.c @@ -4,7 +4,7 @@ * Copyright (c) 2004 Anton Altaparmakov * Copyright (c) 2005-2006 Szabolcs Szakacsits * Copyright (c) 2006 Yura Pakhuchiy - * Copyright (c) 2007-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 @@ -409,91 +409,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 @@ -536,7 +451,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; @@ -594,10 +509,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) @@ -955,7 +870,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); @@ -1154,7 +1069,6 @@ static int upgrade_secur_desc(ntfs_volume *vol, na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION, AT_UNNAMED, 0); if (na) { - res = 0; /* expand standard information attribute to v3.x */ res = ntfs_attr_truncate(na, (s64)sizeof(STANDARD_INFORMATION)); @@ -1240,6 +1154,7 @@ static BOOL staticgroupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid * /proc/$TID/task/$TID/status which apparently exists and * contains the same data. */ + #ifdef __HAIKU__ static BOOL groupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid) { @@ -1783,7 +1698,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) @@ -2109,7 +2024,6 @@ int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, const SID *gsid; /* group of file/directory */ uid_t uid; gid_t gid; - int perm; BOOL isdir; size_t outsize; @@ -2144,7 +2058,6 @@ int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, * fetch owner and group for cacheing */ if (pxdesc) { - perm = pxdesc->mode & 07777; /* * Create a security id if there were none * and upgrade option is selected @@ -2158,11 +2071,10 @@ int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, #if OWNERFROMACL uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); #else - if (!perm && ntfs_same_sid(usid, adminsid)) { + if (!(pxdesc->mode & 07777) + && ntfs_same_sid(usid, adminsid)) { uid = find_tenant(scx, securattr); - if (uid) - perm = 0700; } else uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); #endif @@ -2989,7 +2901,6 @@ int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, uid_t uid; uid_t gid; int res; - mode_t mode; BOOL isdir; BOOL deflt; BOOL exist; @@ -3015,7 +2926,6 @@ int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, gid = cached->gid; oldpxdesc = cached->pxdesc; if (oldpxdesc) { - mode = oldpxdesc->mode; newpxdesc = ntfs_replace_acl(oldpxdesc, (const struct POSIX_ACL*)value,count,deflt); } @@ -3042,7 +2952,6 @@ int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, || (!exist && (flags & XATTR_REPLACE))) { errno = (exist ? EEXIST : ENODATA); } else { - mode = oldpxdesc->mode; newpxdesc = ntfs_replace_acl(oldpxdesc, (const struct POSIX_ACL*)value,count,deflt); } @@ -3609,16 +3518,16 @@ int ntfs_set_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, int ntfs_set_ownmod(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, uid_t uid, gid_t gid, const mode_t mode) { - const SECURITY_DESCRIPTOR_RELATIVE *phead; const struct CACHED_PERMISSIONS *cached; char *oldattr; - const SID *usid; - const SID *gsid; uid_t fileuid; uid_t filegid; - BOOL isdir; int res; #if POSIXACLS + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const SID *usid; + const SID *gsid; + BOOL isdir; const struct POSIX_SECURITY *oldpxdesc; struct POSIX_SECURITY *newpxdesc = (struct POSIX_SECURITY*)NULL; int pxsize; @@ -3651,6 +3560,7 @@ int ntfs_set_ownmod(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, filegid = 0; oldattr = getsecurityattr(scx->vol, ni); if (oldattr) { +#if POSIXACLS isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0); phead = (const SECURITY_DESCRIPTOR_RELATIVE*) @@ -3663,7 +3573,6 @@ int ntfs_set_ownmod(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, usid = (const SID*) &oldattr[le32_to_cpu(phead->owner)]; #endif -#if POSIXACLS newpxdesc = ntfs_build_permissions_posix(scx->mapping, oldattr, usid, gsid, isdir); if (!newpxdesc || ntfs_merge_mode_posix(newpxdesc, mode)) @@ -4108,7 +4017,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)); } @@ -4880,6 +4789,7 @@ BOOL ntfs_set_file_attributes(struct SECURITY_API *scapi, ni->flags = (ni->flags & ~settable) | (cpu_to_le32(attrib) & settable); NInoSetDirty(ni); + NInoFileNameSetDirty(ni); } if (!ntfs_inode_close(ni)) res = -1; @@ -4930,7 +4840,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; @@ -5126,7 +5036,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/src/add-ons/kernel/file_systems/ntfs/libntfs/security.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/security.h index 83d7c0e131..9d7dd33142 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/security.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/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; @@ -135,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 */ @@ -181,6 +184,7 @@ struct POSIX_SECURITY { int defcnt; int firstdef; u16 tagsset; + s32 alignment[0]; struct POSIX_ACL acl; } ; @@ -346,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/src/add-ons/kernel/file_systems/ntfs/libntfs/types.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/types.h index 8b7f3f5200..a64f12faae 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/types.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/types.h @@ -120,5 +120,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/src/add-ons/kernel/file_systems/ntfs/libntfs/unistr.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/unistr.c index d091d1c73b..a5e88e8bd2 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/unistr.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/unistr.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2004 Anton Altaparmakov * Copyright (c) 2002-2009 Szabolcs Szakacsits - * Copyright (c) 2008-2009 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 @@ -175,24 +175,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) @@ -1139,6 +1137,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}, @@ -1175,6 +1236,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; @@ -1198,6 +1260,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 = 0; + + *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 * @@ -1332,16 +1415,18 @@ BOOL ntfs_collapsible_chars(ntfs_volume *vol, { BOOL collapsible; unsigned int ch; + unsigned int cs; int i; collapsible = shortlen == longlen; - if (collapsible) - for (i=0; i= vol->upcase_len) - || ((shortname[i] != longname[i]) - && (shortname[i] != vol->upcase[ch]))) - collapsible = FALSE; + for (i=0; collapsible && (i= vol->upcase_len) + || (cs >= vol->upcase_len) + || (vol->upcase[cs] != vol->upcase[ch]))) + collapsible = FALSE; } return (collapsible); } diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/unistr.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/unistr.h index 639c5033ee..8cadc3c4fc 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/unistr.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/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/src/add-ons/kernel/file_systems/ntfs/libntfs/volume.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/volume.c index 5d82e5330f..3f198793b5 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/volume.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/volume.c @@ -54,6 +54,7 @@ #include #endif +#include "param.h" #include "compat.h" #include "volume.h" #include "attrib.h" @@ -67,10 +68,11 @@ #include "dir.h" #include "logging.h" #include "cache.h" +#include "realpath.h" #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 +115,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 @@ -373,6 +375,12 @@ mft_has_no_attr_list: /* Done with the $Mft mft record. */ ntfs_attr_put_search_ctx(ctx); ctx = NULL; + + /* Update the size fields in the inode. */ + vol->mft_ni->data_size = vol->mft_na->data_size; + vol->mft_ni->allocated_size = vol->mft_na->allocated_size; + set_nino_flag(vol->mft_ni, KnownSize); + /* * The volume is now setup so we can use all read access functions. */ @@ -482,13 +490,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); @@ -497,6 +502,12 @@ ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, unsigned long flags) NVolSetShowSysFiles(vol); NVolSetShowHidFiles(vol); NVolClearHideDotFiles(vol); + /* set default compression */ +#if DEFAULT_COMPRESSION + NVolSetCompression(vol); +#else + NVolClearCompression(vol); +#endif if (flags & MS_RDONLY) NVolSetReadOnly(vol); @@ -868,6 +879,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 +1037,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 @@ -1172,7 +1195,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; @@ -1184,10 +1207,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: @@ -1350,18 +1373,6 @@ int ntfs_umount(ntfs_volume *vol, const BOOL force __attribute__((unused))) #ifdef HAVE_MNTENT_H -#ifndef HAVE_REALPATH -/** - * realpath - If there is no realpath on the system - */ -static char *realpath(const char *path, char *resolved_path) -{ - strncpy(resolved_path, path, PATH_MAX); - resolved_path[PATH_MAX] = '\0'; - return resolved_path; -} -#endif - /** * ntfs_mntent_check - desc * @@ -1385,7 +1396,7 @@ static int ntfs_mntent_check(const char *file, unsigned long *mnt_flags) err = errno; goto exit; } - if (!realpath(file, real_file)) { + if (!ntfs_realpath_canonicalize(file, real_file)) { err = errno; goto exit; } @@ -1394,7 +1405,7 @@ static int ntfs_mntent_check(const char *file, unsigned long *mnt_flags) goto exit; } while ((mnt = getmntent(f))) { - if (!realpath(mnt->mnt_fsname, real_fsname)) + if (!ntfs_realpath_canonicalize(mnt->mnt_fsname, real_fsname)) continue; if (!strcmp(real_file, real_fsname)) break; @@ -1682,7 +1693,7 @@ void ntfs_mount_error(const char *volume, const char *mntpoint, int err) int ntfs_set_locale(void) { -#ifndef __HAIKU__ +#ifndef __HAIKU__ const char *locale; locale = setlocale(LC_ALL, ""); @@ -1692,7 +1703,7 @@ int ntfs_set_locale(void) "'%s'.\n", locale); return 1; } -#endif +#endif return 0; } @@ -1723,3 +1734,113 @@ int ntfs_volume_get_free_space(ntfs_volume *vol) } return (ret); } + +/** + * ntfs_volume_rename - change the current label on a volume + * @vol: volume to change the label on + * @label: the new label + * @label_len: the length of @label in ntfschars including the terminating NULL + * character, which is mandatory (the value can not exceed 128) + * + * Change the label on the volume @vol to @label. + */ +int ntfs_volume_rename(ntfs_volume *vol, ntfschar *label, int label_len) +{ + ntfs_attr *na; + char *old_vol_name; + char *new_vol_name = NULL; + int new_vol_name_len; + int err; + + if (NVolReadOnly(vol)) { + ntfs_log_error("Refusing to change label on read-only mounted " + "volume.\n"); + errno = EROFS; + return -1; + } + + label_len *= sizeof(ntfschar); + if (label_len > 0x100) { + ntfs_log_error("New label is too long. Maximum %u characters " + "allowed.\n", + (unsigned)(0x100 / sizeof(ntfschar))); + errno = ERANGE; + return -1; + } + + na = ntfs_attr_open(vol->vol_ni, AT_VOLUME_NAME, AT_UNNAMED, 0); + if (!na) { + if (errno != ENOENT) { + err = errno; + ntfs_log_perror("Lookup of $VOLUME_NAME attribute " + "failed"); + goto err_out; + } + + /* 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)) + { + err = errno; + ntfs_log_perror("Encountered error while adding " + "$VOLUME_NAME attribute"); + goto err_out; + } + } + else { + s64 written; + + if (NAttrNonResident(na)) { + err = errno; + ntfs_log_error("Error: Attribute $VOLUME_NAME must be " + "resident.\n"); + goto err_out; + } + + if (na->data_size != label_len) { + if (ntfs_attr_truncate(na, label_len)) { + err = errno; + ntfs_log_perror("Error resizing resident " + "attribute"); + goto err_out; + } + } + + if (label_len) { + written = ntfs_attr_pwrite(na, 0, label_len, label); + if (written == -1) { + err = errno; + ntfs_log_perror("Error when writing " + "$VOLUME_NAME data"); + goto err_out; + } + else if (written != label_len) { + err = EIO; + ntfs_log_error("Partial write when writing " + "$VOLUME_NAME data."); + goto err_out; + + } + } + } + + new_vol_name_len = + ntfs_ucstombs(label, label_len, &new_vol_name, 0); + if (new_vol_name_len == -1) { + err = errno; + ntfs_log_perror("Error while decoding new volume name"); + goto err_out; + } + + old_vol_name = vol->vol_name; + vol->vol_name = new_vol_name; + free(old_vol_name); + + err = 0; +err_out: + if (na) + ntfs_attr_close(na); + if (err) + errno = err; + return err ? -1 : 0; +} diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/volume.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/volume.h index 79193c53ff..9d62d66883 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/volume.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/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 @@ -57,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; @@ -111,6 +113,7 @@ typedef enum { NV_ShowHidFiles, /* 1: Show files marked hidden. */ NV_HideDotFiles, /* 1: Set hidden flag on dot files */ NV_Compression, /* 1: allow compression */ + NV_NoFixupWarn, /* 1: Do not log fixup errors */ } ntfs_volume_state_bits; #define test_nvol_flag(nv, flag) test_bit(NV_##flag, (nv)->state) @@ -145,6 +148,10 @@ typedef enum { #define NVolSetCompression(nv) set_nvol_flag(nv, Compression) #define NVolClearCompression(nv) clear_nvol_flag(nv, Compression) +#define NVolNoFixupWarn(nv) test_nvol_flag(nv, NoFixupWarn) +#define NVolSetNoFixupWarn(nv) set_nvol_flag(nv, NoFixupWarn) +#define NVolClearNoFixupWarn(nv) clear_nvol_flag(nv, NoFixupWarn) + /* * NTFS version 1.1 and 1.2 are used by Windows NT4. * NTFS version 2.x is used by Windows 2000 Beta @@ -251,7 +258,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 @@ -293,6 +302,7 @@ 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_set_shown_files(ntfs_volume *vol, BOOL show_sys_files, BOOL show_hid_files, BOOL hide_dot_files); diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/xattrs.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/xattrs.c new file mode 100644 index 0000000000..5be2c06a68 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/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/src/add-ons/kernel/file_systems/ntfs/libntfs/xattrs.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/xattrs.h new file mode 100644 index 0000000000..515831142e --- /dev/null +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/xattrs.h @@ -0,0 +1,75 @@ +/* + * 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_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_ */ diff --git a/src/add-ons/kernel/file_systems/ntfs/utils.c b/src/add-ons/kernel/file_systems/ntfs/utils.c index c284d28db0..e224d5f515 100644 --- a/src/add-ons/kernel/file_systems/ntfs/utils.c +++ b/src/add-ons/kernel/file_systems/ntfs/utils.c @@ -4,6 +4,7 @@ * Copyright (c) 2002-2005 Richard Russon * Copyright (c) 2003-2006 Anton Altaparmakov * Copyright (c) 2003 Lode Leroy + * Copyright (c) 2005-2007 Yura Pakhuchiy * * A set of shared functions for ntfs utilities * @@ -69,6 +70,7 @@ #include "volume.h" #include "debug.h" #include "dir.h" +/* #include "version.h" */ #include "logging.h" #include "misc.h" @@ -80,9 +82,120 @@ const char *ntfs_gpl = "This program is free software, released under the GNU " "\"COPYING\" distributed with this program, or online at:\n" "http://www.gnu.org/copyleft/gpl.html\n"; +static const char *invalid_ntfs_msg = +"The device '%s' doesn't have a valid NTFS.\n" +"Maybe you selected the wrong device? Or the whole disk instead of a\n" +"partition (e.g. /dev/hda, not /dev/hda1)? Or the other way around?\n"; + +static const char *corrupt_volume_msg = +"NTFS is inconsistent. Run chkdsk /f on Windows then reboot it TWICE!\n" +"The usage of the /f parameter is very IMPORTANT! No modification was\n" +"made to NTFS by this software.\n"; + +static const char *hibernated_volume_msg = +"The NTFS partition is hibernated. Please resume Windows and turned it \n" +"off properly, so mounting could be done safely.\n"; + +static const char *unclean_journal_msg = +"Access is denied because the NTFS journal file is unclean. Choices are:\n" +" A) Shutdown Windows properly.\n" +" B) Click the 'Safely Remove Hardware' icon in the Windows taskbar\n" +" notification area before disconnecting the device.\n" +" C) Use 'Eject' from Windows Explorer to safely remove the device.\n" +" D) If you ran chkdsk previously then boot Windows again which will\n" +" automatically initialize the journal.\n" +" E) Submit 'force' option (WARNING: This solution it not recommended).\n" +" F) ntfsmount: Mount the volume read-only by using the 'ro' mount option.\n"; + +static const char *opened_volume_msg = +"Access is denied because the NTFS volume is already exclusively opened.\n" +"The volume may be already mounted, or another software may use it which\n" +"could be identified for example by the help of the 'fuser' command.\n"; + +static const char *dirty_volume_msg = +"Volume is scheduled for check.\n" +"Please boot into Windows TWICE, or use the 'force' option.\n" +"NOTE: If you had not scheduled check and last time accessed this volume\n" +"using ntfsmount and shutdown system properly, then init scripts in your\n" +"distribution are broken. Please report to your distribution developers\n" +"(NOT to us!) that init scripts kill ntfsmount or mount.ntfs-fuse during\n" +"shutdown instead of proper umount.\n"; + +static const char *fakeraid_msg = +"You seem to have a SoftRAID/FakeRAID hardware and must use an activated,\n" +"different device under /dev/mapper, (e.g. /dev/mapper/nvidia_eahaabcc1)\n" +"to mount NTFS. Please see the 'dmraid' documentation for help.\n"; /** - * utils_valid_device - Perform some safety checks on the device, before we start + * utils_set_locale + */ +#ifndef __HAIKU__ +int utils_set_locale(void) +{ + const char *locale; + + locale = setlocale(LC_ALL, ""); + if (!locale) { + locale = setlocale(LC_ALL, NULL); + ntfs_log_error("Failed to set locale, using default '%s'.\n", + locale); + return 1; + } else { + return 0; + } +} +#endif + +/** + * linux-ntfs's ntfs_mbstoucs has different semantics, so we emulate it with + * ntfs-3g's. + */ +int ntfs_mbstoucs_libntfscompat(const char *ins, + ntfschar **outs, int outs_len) +{ + if(!outs) { + errno = EINVAL; + return -1; + } + else if(*outs != NULL) { + /* Note: libntfs's mbstoucs implementation allows the caller to + * specify a preallocated buffer while libntfs-3g's always + * allocates the output buffer. + */ + ntfschar *tmpstr = NULL; + int tmpstr_len; + + tmpstr_len = ntfs_mbstoucs(ins, &tmpstr); + if(tmpstr_len >= 0) { + if((tmpstr_len + 1) > outs_len) { + /* Doing a realloc instead of reusing tmpstr + * because it emulates libntfs's mbstoucs more + * closely. */ + ntfschar *re_outs = realloc(*outs, + sizeof(ntfschar)*(tmpstr_len + 1)); + if(!re_outs) + tmpstr_len = -1; + else + *outs = re_outs; + } + + if(tmpstr_len >= 0) { + /* The extra character is the \0 terminator. */ + memcpy(*outs, tmpstr, + sizeof(ntfschar)*(tmpstr_len + 1)); + } + + free(tmpstr); + } + + return tmpstr_len; + } + else + return ntfs_mbstoucs(ins, outs); +} + +/** + * utils_valid_device - Perform some safety checks on the device, before start * @name: Full pathname of the device/file to work with * @force: Continue regardless of problems * @@ -98,7 +211,7 @@ int utils_valid_device(const char *name, int force) struct stat st; #ifdef __CYGWIN32__ - /* FIXME: This doesn't work for Cygwin, so just return success for now... */ + /* FIXME: This doesn't work for Cygwin, so just return success. */ return 1; #endif if (!name) { @@ -107,37 +220,30 @@ int utils_valid_device(const char *name, int force) } if (stat(name, &st) == -1) { - if (errno == ENOENT) { + if (errno == ENOENT) ntfs_log_error("The device %s doesn't exist\n", name); - } else { - ntfs_log_perror("Error getting information about %s", name); - } + else + ntfs_log_perror("Error getting information about %s", + name); return 0; } - if (!S_ISBLK(st.st_mode) && !S_ISREG(st.st_mode)) { - ntfs_log_warning("%s is not a block device, " - "nor regular file.\n", name); - if (!force) { - ntfs_log_error("Use the force option to work with other" - " file types, for your own risk!\n"); - return 0; - } - ntfs_log_warning("Forced to continue.\n"); - } - /* Make sure the file system is not mounted. */ if (ntfs_check_if_mounted(name, &mnt_flags)) { - ntfs_log_perror("Failed to determine whether %s is mounted", name); + ntfs_log_perror("Failed to determine whether %s is mounted", + name); if (!force) { - ntfs_log_error("Use the force option to ignore this error.\n"); + ntfs_log_error("Use the force option to ignore this " + "error.\n"); return 0; } ntfs_log_warning("Forced to continue.\n"); } else if (mnt_flags & NTFS_MF_MOUNTED) { - ntfs_log_warning("The device %s, is mounted.\n", name); if (!force) { - ntfs_log_error("Use the force option to work a mounted filesystem.\n"); + ntfs_log_error("%s", opened_volume_msg); + ntfs_log_error("You can use force option to avoid this " + "check, but this is not recommended\n" + "and may lead to data corruption.\n"); return 0; } ntfs_log_warning("Forced to continue.\n"); @@ -149,7 +255,7 @@ int utils_valid_device(const char *name, int force) /** * utils_mount_volume - Mount an NTFS volume */ -ntfs_volume * utils_mount_volume(const char *device, unsigned long flags, BOOL force) +ntfs_volume * utils_mount_volume(const char *device, unsigned long flags) { ntfs_volume *vol; @@ -158,34 +264,55 @@ ntfs_volume * utils_mount_volume(const char *device, unsigned long flags, BOOL f return NULL; } - if (!utils_valid_device(device, force)) + /* Porting notes: + * + * libntfs-3g does not have the 'force' flag in ntfs_mount_flags. + * The 'force' flag in libntfs bypasses two safety checks when mounting + * read/write: + * 1. Do not mount when the VOLUME_IS_DIRTY flag in + * VOLUME_INFORMATION is set. + * 2. Do not mount when the logfile is unclean. + * + * libntfs-3g only has safety check number 2. The dirty flag is simply + * ignored because we are confident that we can handle a dirty volume. + * So we treat MS_RECOVER like NTFS_MNT_FORCE, knowing that the first + * check is always bypassed. + */ + + if (!utils_valid_device(device, flags & MS_RECOVER)) return NULL; vol = ntfs_mount(device, flags); if (!vol) { - ntfs_log_perror("Couldn't mount device '%s'", device); - if (errno == EPERM) - ntfs_log_error("Windows was hibernated. Try to mount " - "volume in windows, shut down and try " - "again.\n"); - if (errno == EOPNOTSUPP) - ntfs_log_error("Windows did not shut down properly. " - "Try to mount volume in windows, " - "shut down and try again.\n"); + ntfs_log_perror("Failed to mount '%s'", device); + if (errno == EINVAL) + ntfs_log_error(invalid_ntfs_msg, device); + else if (errno == EIO) + ntfs_log_error("%s", corrupt_volume_msg); + else if (errno == EPERM) + ntfs_log_error("%s", hibernated_volume_msg); + else if (errno == EOPNOTSUPP) + ntfs_log_error("%s", unclean_journal_msg); + else if (errno == EBUSY) + ntfs_log_error("%s", opened_volume_msg); + else if (errno == ENXIO) + ntfs_log_error("%s", fakeraid_msg); return NULL; } + /* Porting notes: + * libntfs-3g does not record whether the volume log file was dirty + * before mount, so we can only warn if the VOLUME_IS_DIRTY flag is set + * in VOLUME_INFORMATION. */ if (vol->flags & VOLUME_IS_DIRTY) { - ntfs_log_warning("Volume is dirty.\n"); - if (!force) { - ntfs_log_error("Run chkdsk and try again, or use the " - "force option.\n"); + if (!(flags & MS_RECOVER)) { + ntfs_log_error("%s", dirty_volume_msg); ntfs_umount(vol, FALSE); return NULL; } - ntfs_log_warning("Forced to continue.\n"); + ntfs_log_error("WARNING: Dirty volume mount was forced by the " + "'force' mount option.\n"); } - return vol; } @@ -222,6 +349,8 @@ int utils_parse_size(const char *value, s64 *size, BOOL scale) return 0; } + ntfs_log_debug("Parsing size '%s'.\n", value); + result = strtoll(value, &suffix, 0); if (result < 0 || errno == ERANGE) { ntfs_log_error("Invalid size '%s'.\n", value); @@ -252,6 +381,7 @@ int utils_parse_size(const char *value, s64 *size, BOOL scale) } } + ntfs_log_debug("Parsed size = %lld.\n", result); *size = result; return 1; } @@ -386,11 +516,12 @@ ATTR_RECORD * find_first_attribute(const ATTR_TYPES type, MFT_RECORD *mft) * if parent is 5 (/) stop * get inode of parent */ +#define max_path 20 int utils_inode_get_name(ntfs_inode *inode, char *buffer, int bufsize) { // XXX option: names = posix/win32 or dos // flags: path, filename, or both - const int max_path = 20; + ntfs_volume *vol; ntfs_attr_search_ctx *ctx; @@ -442,10 +573,10 @@ int utils_inode_get_name(ntfs_inode *inode, char *buffer, int bufsize) &names[i], 0) < 0) { char *temp; ntfs_log_error("Couldn't translate filename to current locale.\n"); - temp = ntfs_malloc(64); + temp = ntfs_malloc(30); if (!temp) return 0; - sprintf(temp, "", (unsigned + snprintf(temp, 30, "", (unsigned long long)inode->mft_no); names[i] = temp; } @@ -483,7 +614,7 @@ int utils_inode_get_name(ntfs_inode *inode, char *buffer, int bufsize) if (!names[i]) continue; - len = sprintf(buffer + offset, "%c%s", PATH_SEP, names[i]); //bufsize - offset, + len = snprintf(buffer + offset, bufsize - offset, "%c%s", PATH_SEP, names[i]); if (len >= (bufsize - offset)) { ntfs_log_error("Pathname was truncated.\n"); break; @@ -500,66 +631,7 @@ int utils_inode_get_name(ntfs_inode *inode, char *buffer, int bufsize) return 1; } - - -int utils_inode_get_ucsfilename(ntfs_inode *inode,ntfschar **uname, int *size) -{ - // XXX option: names = posix/win32 or dos - // flags: path, filename, or both - - ntfs_volume *vol; - ntfs_attr_search_ctx *ctx; - ATTR_RECORD *rec; - FILE_NAME_ATTR *attr; - - int name_space; - int res=B_NO_ERROR; - - *uname=NULL; - - - if (!inode || !uname || !size) { - res = EINVAL; - return 0; - } - - vol = inode->vol; - - ctx = ntfs_attr_get_search_ctx(inode, NULL); - if (!ctx) { - res = EINVAL; - return res; - } - - name_space = 4; - while ((rec = find_attribute(AT_FILE_NAME, ctx))) - { - /* We know this will always be resident. */ - attr = (FILE_NAME_ATTR *) ((char *) rec + le16_to_cpu(rec->value_offset)); - - if (attr->file_name_type > name_space) { //XXX find the ... - continue; - } - - name_space = attr->file_name_type; - - if(*uname!=NULL)free(*uname); - *uname=(ntfschar*)ntfs_malloc(attr->file_name_length); - if(*uname==NULL) - { - res = EINVAL; - goto exit; - } - - memcpy(*uname,attr->file_name, attr->file_name_length); - *size = attr->file_name_length; - } - -exit: - ntfs_attr_put_search_ctx(ctx); - - return res; -} +#undef max_path /** * utils_attr_get_name @@ -578,45 +650,44 @@ int utils_attr_get_name(ntfs_volume *vol, ATTR_RECORD *attr, char *buffer, int b attrdef = ntfs_attr_find_in_attrdef(vol, attr->type); if (attrdef) { - name = NULL; + name = NULL; namelen = ntfs_ucsnlen(attrdef->name, sizeof(attrdef->name)); if (ntfs_ucstombs(attrdef->name, namelen, &name, 0) < 0) { - ntfs_log_error("Couldn't translate attribute type to current locale.\n"); + ntfs_log_error("Couldn't translate attribute type to " + "current locale.\n"); // ? return 0; } - len = sprintf(buffer, "%s", name); + len = snprintf(buffer, bufsize, "%s", name); } else { ntfs_log_error("Unknown attribute type 0x%02x\n", attr->type); - len = sprintf(buffer, ""); + len = snprintf(buffer, bufsize, ""); } if (len >= bufsize) { ntfs_log_error("Attribute type was truncated.\n"); - free(name); return 0; } if (!attr->name_length) { - free(name); return 0; } buffer += len; bufsize -= len; - free(name); - name = NULL; + name = NULL; namelen = attr->name_length; if (ntfs_ucstombs((ntfschar *)((char *)attr + attr->name_offset), - namelen, &name, 0) < 0) { - ntfs_log_error("Couldn't translate attribute name to current locale.\n"); + namelen, &name, 0) < 0) { + ntfs_log_error("Couldn't translate attribute name to current " + "locale.\n"); // ? - len = sprintf(buffer, ""); + len = snprintf(buffer, bufsize, ""); return 0; } - len = sprintf(buffer, "(%s)", name); + len = snprintf(buffer, bufsize, "(%s)", name); free(name); if (len >= bufsize) { @@ -638,7 +709,10 @@ int utils_attr_get_name(ntfs_volume *vol, ATTR_RECORD *attr, char *buffer, int b * * This function has a static buffer in which it caches a section of $Bitmap. * If the lcn, being tested, lies outside the range, the buffer will be - * refreshed. + * refreshed. @bmplcn stores offset to the first bit (in bits) stored in the + * buffer. + * + * NOTE: Be very carefull with shifts by 3 everywhere in this function. * * Return: 1 Cluster is in use * 0 Cluster is free space @@ -647,8 +721,7 @@ int utils_attr_get_name(ntfs_volume *vol, ATTR_RECORD *attr, char *buffer, int b int utils_cluster_in_use(ntfs_volume *vol, long long lcn) { static unsigned char buffer[512]; - static long long bmplcn = -sizeof(buffer) - 1; /* Which bit of $Bitmap is in the buffer */ - + static long long bmplcn = -(sizeof(buffer) << 3); int byte, bit; ntfs_attr *attr; @@ -658,7 +731,8 @@ int utils_cluster_in_use(ntfs_volume *vol, long long lcn) } /* Does lcn lie in the section of $Bitmap we already have cached? */ - if ((lcn < bmplcn) || (lcn >= (bmplcn + (sizeof(buffer) << 3)))) { + if ((lcn < bmplcn) + || (lcn >= (long long)(bmplcn + (sizeof(buffer) << 3)))) { ntfs_log_debug("Bit lies outside cache.\n"); attr = ntfs_attr_open(vol->lcnbmp_ni, AT_DATA, AT_UNNAMED, 0); if (!attr) { @@ -670,7 +744,8 @@ int utils_cluster_in_use(ntfs_volume *vol, long long lcn) memset(buffer, 0xFF, sizeof(buffer)); bmplcn = lcn & (~((sizeof(buffer) << 3) - 1)); - if (ntfs_attr_pread(attr, (bmplcn>>3), sizeof(buffer), buffer) < 0) { + if (ntfs_attr_pread(attr, (bmplcn >> 3), sizeof(buffer), + buffer) < 0) { ntfs_log_perror("Couldn't read $Bitmap"); ntfs_attr_close(attr); return -1; @@ -682,7 +757,9 @@ int utils_cluster_in_use(ntfs_volume *vol, long long lcn) bit = 1 << (lcn & 7); byte = (lcn >> 3) & (sizeof(buffer) - 1); - ntfs_log_debug("cluster = %lld, bmplcn = %lld, byte = %d, bit = %d, in use %d\n", lcn, bmplcn, byte, bit, buffer[byte] & bit); + ntfs_log_debug("cluster = %lld, bmplcn = %lld, byte = %d, bit = %d, " + "in use %d\n", lcn, bmplcn, byte, bit, buffer[byte] & + bit); return (buffer[byte] & bit); } @@ -707,19 +784,19 @@ int utils_cluster_in_use(ntfs_volume *vol, long long lcn) int utils_mftrec_in_use(ntfs_volume *vol, MFT_REF mref) { static u8 buffer[512]; - static s64 bmpmref = -sizeof(buffer) - 1; /* Which bit of $BITMAP is in the buffer */ - + static s64 bmpmref = -(sizeof(buffer) << 3) - 1; /* Which bit of $BITMAP is in the buffer */ int byte, bit; + ntfs_log_trace("Entering.\n"); + if (!vol) { errno = EINVAL; return -1; } - ntfs_log_trace("entering\n"); /* Does mref lie in the section of $Bitmap we already have cached? */ - if (((s64)MREF(mref) < bmpmref) || ((s64)MREF(mref) >= (bmpmref + - (sizeof(buffer) << 3)))) { + if (((s64)MREF(mref) < bmpmref) + || ((s64)MREF(mref) >= (s64)(bmpmref + (sizeof(buffer) << 3)))) { ntfs_log_debug("Bit lies outside cache.\n"); /* Mark the buffer as not in use, in case the read is shorter. */ @@ -791,20 +868,19 @@ int utils_is_metadata(ntfs_inode *inode) file = inode->mrec; if (file && (file->base_mft_record != 0)) { - num = MREF(file->base_mft_record); + num = MREF_LE(file->base_mft_record); if (__metadata(vol, num) == 1) return 1; } - file = inode->mrec; rec = find_first_attribute(AT_FILE_NAME, inode->mrec); if (!rec) return -1; /* We know this will always be resident. */ - attr = (FILE_NAME_ATTR *) ((char *) rec + le16_to_cpu(rec->value_offset)); + attr = (FILE_NAME_ATTR *)((char *)rec + le16_to_cpu(rec->value_offset)); - num = MREF(attr->parent_directory); + num = MREF_LE(attr->parent_directory); if ((num != FILE_root) && (__metadata(vol, num) == 1)) return 1; @@ -890,7 +966,7 @@ struct mft_search_ctx * mft_get_search_ctx(ntfs_volume *vol) return NULL; } - ctx = calloc(1, sizeof *ctx); + ctx = (struct mft_search_ctx*)calloc(1, sizeof *ctx); ctx->mft_num = -1; ctx->vol = vol; @@ -997,7 +1073,7 @@ int mft_next_record(struct mft_search_ctx *ctx) ctx->flags_match |= FEMR_NOT_IN_USE; - ctx->inode = calloc(1, sizeof(*ctx->inode)); + ctx->inode = (ntfs_inode*)calloc(1, sizeof(*ctx->inode)); if (!ctx->inode) { ntfs_log_error("Out of memory. Aborting.\n"); return -1; @@ -1047,7 +1123,7 @@ int mft_next_record(struct mft_search_ctx *ctx) } /** - * ntfs_get_parent_ref - find mft reference of parent directory of an inode + * ntfs_mft_get_parent_ref - find mft reference of parent directory of an inode * @ni: ntfs inode whose parent directory to find * * Find the parent directory of the ntfs inode @ni. To do this, find the first @@ -1064,7 +1140,7 @@ int mft_next_record(struct mft_search_ctx *ctx) * Return the mft reference of the parent directory on success or -1 on error * with errno set to the error code. */ -MFT_REF ntfs_get_parent_ref(ntfs_inode *ni) +MFT_REF ntfs_mft_get_parent_ref(ntfs_inode *ni) { MFT_REF mref; ntfs_attr_search_ctx *ctx; @@ -1082,21 +1158,21 @@ MFT_REF ntfs_get_parent_ref(ntfs_inode *ni) if (!ctx) return ERR_MREF(-1); if (ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { - ntfs_log_debug("No file name found in inode 0x%llx. Corrupt " - "inode.\n", (unsigned long long)ni->mft_no); + ntfs_log_error("No file name found in inode %lld\n", + (unsigned long long)ni->mft_no); goto err_out; } if (ctx->attr->non_resident) { - ntfs_log_debug("File name attribute must be resident. Corrupt inode " - "0x%llx.\n", (unsigned long long)ni->mft_no); + ntfs_log_error("File name attribute must be resident (inode " + "%lld)\n", (unsigned long long)ni->mft_no); goto io_err_out; } fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + le16_to_cpu(ctx->attr->value_offset)); if ((u8*)fn + le32_to_cpu(ctx->attr->value_length) > (u8*)ctx->attr + le32_to_cpu(ctx->attr->length)) { - ntfs_log_debug("Corrupt file name attribute in inode 0x%llx.\n", - (unsigned long long)ni->mft_no); + ntfs_log_error("Corrupt file name attribute in inode %lld.\n", + (unsigned long long)ni->mft_no); goto io_err_out; } mref = le64_to_cpu(fn->parent_directory); @@ -1110,3 +1186,5 @@ err_out: errno = eo; return ERR_MREF(-1); } + + diff --git a/src/add-ons/kernel/file_systems/ntfs/utils.h b/src/add-ons/kernel/file_systems/ntfs/utils.h index 48c00d3413..027a15a91e 100644 --- a/src/add-ons/kernel/file_systems/ntfs/utils.h +++ b/src/add-ons/kernel/file_systems/ntfs/utils.h @@ -39,26 +39,23 @@ #endif extern const char *ntfs_bugs; -extern const char *ntfs_home; extern const char *ntfs_gpl; -//int utils_set_locale(void); +int utils_set_locale(void); int utils_parse_size(const char *value, s64 *size, BOOL scale); int utils_parse_range(const char *string, s64 *start, s64 *finish, BOOL scale); int utils_inode_get_name(ntfs_inode *inode, char *buffer, int bufsize); -int utils_inode_get_ucsfilename(ntfs_inode *inode,ntfschar **, int *size); int utils_attr_get_name(ntfs_volume *vol, ATTR_RECORD *attr, char *buffer, int bufsize); int utils_cluster_in_use(ntfs_volume *vol, long long lcn); int utils_mftrec_in_use(ntfs_volume *vol, MFT_REF mref); int utils_is_metadata(ntfs_inode *inode); void utils_dump_mem(void *buf, int start, int length, int flags); -MFT_REF ntfs_get_parent_ref(ntfs_inode *ni); ATTR_RECORD * find_attribute(const ATTR_TYPES type, ntfs_attr_search_ctx *ctx); ATTR_RECORD * find_first_attribute(const ATTR_TYPES type, MFT_RECORD *mft); int utils_valid_device(const char *name, int force); -ntfs_volume * utils_mount_volume(const char *device, unsigned long flags, BOOL force); +ntfs_volume * utils_mount_volume(const char *device, unsigned long flags); /** * defines... @@ -88,6 +85,7 @@ struct mft_search_ctx { struct mft_search_ctx * mft_get_search_ctx(ntfs_volume *vol); void mft_put_search_ctx(struct mft_search_ctx *ctx); int mft_next_record(struct mft_search_ctx *ctx); +MFT_REF ntfs_mft_get_parent_ref(ntfs_inode *ni); // Flags for dump mem #define DM_DEFAULTS 0 @@ -99,4 +97,22 @@ int mft_next_record(struct mft_search_ctx *ctx); #define DM_BLUE (1 << 5) #define DM_BOLD (1 << 6) +/* MAX_PATH definition was missing in ntfs-3g's headers. */ +#ifndef MAX_PATH +#define MAX_PATH 1024 +#endif + +/** + * linux-ntfs's ntfs_mbstoucs has different semantics, so we emulate it with + * ntfs-3g's. + */ +int ntfs_mbstoucs_libntfscompat(const char *ins, + ntfschar **outs, int outs_len); + +/* This simple utility function was missing from libntfs-3g. */ +static __inline__ ntfschar *ntfs_attr_get_name(ATTR_RECORD *attr) +{ + return (ntfschar*)((u8*)attr + le16_to_cpu(attr->name_offset)); +} + #endif /* _NTFS_UTILS_H_ */