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 86adc3f9fb..fa968d2c5d 100644 --- a/src/add-ons/kernel/file_systems/ntfs/fs_func.c +++ b/src/add-ons/kernel/file_systems/ntfs/fs_func.c @@ -92,7 +92,7 @@ fs_identify_partition(int fd, partition_data *partition, void **_cookie) if(strlen(ntVolume->vol_name)>0) strcpy(cookie->label,ntVolume->vol_name); - ntfs_device_umount( ntVolume, true ); + ntfs_umount( ntVolume, true ); *_cookie = cookie; @@ -230,7 +230,7 @@ fs_unmount(void *_ns) ERRPRINT("fs_unmount - ENTER\n"); - ntfs_device_umount( ns->ntvol, true ); + ntfs_umount( ns->ntvol, true ); #ifdef __HAIKU__ recursive_lock_destroy(&(ns->vlock)); 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 acec8434d3..bf903f9bf0 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/attrib.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/attrib.c @@ -544,8 +544,9 @@ int ntfs_attr_map_whole_runlist(ntfs_attr *na) if (!next_vcn) { if (a->lowest_vcn) { errno = EIO; - ntfs_log_perror("Attribute first extent has " - "non-zero lowest_vcn"); + ntfs_log_perror("First extent of inode %llu " + "attribute has non-zero lowest_vcn", + (unsigned long long)na->ni->mft_no); goto err_out; } /* Get the last vcn in the attribute. */ @@ -566,7 +567,8 @@ int ntfs_attr_map_whole_runlist(ntfs_attr *na) /* Avoid endless loops due to corruption. */ if (next_vcn < sle64_to_cpu(a->lowest_vcn)) { errno = EIO; - ntfs_log_perror("Inode has corrupt attribute list"); + ntfs_log_perror("Inode %llu has corrupt attribute list", + (unsigned long long)na->ni->mft_no); goto err_out; } } @@ -576,8 +578,9 @@ int ntfs_attr_map_whole_runlist(ntfs_attr *na) } if (highest_vcn && highest_vcn != last_vcn - 1) { errno = EIO; - ntfs_log_perror("Couldn't load full runlist: " - "highest_vcn = 0x%llx, last_vcn = 0x%llx", + ntfs_log_perror("Failed to load full runlist: inode: %llu " + "highest_vcn: 0x%llx last_vcn: 0x%llx", + (unsigned long long)na->ni->mft_no, (long long)highest_vcn, (long long)last_vcn); goto err_out; } @@ -988,10 +991,8 @@ static int ntfs_attr_fill_hole(ntfs_attr *na, s64 count, s64 *ofs, ((*ofs + to_write - 1) >> vol->cluster_size_bits) + 1 + (*rl)->vcn - from_vcn, lcn_seek_from, DATA_ZONE); - if (!rlc) { - ntfs_log_perror("Hole filling cluster allocation failed"); + if (!rlc) goto err_out; - } *rl = ntfs_runlists_merge(na->rl, rlc); if (!*rl) { 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 118165834b..eb00e3810d 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/dir.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/dir.c @@ -466,11 +466,14 @@ ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent, q++; } - len = ntfs_mbstoucs(p, &unicode, NTFS_MAX_NAME_LEN); + len = ntfs_mbstoucs(p, &unicode, MAX_PATH); if (len < 0) { ntfs_log_debug("Couldn't convert name to Unicode: %s.\n", p); err = errno; goto close; + } else if (len > NTFS_MAX_NAME_LEN) { + err = ENAMETOOLONG; + goto close; } inum = ntfs_inode_lookup_by_name(ni, unicode, len); @@ -482,7 +485,10 @@ ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent, } if (ni != parent) - ntfs_inode_close(ni); + if (ntfs_inode_close(ni)) { + err = errno; + goto out; + } inum = MREF(inum); ni = ntfs_inode_open(vol, inum); @@ -502,7 +508,9 @@ ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent, ni = NULL; close: if (ni && (ni != parent)) - ntfs_inode_close(ni); + if (ntfs_inode_close(ni) && !err) + err = errno; +out: free(ascii); free(unicode); if (err) @@ -1045,12 +1053,15 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, errno = EINVAL; return NULL; } - /* Allocate MFT record for new file. */ - ni = ntfs_mft_record_alloc(dir_ni->vol, NULL); - if (!ni) { - ntfs_log_perror("Could not allocate new MFT record"); + + if (dir_ni->flags & FILE_ATTR_REPARSE_POINT) { + errno = EOPNOTSUPP; return NULL; } + + ni = ntfs_mft_record_alloc(dir_ni->vol, NULL); + if (!ni) + return NULL; /* * Create STANDARD_INFORMATION attribute. Write STANDARD_INFORMATION * version 1.2, windows will upgrade it to version 3 if needed. @@ -1299,7 +1310,7 @@ int ntfs_check_empty_dir(ntfs_inode *ni) } /* Non-empty directory? */ - if ((na->data_size != sizeof(INDEX_ROOT) + sizeof(INDEX_ENTRY_HEADER))) { + if ((na->data_size != sizeof(INDEX_ROOT) + sizeof(INDEX_ENTRY_HEADER))){ /* Both ENOTEMPTY and EEXIST are ok. We use the more common. */ errno = EEXIST; ntfs_log_debug("Directory is not empty\n"); @@ -1416,7 +1427,7 @@ search: if (ntfs_names_are_equal(fn->file_name, fn->file_name_length, name, name_len, case_sensitive, - ni->vol->upcase, ni->vol->upcase_len)) { + ni->vol->upcase, ni->vol->upcase_len)){ if (fn->file_name_type == FILE_NAME_WIN32) { looking_for_dos_name = TRUE; @@ -1525,8 +1536,8 @@ out: ntfs_attr_put_search_ctx(actx); if (ictx) ntfs_index_ctx_put(ictx); - if (ni) - ntfs_inode_close(ni); + if (ni && ntfs_inode_close(ni) && !err) + err = errno; if (err) { errno = err; ntfs_log_debug("Could not delete file: %s\n", strerror(errno)); @@ -1567,6 +1578,12 @@ int ntfs_link(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, u8 name_len) ntfs_log_perror("ntfs_link wrong arguments"); goto err_out; } + + if (ni->flags & FILE_ATTR_REPARSE_POINT) { + err = EOPNOTSUPP; + goto err_out; + } + /* Create FILE_NAME attribute. */ fn_len = sizeof(FILE_NAME_ATTR) + name_len * sizeof(ntfschar); fn = ntfs_calloc(fn_len); @@ -1628,7 +1645,6 @@ rollback_failed: err_out: free(fn); errno = err; - ntfs_log_perror("Hard link failed"); return -1; } diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/dir.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/dir.h index eac72def54..2d81cff0d2 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/dir.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/dir.h @@ -40,6 +40,7 @@ #define S_IFSOCK 0140000 #endif #endif + /* * We do not have these under DJGPP, so define our version that do not conflict * with other S_IFs defined under DJGPP. 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 0c3fc58aa2..506a6a5265 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/index.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/index.c @@ -1417,6 +1417,7 @@ static int ntfs_ie_add(ntfs_index_context *icx, INDEX_ENTRY *ie) fn = ntfs_ie_filename_get(ie); ntfs_log_trace("file: '%s'\n", fn); + ntfs_attr_name_free(&fn); while (1) { @@ -1461,7 +1462,6 @@ static int ntfs_ie_add(ntfs_index_context *icx, INDEX_ENTRY *ie) ret = STATUS_OK; err_out: - ntfs_attr_name_free(&fn); ntfs_log_trace("%s\n", ret ? "Failed" : "Done"); return ret; } 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 3405669e2c..fee018735f 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/inode.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/inode.c @@ -107,15 +107,16 @@ ntfs_inode *ntfs_inode_allocate(ntfs_volume *vol) * * Returns: */ -static int __ntfs_inode_release(ntfs_inode *ni) +static void __ntfs_inode_release(ntfs_inode *ni) { if (NInoDirty(ni)) - ntfs_log_debug("Eeek. Discarding dirty inode!\n"); + ntfs_log_error("Releasing dirty inode %lld!\n", + (long long)ni->mft_no); if (NInoAttrList(ni) && ni->attr_list) free(ni->attr_list); free(ni->mrec); free(ni); - return 0; + return; } /** @@ -271,7 +272,7 @@ int ntfs_inode_close(ntfs_inode *ni) if (!ni) return 0; - ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no); + ntfs_log_trace("Entering for inode 0x%llx.\n", (long long)ni->mft_no); /* If we have dirty metadata, write it out. */ if (NInoDirty(ni) || NInoAttrListDirty(ni)) { @@ -330,11 +331,18 @@ int ntfs_inode_close(ntfs_inode *ni) i = -1; break; } + + /* + * We could successfully sync, so only log this error + * and try to sync other inode extents too. + */ if (i != -1) - ntfs_log_debug("Extent inode was not attached to base inode! " - "Weird! Continuing regardless.\n"); + ntfs_log_error("Extent inode %lld was not found\n", + (long long)ni->mft_no); } - return __ntfs_inode_release(ni); + + __ntfs_inode_release(ni); + return 0; } /** @@ -425,9 +433,7 @@ ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni, const MFT_REF mref) base_ni->extent_nis[base_ni->nr_extents++] = ni; return ni; err_out: - i = errno; __ntfs_inode_release(ni); - errno = i; ntfs_log_perror("Failed to open extent inode"); return NULL; } @@ -536,12 +542,11 @@ static int ntfs_inode_sync_file_name(ntfs_inode *ni) FILE_NAME_ATTR *fn; int err = 0; - ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no); + ntfs_log_trace("Entering for inode %lld\n", (long long)ni->mft_no); ctx = ntfs_attr_get_search_ctx(ni, NULL); if (!ctx) { err = errno; - ntfs_log_trace("Failed to get attribute search context.\n"); goto err_out; } /* Walk through all FILE_NAME attributes and update them. */ @@ -550,27 +555,29 @@ static int ntfs_inode_sync_file_name(ntfs_inode *ni) le16_to_cpu(ctx->attr->value_offset)); if (MREF_LE(fn->parent_directory) == ni->mft_no) { /* - * WARNING: We cheater here and obtain 2 attribute + * WARNING: We cheat here and obtain 2 attribute * search contexts for one inode (first we obtained * above, second will be obtained inside * ntfs_index_lookup), it's acceptable for library, - * but will lock kernel. + * but will deadlock in the kernel. */ index_ni = ni; } else - index_ni = ntfs_inode_open(ni->vol, - le64_to_cpu(fn->parent_directory)); + index_ni = ntfs_inode_open(ni->vol, + le64_to_cpu(fn->parent_directory)); if (!index_ni) { if (!err) err = errno; - ntfs_log_trace("Failed to open inode with index.\n"); + ntfs_log_perror("Failed to open inode %lld with index", + le64_to_cpu(fn->parent_directory)); continue; } ictx = ntfs_index_ctx_get(index_ni, NTFS_INDEX_I30, 4); if (!ictx) { if (!err) err = errno; - ntfs_log_trace("Failed to get index context.\n"); + ntfs_log_perror("Failed to get index ctx, inode %lld", + (long long)index_ni->mft_no); ntfs_inode_close(index_ni); continue; } @@ -581,7 +588,8 @@ static int ntfs_inode_sync_file_name(ntfs_inode *ni) else err = errno; } - ntfs_log_trace("Index lookup failed.\n"); + ntfs_log_perror("Index lookup failed, inode %lld", + (long long)index_ni->mft_no); ntfs_index_ctx_put(ictx); ntfs_inode_close(index_ni); continue; @@ -599,13 +607,14 @@ static int ntfs_inode_sync_file_name(ntfs_inode *ni) fn->last_access_time = utc2ntfs(ni->last_access_time); ntfs_index_entry_mark_dirty(ictx); ntfs_index_ctx_put(ictx); - if (ni != index_ni) - ntfs_inode_close(index_ni); + if ((ni != index_ni) && ntfs_inode_close(index_ni) && !err) + err = errno; } /* Check for real error occurred. */ if (errno != ENOENT) { err = errno; - ntfs_log_trace("Attribute lookup failed.\n"); + ntfs_log_perror("Attribute lookup failed, inode %lld", + (long long)ni->mft_no); goto err_out; } ntfs_attr_put_search_ctx(ctx); @@ -646,10 +655,11 @@ int ntfs_inode_sync(ntfs_inode *ni) if (!ni) { errno = EINVAL; + ntfs_log_error("Failed to sync NULL inode\n"); return -1; } - ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no); + ntfs_log_trace("Entering for inode %lld\n", (long long)ni->mft_no); /* Update STANDARD_INFORMATION. */ if ((ni->mrec->flags & MFT_RECORD_IN_USE) && ni->nr_extents != -1 && @@ -659,7 +669,8 @@ int ntfs_inode_sync(ntfs_inode *ni) if (err != EIO) err = EBUSY; } - ntfs_log_trace("Failed to sync standard information.\n"); + ntfs_log_perror("Failed to sync standard info (inode %lld)", + (long long)ni->mft_no); } /* Update FILE_NAME's in the index. */ @@ -671,7 +682,8 @@ int ntfs_inode_sync(ntfs_inode *ni) if (err != EIO) err = EBUSY; } - ntfs_log_trace("Failed to sync FILE_NAME attributes.\n"); + ntfs_log_perror("Failed to sync FILE_NAME (inode %lld)", + (long long)ni->mft_no); NInoFileNameSetDirty(ni); } @@ -686,33 +698,37 @@ int ntfs_inode_sync(ntfs_inode *ni) err = errno; if (err != EIO) err = EBUSY; - ntfs_log_trace("Attribute list sync failed (open " - "failed).\n"); + ntfs_log_perror("Attribute list sync failed " + "(open, inode %lld)", + (long long)ni->mft_no); } NInoAttrListSetDirty(ni); - } else { - if (na->data_size == ni->attr_list_size) { - if (ntfs_attr_pwrite(na, 0, ni->attr_list_size, - ni->attr_list) != - ni->attr_list_size) { - if (!err || errno == EIO) { - err = errno; - if (err != EIO) - err = EBUSY; - ntfs_log_trace("Attribute list sync " - "failed (write failed).\n"); - } - NInoAttrListSetDirty(ni); + goto sync_inode; + } + + if (na->data_size == ni->attr_list_size) { + if (ntfs_attr_pwrite(na, 0, ni->attr_list_size, + ni->attr_list) != ni->attr_list_size) { + if (!err || errno == EIO) { + err = errno; + if (err != EIO) + err = EBUSY; + ntfs_log_perror("Attribute list sync " + "failed (write, inode %lld)", + (long long)ni->mft_no); } - } else { - err = EIO; - ntfs_log_trace("Attribute list sync failed (invalid size).\n"); NInoAttrListSetDirty(ni); } - ntfs_attr_close(na); + } else { + err = EIO; + ntfs_log_error("Attribute list sync failed (bad size, " + "inode %lld)\n", (long long)ni->mft_no); + NInoAttrListSetDirty(ni); } + ntfs_attr_close(na); } - + +sync_inode: /* Write this inode out to the $MFT (and $MFTMirr if applicable). */ if (NInoTestAndClearDirty(ni)) { if (ntfs_mft_record_write(ni->vol, ni->mft_no, ni->mrec)) { @@ -722,7 +738,8 @@ int ntfs_inode_sync(ntfs_inode *ni) err = EBUSY; } NInoSetDirty(ni); - ntfs_log_trace("Base MFT record sync failed.\n"); + ntfs_log_perror("MFT record sync failed, inode %lld", + (long long)ni->mft_no); } } @@ -734,18 +751,21 @@ int ntfs_inode_sync(ntfs_inode *ni) ntfs_inode *eni; eni = ni->extent_nis[i]; - if (NInoTestAndClearDirty(eni)) { - if (ntfs_mft_record_write(eni->vol, eni->mft_no, - eni->mrec)) { - if (!err || errno == EIO) { - err = errno; - if (err != EIO) - err = EBUSY; - } - NInoSetDirty(eni); - ntfs_log_trace("Extent MFT record sync " - "failed.\n"); + if (!NInoTestAndClearDirty(eni)) + continue; + + if (ntfs_mft_record_write(eni->vol, eni->mft_no, + eni->mrec)) { + if (!err || errno == EIO) { + err = errno; + if (err != EIO) + err = EBUSY; } + NInoSetDirty(eni); + ntfs_log_perror("Extent MFT record sync failed," + " inode %lld/%lld", + (long long)ni->mft_no, + (long long)eni->mft_no); } } } diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/lcnalloc.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/lcnalloc.c index b1cb10a464..45b76f67af 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/lcnalloc.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/lcnalloc.c @@ -2,8 +2,8 @@ * lcnalloc.c - Cluster (de)allocation code. Originated from the Linux-NTFS project. * * Copyright (c) 2002-2004 Anton Altaparmakov - * Copyright (c) 2004-2007 Szabolcs Szakacsits * Copyright (c) 2004 Yura Pakhuchiy + * Copyright (c) 2004-2007 Szabolcs Szakacsits * * 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 @@ -45,56 +45,91 @@ #include "logging.h" #include "misc.h" -#define NTFS_LCNALLOC_BSIZE 512 +/* + * Plenty possibilities for big optimizations all over in the cluster + * allocation, however at the moment the dominant bottleneck (~ 90%) is + * the update of the mapping pairs which converges to the cubic Faulhaber's + * formula as the function of the number of extents (fragments, runs). + */ +#define NTFS_LCNALLOC_BSIZE 1024 +#define NTFS_LCNALLOC_SKIP NTFS_LCNALLOC_BSIZE -#define NTFS_LCNALLOC_SKIP 4096 - -static void ntfs_cluster_set_zone_pos(LCN zone_start, LCN zone_end, - LCN *zone_pos, LCN tc, LCN bmp_initial_pos) +static void ntfs_cluster_set_zone_pos(LCN start, LCN end, LCN *pos, LCN tc) { - ntfs_log_trace("Before: zone_pos: %lld\n", (long long)*zone_pos); + ntfs_log_trace("pos: %lld tc: %lld\n", (long long)*pos, tc); - if (tc >= zone_end) { - *zone_pos = zone_start; - // FIXME: seems to be bogus and only MFT zone used it - if (!zone_end) - *zone_pos = 0; - } else if ((bmp_initial_pos >= *zone_pos || tc > *zone_pos) && - tc >= zone_start) - *zone_pos = tc; - - ntfs_log_trace("After: zone_pos: %lld\n", (long long)*zone_pos); + if (tc >= end) + *pos = start; + else if (tc >= start) + *pos = tc; } -static int ntfs_cluster_update_zone_pos(ntfs_volume *vol, u8 zone, LCN tc, - LCN bmp_initial_pos) +static void ntfs_cluster_update_zone_pos(ntfs_volume *vol, u8 zone, LCN tc) { ntfs_log_trace("tc = %lld, zone = %d\n", (long long)tc, zone); - switch (zone) { - case 1: - ntfs_cluster_set_zone_pos(vol->mft_lcn, - vol->mft_zone_end, - &vol->mft_zone_pos, - tc, bmp_initial_pos); - break; - case 2: - ntfs_cluster_set_zone_pos(vol->mft_zone_end, - vol->nr_clusters, - &vol->data1_zone_pos, - tc, bmp_initial_pos); - break; - case 4: - ntfs_cluster_set_zone_pos(0, - vol->mft_zone_start, - &vol->data2_zone_pos, - tc, bmp_initial_pos); - break; - default: - ntfs_log_error("Invalid zone: %d\n", zone); - return -1; + if (zone == 1) + ntfs_cluster_set_zone_pos(vol->mft_lcn, vol->mft_zone_end, + &vol->mft_zone_pos, tc); + else if (zone == 2) + ntfs_cluster_set_zone_pos(vol->mft_zone_end, vol->nr_clusters, + &vol->data1_zone_pos, tc); + else /* zone == 4 */ + ntfs_cluster_set_zone_pos(0, vol->mft_zone_start, + &vol->data2_zone_pos, tc); +} + +static s64 max_empty_bit_range(unsigned char *buf, int size) +{ + int i, j, run = 0; + int max_range = 0; + s64 start_pos = -1; + + ntfs_log_trace("Entering"); + + for (i = 0; i < size; i++, buf++) { + + for (j = 0; j < 8; j++) { + + int bit = *buf & (1 << j); + + if (bit) { + if (run > max_range) { + max_range = run; + start_pos = i * 8 + j - run; + } + run = 0; + } else + run++; + } } + if (run > max_range) + start_pos = i * 8 - run; + + return start_pos; +} + +static int bitmap_writeback(ntfs_volume *vol, s64 pos, s64 size, void *b, + u8 *writeback) +{ + s64 written; + + ntfs_log_trace("Entering"); + + if (!*writeback) + return 0; + + *writeback = 0; + + written = ntfs_attr_pwrite(vol->lcnbmp_na, pos, size, b); + if (written != size) { + if (!written) + errno = EIO; + ntfs_log_perror("Bitmap write error (%lld, %lld)", pos, size); + return -1; + } + return 0; } @@ -126,45 +161,33 @@ static int ntfs_cluster_update_zone_pos(ntfs_volume *vol, u8 zone, LCN tc, * expanded to cover the start of the volume in order to reserve space for the * mft bitmap attribute. * - * This is not the prettiest function but the complexity stems from the need of - * implementing the mft vs data zoned approach and from the fact that we have - * access to the lcn bitmap via up to NTFS_LCNALLOC_BSIZE bytes at a time, so we - * need to cope with crossing over boundaries of two buffers. Further, the fact - * that the allocator allows for caller supplied hints as to the location of - * where allocation should begin and the fact that the allocator keeps track of - * where in the data zones the next natural allocation should occur, contribute - * to the complexity of the function. But it should all be worthwhile, because - * this allocator should: 1) be a full implementation of the MFT zone approach - * used by Windows, 2) cause reduction in fragmentation as much as possible, - * and 3) be speedy in allocations (the code is not optimized for speed, but - * the algorithm is, so further speed improvements are probably possible). - * - * FIXME: We should be monitoring cluster allocation and increment the MFT zone - * size dynamically but this is something for the future. We will just cause - * heavier fragmentation by not doing it and I am not even sure Windows would - * grow the MFT zone dynamically, so it might even be correct not to do this. - * The overhead in doing dynamic MFT zone expansion would be very large and - * unlikely worth the effort. (AIA) - * - * TODO: I have added in double the required zone position pointer wrap around - * logic which can be optimized to having only one of the two logic sets. - * However, having the double logic will work fine, but if we have only one of - * the sets and we get it wrong somewhere, then we get into trouble, so - * removing the duplicate logic requires _very_ careful consideration of _all_ - * possible code paths. So at least for now, I am leaving the double logic - - * better safe than sorry... (AIA) + * The complexity stems from the need of implementing the mft vs data zoned + * approach and from the fact that we have access to the lcn bitmap via up to + * NTFS_LCNALLOC_BSIZE bytes at a time, so we need to cope with crossing over + * boundaries of two buffers. Further, the fact that the allocator allows for + * caller supplied hints as to the location of where allocation should begin + * and the fact that the allocator keeps track of where in the data zones the + * next natural allocation should occur, contribute to the complexity of the + * function. But it should all be worthwhile, because this allocator: + * 1) implements MFT zone reservation + * 2) causes reduction in fragmentation. + * The code is not optimized for speed. */ runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count, LCN start_lcn, const NTFS_CLUSTER_ALLOCATION_ZONES zone) { - LCN zone_start, zone_end, bmp_pos, bmp_initial_pos, last_read_pos, lcn; - LCN prev_lcn = 0, prev_run_len = 0, mft_zone_size; + LCN zone_start, zone_end; /* current search range */ + LCN last_read_pos, lcn; + LCN bmp_pos; /* current bit position inside the bitmap */ + LCN prev_lcn = 0, prev_run_len = 0; s64 clusters, br; runlist *rl = NULL, *trl; - u8 *buf, *byte; + u8 *buf, *byte, bit, writeback; + u8 pass = 1; /* 1: inside zone; 2: start of zone */ + u8 search_zone; /* 4: data2 (start) 1: mft (middle) 2: data1 (end) */ + u8 done_zones = 0; + u8 has_guess, used_zone_pos; int err = 0, rlpos, rlsize, buf_size; - u8 pass, done_zones, search_zone, need_writeback, bit; - u8 first_try = 1; ntfs_log_trace("Entering with count = 0x%llx, start_lcn = 0x%llx, " "zone = %s_ZONE.\n", (long long)count, (long long) @@ -181,490 +204,280 @@ runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count, /* Return empty runlist if @count == 0 */ if (!count) { rl = ntfs_malloc(0x1000); - if (!rl) - return NULL; - rl[0].vcn = start_vcn; - rl[0].lcn = LCN_RL_NOT_MAPPED; - rl[0].length = 0; + if (rl) { + rl[0].vcn = start_vcn; + rl[0].lcn = LCN_RL_NOT_MAPPED; + rl[0].length = 0; + } return rl; } - /* Allocate memory. */ buf = ntfs_malloc(NTFS_LCNALLOC_BSIZE); if (!buf) return NULL; /* - * If no specific @start_lcn was requested, use the current data zone - * position, otherwise use the requested @start_lcn but make sure it - * lies outside the mft zone. Also set done_zones to 0 (no zones done) - * and pass depending on whether we are starting inside a zone (1) or - * at the beginning of a zone (2). If requesting from the MFT_ZONE, - * we either start at the current position within the mft zone or at - * the specified position. If the latter is out of bounds then we start - * at the beginning of the MFT_ZONE. - */ - done_zones = 0; - pass = 1; - /* - * zone_start and zone_end are the current search range. search_zone - * is 1 for mft zone, 2 for data zone 1 (end of mft zone till end of - * volume) and 4 for data zone 2 (start of volume till start of mft - * zone). + * If no @start_lcn was requested, use the current zone + * position otherwise use the requested @start_lcn. */ + has_guess = 1; zone_start = start_lcn; + if (zone_start < 0) { if (zone == DATA_ZONE) zone_start = vol->data1_zone_pos; else zone_start = vol->mft_zone_pos; - if (!zone_start) { - /* - * Zone starts at beginning of volume which means a - * single pass is sufficient. - */ - pass = 2; - } - } else if (zone == DATA_ZONE && zone_start >= vol->mft_zone_start && - zone_start < vol->mft_zone_end) { - zone_start = vol->mft_zone_end; - /* - * Starting at beginning of data1_zone which means a single - * pass in this zone is sufficient. - */ - pass = 2; - } else if (zone == MFT_ZONE && (zone_start < vol->mft_zone_start || - zone_start >= vol->mft_zone_end)) { - zone_start = vol->mft_lcn; - if (!vol->mft_zone_end) - zone_start = 0; - /* - * Starting at beginning of volume which means a single pass - * is sufficient. - */ - pass = 2; + has_guess = 0; } - if (zone == MFT_ZONE) { + + used_zone_pos = has_guess ? 0 : 1; + + if (!zone_start || zone_start == vol->mft_zone_start || + zone_start == vol->mft_zone_end) + pass = 2; + + if (zone_start < vol->mft_zone_start) { + zone_end = vol->mft_zone_start; + search_zone = 4; + } else if (zone_start < vol->mft_zone_end) { zone_end = vol->mft_zone_end; search_zone = 1; - } else /* if (zone == DATA_ZONE) */ { - /* Skip searching the mft zone. */ - done_zones |= 1; - if (zone_start >= vol->mft_zone_end) { - zone_end = vol->nr_clusters; - search_zone = 2; - } else { - zone_end = vol->mft_zone_start; - search_zone = 4; - } + } else { + zone_end = vol->nr_clusters; + search_zone = 2; } - /* - * bmp_pos is the current bit position inside the bitmap. We use - * bmp_initial_pos to determine whether or not to do a zone switch. - */ - bmp_pos = bmp_initial_pos = zone_start; + + bmp_pos = zone_start; - /* Loop until all clusters are allocated, i.e. clusters == 0. */ + /* Loop until all clusters are allocated. */ clusters = count; rlpos = rlsize = 0; while (1) { - ntfs_log_trace("Start of outer while loop: done_zones = 0x%x, " - "search_zone = %i, pass = %i, zone_start = " - "0x%llx, zone_end = 0x%llx, bmp_initial_pos = " - "0x%llx, bmp_pos = 0x%llx, rlpos = %i, rlsize = " - "%i.\n", done_zones, search_zone, pass, - (long long)zone_start, (long long)zone_end, - (long long)bmp_initial_pos, (long long)bmp_pos, - rlpos, rlsize); - /* Loop until we run out of free clusters. */ last_read_pos = bmp_pos >> 3; - ntfs_log_trace("last_read_pos = 0x%llx.\n", (long long)last_read_pos); - br = ntfs_attr_pread(vol->lcnbmp_na, last_read_pos, NTFS_LCNALLOC_BSIZE, buf); + br = ntfs_attr_pread(vol->lcnbmp_na, last_read_pos, + NTFS_LCNALLOC_BSIZE, buf); if (br <= 0) { - if (!br) { - /* Reached end of attribute. */ - ntfs_log_trace("End of attribute reached. Skipping " - "to zone_pass_done.\n"); + if (!br) goto zone_pass_done; - } err = errno; - ntfs_log_perror("ntfs_attr_pread() failed"); + ntfs_log_perror("Reading $BITMAP failed"); goto err_ret; } /* - * We might have read less than NTFS_LCNALLOC_BSIZE bytes if we are close to - * the end of the attribute. + * We might have read less than NTFS_LCNALLOC_BSIZE bytes + * if we are close to the end of the attribute. */ buf_size = (int)br << 3; lcn = bmp_pos & 7; bmp_pos &= ~7; - need_writeback = 0; - ntfs_log_trace("Before inner while loop: buf_size = %i, lcn = " - "0x%llx, bmp_pos = 0x%llx, need_writeback = %i.\n", - buf_size, (long long)lcn, (long long)bmp_pos, - need_writeback); - while (lcn < buf_size && lcn + bmp_pos < zone_end) { + writeback = 0; + + while (1) { byte = buf + (lcn >> 3); - ntfs_log_trace("In inner while loop: buf_size = %i, lcn = " - "0x%llx, bmp_pos = 0x%llx, " - "need_writeback = %i, byte ofs = 0x%x, " - "*byte = 0x%x.\n", buf_size, - (long long)lcn, (long long)bmp_pos, - need_writeback, (unsigned int)(lcn >> 3), - (unsigned int)*byte); - /* Skip full bytes. */ - if (*byte == 0xff) { - lcn = (lcn + 8) & ~7; - ntfs_log_trace("continuing while loop 1.\n"); - if (first_try) { - first_try = 0; - lcn += NTFS_LCNALLOC_SKIP; - } - continue; - } bit = 1 << (lcn & 7); - ntfs_log_trace("bit = %i.\n", bit); - /* If the bit is already set, go onto the next one. */ - if (*byte & bit) { - lcn++; - ntfs_log_trace("continuing while loop 2.\n"); - if (first_try) { - first_try = 0; - lcn += NTFS_LCNALLOC_SKIP; + if (has_guess) { + if (*byte & bit) { + has_guess = 0; + break; } + } else { + lcn = max_empty_bit_range(buf, br); + if (lcn < 0) + break; + has_guess = 1; continue; } + + /* First free bit is at lcn + bmp_pos. */ + /* Reallocate memory if necessary. */ if ((rlpos + 2) * (int)sizeof(runlist) >= rlsize) { - ntfs_log_trace("Reallocating space.\n"); - if (!rl) - ntfs_log_trace("First free bit is at LCN = " - "0x%llx.\n", (long long)(lcn + bmp_pos)); rlsize += 4096; - trl = (runlist*)realloc(rl, rlsize); + trl = realloc(rl, rlsize); if (!trl) { err = ENOMEM; - ntfs_log_perror("Failed to allocate memory"); + ntfs_log_perror("realloc() failed"); goto wb_err_ret; } rl = trl; - ntfs_log_trace("Reallocated memory, rlsize = " - "0x%x.\n", rlsize); } + /* Allocate the bitmap bit. */ *byte |= bit; - /* We need to write this bitmap buffer back to disk! */ - need_writeback = 1; - ntfs_log_trace("*byte = 0x%x, need_writeback is set.\n", - (unsigned int)*byte); + writeback = 1; + /* * Coalesce with previous run if adjacent LCNs. * Otherwise, append a new run. */ - ntfs_log_trace("Adding run (lcn 0x%llx, len 0x%llx), " - "prev_lcn = 0x%llx, lcn = 0x%llx, " - "bmp_pos = 0x%llx, prev_run_len = " - "0x%llx, rlpos = %i.\n", - (long long)(lcn + bmp_pos), 1LL, - (long long)prev_lcn, (long long)lcn, - (long long)bmp_pos, - (long long)prev_run_len, rlpos); - if (prev_lcn == lcn + bmp_pos - prev_run_len && rlpos) { - ntfs_log_trace("Coalescing to run (lcn 0x%llx, len " - "0x%llx).\n", - (long long)rl[rlpos - 1].lcn, - (long long) rl[rlpos - 1].length); + if (prev_lcn == lcn + bmp_pos - prev_run_len && rlpos) rl[rlpos - 1].length = ++prev_run_len; - ntfs_log_trace("Run now (lcn 0x%llx, len 0x%llx), " - "prev_run_len = 0x%llx.\n", - (long long)rl[rlpos - 1].lcn, - (long long)rl[rlpos - 1].length, - (long long)prev_run_len); - } else { - if (rlpos) { - ntfs_log_trace("Adding new run, (previous " - "run lcn 0x%llx, len 0x%llx).\n", - (long long) rl[rlpos - 1].lcn, - (long long) rl[rlpos - 1].length); + else { + if (rlpos) rl[rlpos].vcn = rl[rlpos - 1].vcn + prev_run_len; - } else { - ntfs_log_trace("Adding new run, is first run.\n"); + else rl[rlpos].vcn = start_vcn; - } + rl[rlpos].lcn = prev_lcn = lcn + bmp_pos; rl[rlpos].length = prev_run_len = 1; rlpos++; } + /* Done? */ if (!--clusters) { - if (ntfs_cluster_update_zone_pos(vol, - search_zone, lcn + bmp_pos + 1 - + NTFS_LCNALLOC_SKIP, - bmp_initial_pos)) { - free(rl); - free(buf); - return NULL; - } + if (used_zone_pos) + ntfs_cluster_update_zone_pos(vol, + search_zone, lcn + bmp_pos + 1 + + NTFS_LCNALLOC_SKIP); goto done_ret; } + lcn++; } - bmp_pos += buf_size; - ntfs_log_trace("After inner while loop: buf_size = 0x%x, lcn = " - "0x%llx, bmp_pos = 0x%llx, need_writeback = %i.\n", - buf_size, (long long)lcn, - (long long)bmp_pos, need_writeback); - if (need_writeback) { - s64 bw; - ntfs_log_trace("Writing back.\n"); - need_writeback = 0; - bw = ntfs_attr_pwrite(vol->lcnbmp_na, last_read_pos, - br, buf); - if (bw != br) { - if (bw == -1) - err = errno; - else - err = EIO; - ntfs_log_perror("Bitmap writeback failed in " - "read next buffer code path"); - goto err_ret; - } + + if (bitmap_writeback(vol, last_read_pos, br, buf, &writeback)) { + err = errno; + goto err_ret; } - if (bmp_pos < zone_end) { - ntfs_log_trace("Continuing outer while loop, bmp_pos = " - "0x%llx, zone_end = 0x%llx.\n", - (long long)bmp_pos, - (long long)zone_end); + + if (!used_zone_pos) { + + used_zone_pos = 1; + + if (search_zone == 1) + zone_start = vol->mft_zone_pos; + else if (search_zone == 2) + zone_start = vol->data1_zone_pos; + else + zone_start = vol->data2_zone_pos; + + if (!zone_start || zone_start == vol->mft_zone_start || + zone_start == vol->mft_zone_end) + pass = 2; + bmp_pos = zone_start; + } else + bmp_pos += buf_size; + + if (bmp_pos < zone_end) continue; - } -zone_pass_done: /* Finished with the current zone pass. */ - ntfs_log_trace("At zone_pass_done, pass = %i.\n", pass); + +zone_pass_done: + ntfs_log_trace("Finished current zone pass(%i).\n", pass); if (pass == 1) { - /* - * Now do pass 2, scanning the first part of the zone - * we omitted in pass 1. - */ + pass = 2; zone_end = zone_start; - switch (search_zone) { - case 1: /* mft_zone */ + + if (search_zone == 1) zone_start = vol->mft_zone_start; - break; - case 2: /* data1_zone */ + else if (search_zone == 2) zone_start = vol->mft_zone_end; - break; - case 4: /* data2_zone */ + else zone_start = 0; - break; - default: - NTFS_BUG("switch (search_zone) 2"); - } + /* Sanity check. */ if (zone_end < zone_start) zone_end = zone_start; + bmp_pos = zone_start; - ntfs_log_trace("Continuing outer while loop, pass = 2, " - "zone_start = 0x%llx, zone_end = " - "0x%llx, bmp_pos = 0x%llx.\n", - zone_start, zone_end, bmp_pos); + continue; - } /* pass == 2 */ + } + /* pass == 2 */ done_zones_check: - ntfs_log_trace("At done_zones_check, search_zone = %i, done_zones " - "before = 0x%x, done_zones after = 0x%x.\n", - search_zone, done_zones, done_zones | search_zone); done_zones |= search_zone; if (done_zones < 7) { ntfs_log_trace("Switching zone.\n"); - /* Now switch to the next zone we haven't done yet. */ pass = 1; if (rlpos) { - LCN tc; - - tc = rl[rlpos - 1].lcn + rl[rlpos - 1].length - + NTFS_LCNALLOC_SKIP; + LCN tc = tc = rl[rlpos - 1].lcn + + rl[rlpos - 1].length + NTFS_LCNALLOC_SKIP; - if (ntfs_cluster_update_zone_pos(vol, - search_zone, tc, bmp_initial_pos)) - return NULL; + if (used_zone_pos) + ntfs_cluster_update_zone_pos(vol, + search_zone, tc); } switch (search_zone) { case 1: ntfs_log_trace("Zone switch: mft -> data1\n"); switch_to_data1_zone: search_zone = 2; - zone_start = bmp_initial_pos = - vol->data1_zone_pos; + zone_start = vol->data1_zone_pos; zone_end = vol->nr_clusters; if (zone_start == vol->mft_zone_end) pass = 2; - if (zone_start >= zone_end) { - vol->data1_zone_pos = zone_start = - vol->mft_zone_end; - pass = 2; - } break; case 2: ntfs_log_trace("Zone switch: data1 -> data2\n"); search_zone = 4; - zone_start = bmp_initial_pos = - vol->data2_zone_pos; + zone_start = vol->data2_zone_pos; zone_end = vol->mft_zone_start; if (!zone_start) pass = 2; - if (zone_start >= zone_end) { - vol->data2_zone_pos = zone_start = - bmp_initial_pos = 0; - pass = 2; - } break; case 4: - ntfs_log_trace("Zone switch: data2 -> data1\n"); - goto switch_to_data1_zone; /* See above. */ - default: - NTFS_BUG("switch (search_zone) 3"); + if (!(done_zones & 2)) { + ntfs_log_trace("data2 -> data1\n"); + goto switch_to_data1_zone; + } + ntfs_log_trace("Zone switch: data2 -> mft\n"); + search_zone = 1; + zone_start = vol->mft_zone_pos; + zone_end = vol->mft_zone_end; + if (!zone_start == vol->mft_zone_start) + pass = 2; + break; } - ntfs_log_trace("After zone switch, search_zone = %i, pass = " - "%i, bmp_initial_pos = 0x%llx, " - "zone_start = 0x%llx, zone_end = " - "0x%llx.\n", search_zone, pass, - (long long)bmp_initial_pos, - (long long)zone_start, - (long long)zone_end); + bmp_pos = zone_start; + if (zone_start == zone_end) { - ntfs_log_trace("Empty zone, going to " - "done_zones_check.\n"); - /* Empty zone. Don't bother searching it. */ + ntfs_log_trace("Empty zone, skipped.\n"); goto done_zones_check; } - ntfs_log_trace("Continuing outer while loop.\n"); + continue; - } /* done_zones == 7 */ - ntfs_log_trace("All zones are finished.\n"); - /* - * All zones are finished! If DATA_ZONE, shrink mft zone. If - * MFT_ZONE, we have really run out of space. - */ - mft_zone_size = vol->mft_zone_end - vol->mft_zone_start; - ntfs_log_trace("vol->mft_zone_start = 0x%llx, vol->mft_zone_end = " - "0x%llx, mft_zone_size = 0x%llx.\n", - (long long)vol->mft_zone_start, - (long long)vol->mft_zone_end, - (long long)mft_zone_size); - if (zone == MFT_ZONE || mft_zone_size <= 0) { - ntfs_log_trace("No free clusters left, going to err_ret.\n"); - /* Really no more space left on device. */ - err = ENOSPC; - goto err_ret; - } /* zone == DATA_ZONE && mft_zone_size > 0 */ - ntfs_log_trace("Shrinking mft zone.\n"); - zone_end = vol->mft_zone_end; - mft_zone_size >>= 1; - if (mft_zone_size > 0) - vol->mft_zone_end = vol->mft_zone_start + mft_zone_size; - else /* mft zone and data2 zone no longer exist. */ - vol->data2_zone_pos = vol->mft_zone_start = - vol->mft_zone_end = 0; - if (vol->mft_zone_pos >= vol->mft_zone_end) { - vol->mft_zone_pos = vol->mft_lcn; - if (!vol->mft_zone_end) - vol->mft_zone_pos = 0; } - bmp_pos = zone_start = bmp_initial_pos = - vol->data1_zone_pos = vol->mft_zone_end; - search_zone = 2; - pass = 2; - done_zones &= ~2; - ntfs_log_trace("After shrinking mft zone, mft_zone_size = 0x%llx, " - "vol->mft_zone_start = 0x%llx, " - "vol->mft_zone_end = 0x%llx, vol->mft_zone_pos " - "= 0x%llx, search_zone = 2, pass = 2, " - "dones_zones = 0x%x, zone_start = 0x%llx, " - "zone_end = 0x%llx, vol->data1_zone_pos = " - "0x%llx, continuing outer while loop.\n", - (long long)mft_zone_size, - (long long)vol->mft_zone_start, - (long long)vol->mft_zone_end, - (long long)vol->mft_zone_pos, - done_zones, - (long long)zone_start, - (long long)zone_end, - (long long)vol->data1_zone_pos); + + ntfs_log_trace("All zones are finished, no space on device.\n"); + err = ENOSPC; + goto err_ret; } - ntfs_log_debug("After outer while loop.\n"); done_ret: ntfs_log_debug("At done_ret.\n"); /* Add runlist terminator element. */ rl[rlpos].vcn = rl[rlpos - 1].vcn + rl[rlpos - 1].length; rl[rlpos].lcn = LCN_RL_NOT_MAPPED; rl[rlpos].length = 0; - if (need_writeback) { - s64 bw; - ntfs_log_trace("Writing back.\n"); - need_writeback = 0; - bw = ntfs_attr_pwrite(vol->lcnbmp_na, last_read_pos, br, buf); - if (bw != br) { - if (bw < 0) - err = errno; - else - err = EIO; - ntfs_log_perror("Bitmap writeback failed"); - goto err_ret; - } + if (bitmap_writeback(vol, last_read_pos, br, buf, &writeback)) { + err = errno; + goto err_ret; } done_err_ret: ntfs_log_debug("At done_err_ret (follows done_ret).\n"); free(buf); - /* Done! */ if (!err) return rl; - ntfs_log_perror("Failed to allocate clusters"); + ntfs_log_trace("Failed to allocate clusters (%d)", errno); errno = err; return NULL; + wb_err_ret: ntfs_log_trace("At wb_err_ret.\n"); - if (need_writeback) { - s64 bw; - ntfs_log_trace("Writing back.\n"); - need_writeback = 0; - bw = ntfs_attr_pwrite(vol->lcnbmp_na, last_read_pos, br, buf); - if (bw != br) { - if (bw < 0) - err = errno; - else - err = EIO; - ntfs_log_trace("Bitmap writeback failed in error code path " - "with error code %i.\n", err); - } - } + if (bitmap_writeback(vol, last_read_pos, br, buf, &writeback)) + err = errno; err_ret: ntfs_log_trace("At err_ret.\n"); if (rl) { - if (err == ENOSPC) { - ntfs_log_trace("err = ENOSPC, first free lcn = 0x%llx, could " - "allocate up to = 0x%llx clusters.\n", - (long long)rl[0].lcn, - (long long)count - clusters); - } /* Add runlist terminator element. */ rl[rlpos].vcn = rl[rlpos - 1].vcn + rl[rlpos - 1].length; rl[rlpos].lcn = LCN_RL_NOT_MAPPED; rl[rlpos].length = 0; - /* Deallocate all allocated clusters. */ - ntfs_log_trace("Deallocating allocated clusters.\n"); ntfs_cluster_free_from_rl(vol, rl); - /* Free the runlist. */ free(rl); rl = NULL; - } else { - if (err == ENOSPC) { - ntfs_log_trace("No space left at all, err = ENOSPC, first " - "free lcn = 0x%llx.\n", - (long long)vol->data1_zone_pos); - } } - ntfs_log_trace("rl = NULL, going to done_err_ret.\n"); goto done_err_ret; } 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 9b11926952..3fb04f3dc8 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/logfile.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/logfile.c @@ -699,55 +699,31 @@ BOOL ntfs_is_logfile_clean(ntfs_attr *log_na, RESTART_PAGE_HEADER *rp) */ int ntfs_empty_logfile(ntfs_attr *na) { - s64 len, pos, count; + s64 pos, count; char buf[NTFS_BUF_SIZE]; ntfs_log_trace("Entering.\n"); + if (NVolLogFileEmpty(na->ni->vol)) return 0; - /* The $DATA attribute of the $LogFile has to be non-resident. */ if (!NAttrNonResident(na)) { errno = EIO; - ntfs_log_perror("$LogFile $DATA attribute is resident!?!\n"); + ntfs_log_perror("Resident $LogFile $DATA attribute"); return -1; } - /* Get length of $LogFile contents. */ - len = na->data_size; - if (!len) { - ntfs_log_debug("$LogFile has zero length, no disk write " - "needed.\n"); - return 0; - } - - /* Read $LogFile until its end. We do this as a check for correct - length thus making sure we are decompressing the mapping pairs - array correctly and hence writing below is safe as well. */ - pos = 0; - while ((count = ntfs_attr_pread(na, pos, NTFS_BUF_SIZE, buf)) > 0) - pos += count; - - if (count == -1 || pos != len) { - ntfs_log_error("Amount of $LogFile data read does not " - "correspond to expected length!\n"); - if (count != -1) - errno = EIO; - return -1; - } - - /* Fill the buffer with 0xff's. */ memset(buf, -1, NTFS_BUF_SIZE); - /* Set the $DATA attribute. */ pos = 0; - while ((count = len - pos) > 0) { + while ((count = na->data_size - pos) > 0) { + if (count > NTFS_BUF_SIZE) count = NTFS_BUF_SIZE; - if ((count = ntfs_attr_pwrite(na, pos, count, buf)) <= 0) { - ntfs_log_perror("Failed to set the $LogFile attribute " - "value.\n"); + count = ntfs_attr_pwrite(na, pos, count, buf); + if (count <= 0) { + ntfs_log_perror("Failed to reset $LogFile"); if (count != -1) errno = EIO; return -1; @@ -755,7 +731,7 @@ int ntfs_empty_logfile(ntfs_attr *na) pos += count; } - /* Set the flag so we do not have to do it again on remount. */ NVolSetLogFileEmpty(na->ni->vol); + return 0; } 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 9ffe8819d7..743794e8f0 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/logging.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/logging.c @@ -220,7 +220,7 @@ static FILE * ntfs_log_get_stream(u32 level) } return stream; -#endif +#endif } /** @@ -348,43 +348,33 @@ int ntfs_log_redirect(const char *function, const char *file, * num Number of output characters */ + #ifdef HAVE_SYSLOG_H + +#define LOG_LINE_LEN 512 + int ntfs_log_handler_syslog(const char *function __attribute__((unused)), - const char *file, __attribute__((unused)) int line, - u32 level __attribute__((unused)), void *data __attribute__((unused)), - const char *format, va_list args) + const char *file __attribute__((unused)), + int line __attribute__((unused)), u32 level, + void *data __attribute__((unused)), + const char *format, va_list args) { - int ret = 0; - int olderr = errno; + char log[LOG_LINE_LEN]; + int ret, olderr = errno; - if ((ntfs_log.flags & NTFS_LOG_FLAG_ONLYNAME) && - (strchr(file, PATH_SEP))) /* Abbreviate the filename */ - file = strrchr(file, PATH_SEP) + 1; -#if 0 /* FIXME: Implement this all. */ - if (ntfs_log.flags & NTFS_LOG_FLAG_PREFIX) /* Prefix the output */ - ret += fprintf(stream, "%s", ntfs_log_get_prefix(level)); - - if (ntfs_log.flags & NTFS_LOG_FLAG_FILENAME) /* Source filename */ - ret += fprintf(stream, "%s ", file); - - if (ntfs_log.flags & NTFS_LOG_FLAG_LINE) /* Source line number */ - ret += fprintf(stream, "(%d) ", line); - - if ((ntfs_log.flags & NTFS_LOG_FLAG_FUNCTION) || /* Source function */ - (level & NTFS_LOG_LEVEL_TRACE)) - ret += fprintf(stream, "%s(): ", function); - - ret += vfprintf(stream, format, args); - - if (level & NTFS_LOG_LEVEL_PERROR) { - if (reason) - ret += fprintf(stream, ": %s\n", reason); - else - ret += fprintf(stream, ": %s\n", strerror(olderr)); + ret = vsnprintf(log, LOG_LINE_LEN, format, args); + if (ret < 0) { + vsyslog(LOG_NOTICE, format, args); + return 1; } -#endif - vsyslog(LOG_NOTICE, format, args); - ret = 1; /* FIXME: caclulate how many bytes had been written. */ + + if ((LOG_LINE_LEN > ret + 3) && (level & NTFS_LOG_LEVEL_PERROR)) { + strncat(log, ": ", LOG_LINE_LEN - ret - 1); + strncat(log, strerror(olderr), LOG_LINE_LEN - (ret + 3)); + ret = strlen(log); + } + + syslog(LOG_NOTICE, "%s", log); errno = olderr; return ret; @@ -543,7 +533,7 @@ int ntfs_log_handler_stdout(const char *function, const char *file, data = stdout; return ntfs_log_handler_fprintf(function, file, line, level, data, format, args); -#endif +#endif } /** @@ -612,7 +602,7 @@ int ntfs_log_handler_stderr(const char *function, const char *file, data = stderr; return ntfs_log_handler_fprintf(function, file, line, level, data, format, args); -#endif +#endif } 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 028a682450..8220eade41 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/mft.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/mft.c @@ -1007,8 +1007,7 @@ static int ntfs_mft_data_extend_allocation(ntfs_volume *vol) // this extent is not required to find the mft record in // question. errno = EOPNOTSUPP; - ntfs_log_perror("Not enough space to extended mft data " - "attribute.\n"); + ntfs_log_perror("Not enough space to extended mft data"); goto undo_alloc; } mp_rebuilt = TRUE; @@ -1098,6 +1097,115 @@ undo_alloc: return -1; } + +static int ntfs_mft_record_init(ntfs_volume *vol, s64 size) +{ + int ret = -1; + ntfs_attr *mft_na, *mftbmp_na; + s64 old_data_initialized, old_data_size; + ntfs_attr_search_ctx *ctx; + + ntfs_log_trace("Entering\n"); + + /* 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 + * attribute until it covers the allocated record. The loop is only + * actually traversed more than once when a freshly formatted volume + * is first written to so it optimizes away nicely in the common case. + */ + ntfs_log_debug("Status of mft data before extension: " + "allocated_size 0x%llx, data_size 0x%llx, " + "initialized_size 0x%llx.\n", + (long long)mft_na->allocated_size, + (long long)mft_na->data_size, + (long long)mft_na->initialized_size); + while (size > mft_na->allocated_size) { + if (ntfs_mft_data_extend_allocation(vol)) + goto out; + ntfs_log_debug("Status of mft data after allocation extension: " + "allocated_size 0x%llx, data_size 0x%llx, " + "initialized_size 0x%llx.\n", + (long long)mft_na->allocated_size, + (long long)mft_na->data_size, + (long long)mft_na->initialized_size); + } + + old_data_initialized = mft_na->initialized_size; + old_data_size = mft_na->data_size; + + /* + * Extend mft data initialized size (and data size of course) to reach + * the allocated mft record, formatting the mft records along the way. + * Note: We only modify the ntfs_attr structure as that is all that is + * needed by ntfs_mft_record_format(). We will update the attribute + * record itself in one fell swoop later on. + */ + while (size > mft_na->initialized_size) { + s64 ll2 = mft_na->initialized_size >> vol->mft_record_size_bits; + mft_na->initialized_size += vol->mft_record_size; + if (mft_na->initialized_size > mft_na->data_size) + mft_na->data_size = mft_na->initialized_size; + ntfs_log_debug("Initializing mft record 0x%llx.\n", (long long)ll2); + if (ntfs_mft_record_format(vol, ll2) < 0) { + ntfs_log_error("Failed to format mft record.\n"); + goto undo_data_init; + } + } + + /* Update the mft data attribute record to reflect the new sizes. */ + ctx = ntfs_attr_get_search_ctx(mft_na->ni, NULL); + if (!ctx) { + ntfs_log_error("Failed to get search context.\n"); + goto undo_data_init; + } + if (ntfs_attr_lookup(mft_na->type, mft_na->name, mft_na->name_len, 0, + 0, NULL, 0, ctx)) { + ntfs_log_error("Failed to find first attribute extent of " + "mft data attribute.\n"); + ntfs_attr_put_search_ctx(ctx); + goto undo_data_init; + } + ctx->attr->initialized_size = cpu_to_sle64(mft_na->initialized_size); + ctx->attr->data_size = cpu_to_sle64(mft_na->data_size); + + /* Ensure the changes make it to disk. */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + ntfs_log_debug("Status of mft data after mft record initialization: " + "allocated_size 0x%llx, data_size 0x%llx, " + "initialized_size 0x%llx.\n", + (long long)mft_na->allocated_size, + (long long)mft_na->data_size, + (long long)mft_na->initialized_size); + + /* Sanity checks. */ + if (mft_na->data_size > mft_na->allocated_size || + mft_na->initialized_size > mft_na->data_size) + NTFS_BUG("mft_na sanity checks failed"); + // BUG_ON(mft_na->initialized_size > mft_na->data_size); + // BUG_ON(mft_na->data_size > mft_na->allocated_size); + + /* Sync MFT to minimize data loss if there won't be clean unmount. */ + if (ntfs_inode_sync(mft_na->ni)) { + ntfs_log_error("Failed to sync $MFT."); + goto undo_data_init; + } + + ret = 0; +out: + return ret; + +undo_data_init: + mft_na->initialized_size = old_data_initialized; + mft_na->data_size = old_data_size; + goto out; +} + /** * ntfs_mft_record_alloc - allocate an mft record on an ntfs volume * @vol: volume on which to allocate the mft record @@ -1183,11 +1291,9 @@ undo_alloc: */ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, ntfs_inode *base_ni) { - s64 ll, bit, old_data_initialized, old_data_size; + s64 ll, bit; ntfs_attr *mft_na, *mftbmp_na; - ntfs_attr_search_ctx *ctx; MFT_RECORD *m; - ATTR_RECORD *a; ntfs_inode *ni; int err; u16 seq_no, usn; @@ -1202,8 +1308,10 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, ntfs_inode *base_ni) errno = EINVAL; return NULL; } + mft_na = vol->mft_na; mftbmp_na = vol->mftbmp_na; +retry: bit = ntfs_mft_bitmap_find_free_rec(vol, base_ni); if (bit >= 0) { ntfs_log_debug("Found free record (#1), bit 0x%llx.\n", @@ -1269,99 +1377,18 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, ntfs_inode *base_ni) ntfs_log_debug("Found free record (#3), bit 0x%llx.\n", (long long)bit); found_free_rec: /* @bit is the found free mft record, allocate it in the mft bitmap. */ - ntfs_log_debug("At found_free_rec.\n"); if (ntfs_bitmap_set_bit(mftbmp_na, bit)) { ntfs_log_error("Failed to allocate bit in mft bitmap.\n"); goto err_out; } ntfs_log_debug("Set bit 0x%llx in mft bitmap.\n", (long long)bit); + /* The mft bitmap is now uptodate. Deal with mft data attribute now. */ ll = (bit + 1) << vol->mft_record_size_bits; - if (ll <= mft_na->initialized_size) { - ntfs_log_debug("Allocated mft record already initialized.\n"); - goto mft_rec_already_initialized; - } - ntfs_log_debug("Initializing allocated mft record.\n"); - /* - * The mft record is outside the initialized data. Extend the mft data - * attribute until it covers the allocated record. The loop is only - * actually traversed more than once when a freshly formatted volume is - * first written to so it optimizes away nicely in the common case. - */ - ntfs_log_debug("Status of mft data before extension: " - "allocated_size 0x%llx, data_size 0x%llx, " - "initialized_size 0x%llx.\n", - (long long)mft_na->allocated_size, - (long long)mft_na->data_size, - (long long)mft_na->initialized_size); - while (ll > mft_na->allocated_size) { - if (ntfs_mft_data_extend_allocation(vol)) + if (ll > mft_na->initialized_size) + if (ntfs_mft_record_init(vol, ll) < 0) goto undo_mftbmp_alloc; - ntfs_log_debug("Status of mft data after allocation extension: " - "allocated_size 0x%llx, data_size 0x%llx, " - "initialized_size 0x%llx.\n", - (long long)mft_na->allocated_size, - (long long)mft_na->data_size, - (long long)mft_na->initialized_size); - } - old_data_initialized = mft_na->initialized_size; - old_data_size = mft_na->data_size; - /* - * Extend mft data initialized size (and data size of course) to reach - * the allocated mft record, formatting the mft records along the way. - * Note: We only modify the ntfs_attr structure as that is all that is - * needed by ntfs_mft_record_format(). We will update the attribute - * record itself in one fell swoop later on. - */ - while (ll > mft_na->initialized_size) { - s64 ll2 = mft_na->initialized_size >> vol->mft_record_size_bits; - mft_na->initialized_size += vol->mft_record_size; - if (mft_na->initialized_size > mft_na->data_size) - mft_na->data_size = mft_na->initialized_size; - ntfs_log_debug("Initializing mft record 0x%llx.\n", (long long)ll2); - err = ntfs_mft_record_format(vol, ll2); - if (err) { - ntfs_log_error("Failed to format mft record.\n"); - goto undo_data_init; - } - } - /* Update the mft data attribute record to reflect the new sizes. */ - ctx = ntfs_attr_get_search_ctx(mft_na->ni, NULL); - if (!ctx) { - ntfs_log_error("Failed to get search context.\n"); - goto undo_data_init; - } - if (ntfs_attr_lookup(mft_na->type, mft_na->name, mft_na->name_len, 0, - 0, NULL, 0, ctx)) { - ntfs_log_error("Failed to find first attribute extent of " - "mft data attribute.\n"); - ntfs_attr_put_search_ctx(ctx); - goto undo_data_init; - } - a = ctx->attr; - a->initialized_size = cpu_to_sle64(mft_na->initialized_size); - a->data_size = cpu_to_sle64(mft_na->data_size); - /* Ensure the changes make it to disk. */ - ntfs_inode_mark_dirty(ctx->ntfs_ino); - ntfs_attr_put_search_ctx(ctx); - ntfs_log_debug("Status of mft data after mft record initialization: " - "allocated_size 0x%llx, data_size 0x%llx, " - "initialized_size 0x%llx.\n", - (long long)mft_na->allocated_size, - (long long)mft_na->data_size, - (long long)mft_na->initialized_size); - /* Sanity checks. */ - if (mft_na->data_size > mft_na->allocated_size || - mft_na->initialized_size > mft_na->data_size) - NTFS_BUG("mft_na sanity checks failed"); - // BUG_ON(mft_na->initialized_size > mft_na->data_size); - // BUG_ON(mft_na->data_size > mft_na->allocated_size); - /* Sync MFT to minimize data loss if there won't be clean unmount. */ - if (ntfs_inode_sync(mft_na->ni)) { - ntfs_log_error("Failed to sync $MFT."); - goto undo_data_init; - } -mft_rec_already_initialized: + /* * We now have allocated and initialized the mft record. Need to read * it from disk and re-format it, preserving the sequence number if it @@ -1373,29 +1400,22 @@ mft_rec_already_initialized: goto undo_mftbmp_alloc; if (ntfs_mft_record_read(vol, bit, m)) { - err = errno; - ntfs_log_error("Failed to read mft record.\n"); + ntfs_log_perror("Error reading mft %lld", (long long)bit); free(m); - errno = err; goto undo_mftbmp_alloc; } /* Sanity check that the mft record is really not in use. */ if (ntfs_is_file_record(m->magic) && (m->flags & MFT_RECORD_IN_USE)) { - ntfs_log_error("Mft record 0x%llx was marked unused in " - "mft bitmap but is marked used itself. " - "Corrupt filesystem or library bug! " - "Run chkdsk immediately!\n", (long long)bit); + ntfs_log_error("Inode %lld is used but it wasn't marked in " + "$MFT bitmap. Fixed.\n", (long long)bit); free(m); - errno = EIO; - goto undo_mftbmp_alloc; + goto retry; } seq_no = m->sequence_number; usn = *(u16*)((u8*)m + le16_to_cpu(m->usa_ofs)); if (ntfs_mft_record_layout(vol, bit, m)) { - err = errno; ntfs_log_error("Failed to re-format mft record.\n"); free(m); - errno = err; goto undo_mftbmp_alloc; } if (le16_to_cpu(seq_no)) @@ -1408,10 +1428,8 @@ mft_rec_already_initialized: /* Now need to open an ntfs inode for the mft record. */ ni = ntfs_inode_allocate(vol); if (!ni) { - err = errno; ntfs_log_error("Failed to allocate buffer for inode.\n"); free(m); - errno = err; goto undo_mftbmp_alloc; } ni->mft_no = bit; @@ -1465,9 +1483,7 @@ mft_rec_already_initialized: ntfs_log_debug("Returning opened, allocated %sinode 0x%llx.\n", base_ni ? "extent " : "", (long long)bit); return ni; -undo_data_init: - mft_na->initialized_size = old_data_initialized; - mft_na->data_size = old_data_size; + undo_mftbmp_alloc: err = errno; if (ntfs_bitmap_clear_bit(mftbmp_na, bit)) diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/misc.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/misc.c index 71173fdeb6..043482ff75 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/misc.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/misc.c @@ -2,10 +2,6 @@ #include "config.h" #endif -#ifdef HAVE_STDLIB_H -#include -#endif - #ifdef HAVE_STDLIB_H #include #endif @@ -39,16 +35,17 @@ void *ntfs_malloc(size_t size) } #if defined(__BEOS__) || defined(__HAIKU__) +#include int ntfs_snprintf(char *buff, size_t size, const char *format, ...) { int ret; - char buffer[BUFSIZ]; + char buffer[BUF_SIZE]; va_list args; va_start(args, format); - memset(buffer,0,BUFSIZ); + memset(buffer,0,BUF_SIZE); ret = sprintf(buffer, format, args); va_end(args); strncpy(buff,buffer,size); return ret; } -#endif \ No newline at end of file +#endif diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/misc.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/misc.h index b204c68052..2282d900c9 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/misc.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/misc.h @@ -5,7 +5,9 @@ void *ntfs_calloc(size_t size); void *ntfs_malloc(size_t size); #if defined(__BEOS__) || defined(__HAIKU__) +#define BUF_SIZE 16384 +int ntfs_snprintf(char *buff, size_t size, const char *format, ...); #define snprintf ntfs_snprintf -#endif /* defined(__BEOS__) || defined(__HAIKU__) */ -#endif /* _NTFS_MISC_H_ */ +#endif +#endif /* _NTFS_MISC_H_ */ diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/unix_io.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/unix_io.c index 2ab9faa906..573097075f 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/unix_io.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/unix_io.c @@ -113,7 +113,7 @@ static int ntfs_device_unix_io_open(struct ntfs_device *dev, int flags) NDevSetReadOnly(dev); /* locking not implemented in BeOS */ -#if !defined(__BEOS__) && !defined(__HAIKU__) +#if !defined(__BEOS__) && !defined(__HAIKU__) memset(&flk, 0, sizeof(flk)); if (NDevReadOnly(dev)) flk.l_type = F_RDLCK; @@ -153,25 +153,27 @@ static int ntfs_device_unix_io_close(struct ntfs_device *dev) if (!NDevOpen(dev)) { errno = EBADF; + ntfs_log_perror("Device %s is not open", dev->d_name); return -1; } if (NDevDirty(dev)) - fsync(DEV_FD(dev)); - + if (fsync(DEV_FD(dev))) { + ntfs_log_perror("Failed to fsync device %s", dev->d_name); + return -1; + } /* locking not implemented in BeOS */ -#if !defined(__BEOS__) && !defined(__HAIKU__) - /* Release exclusive (mandatory) lock on the whole device. */ +#if !defined(__BEOS__) && !defined(__HAIKU__) memset(&flk, 0, sizeof(flk)); flk.l_type = F_UNLCK; flk.l_whence = SEEK_SET; flk.l_start = flk.l_len = 0LL; if (fcntl(DEV_FD(dev), F_SETLK, &flk)) - ntfs_log_perror("ntfs_device_unix_io_close: Warning: Could not " - "unlock %s", dev->d_name); -#endif - /* Close the file descriptor and clear our open flag. */ - if (close(DEV_FD(dev))) + ntfs_log_perror("Could not unlock %s", dev->d_name); +#endif + if (close(DEV_FD(dev))) { + ntfs_log_perror("Failed to close device %s", dev->d_name); return -1; + } NDevClearOpen(dev); free(dev->d_private); dev->d_private = NULL; @@ -288,13 +290,16 @@ static s64 ntfs_device_unix_io_pwrite(struct ntfs_device *dev, const void *buf, */ static int ntfs_device_unix_io_sync(struct ntfs_device *dev) { + int res = 0; + if (!NDevReadOnly(dev)) { - int res = fsync(DEV_FD(dev)); - if (!res) + res = fsync(DEV_FD(dev)); + if (res) + ntfs_log_perror("Failed to sync device %s", dev->d_name); + else NDevClearDirty(dev); - return res; } - return 0; + return res; } /** 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 84db665f06..a3ee7ec840 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/volume.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/volume.c @@ -79,6 +79,35 @@ ntfs_volume *ntfs_volume_alloc(void) return calloc(1, sizeof(ntfs_volume)); } + +static void ntfs_attr_free(ntfs_attr **na) +{ + if (na && *na) { + ntfs_attr_close(*na); + *na = NULL; + } else + ntfs_log_error("Tried to free NULL attribute pointer (%p)\n", na); +} + +static int ntfs_inode_free(ntfs_inode **ni) +{ + int ret = -1; + + if (ni && *ni) { + ret = ntfs_inode_close(*ni); + *ni = NULL; + } else + ntfs_log_error("Tried to free NULL inode pointer (%p)\n", ni); + + return ret; +} + +static void ntfs_error_set(int *err) +{ + if (!*err) + *err = errno; +} + /** * __ntfs_volume_release - Destroy an NTFS volume object * @v: @@ -87,41 +116,51 @@ ntfs_volume *ntfs_volume_alloc(void) * * Returns: */ -static void __ntfs_volume_release(ntfs_volume *v) +static int __ntfs_volume_release(ntfs_volume *v) { + int err = 0; + + if (ntfs_inode_free(&v->vol_ni)) + ntfs_error_set(&err); + /* + * FIXME: Inodes must be synced before closing + * attributes, otherwise unmount could fail. + */ if (v->lcnbmp_ni && NInoDirty(v->lcnbmp_ni)) ntfs_inode_sync(v->lcnbmp_ni); - if (v->vol_ni) - ntfs_inode_close(v->vol_ni); - if (v->lcnbmp_na) - ntfs_attr_close(v->lcnbmp_na); - if (v->lcnbmp_ni) - ntfs_inode_close(v->lcnbmp_ni); + ntfs_attr_free(&v->lcnbmp_na); + if (ntfs_inode_free(&v->lcnbmp_ni)) + ntfs_error_set(&err); + if (v->mft_ni && NInoDirty(v->mft_ni)) ntfs_inode_sync(v->mft_ni); - if (v->mftbmp_na) - ntfs_attr_close(v->mftbmp_na); - if (v->mft_na) - ntfs_attr_close(v->mft_na); - if (v->mft_ni) - ntfs_inode_close(v->mft_ni); + ntfs_attr_free(&v->mftbmp_na); + ntfs_attr_free(&v->mft_na); + if (ntfs_inode_free(&v->mft_ni)) + ntfs_error_set(&err); + if (v->mftmirr_ni && NInoDirty(v->mftmirr_ni)) ntfs_inode_sync(v->mftmirr_ni); - if (v->mftmirr_na) - ntfs_attr_close(v->mftmirr_na); - if (v->mftmirr_ni) - ntfs_inode_close(v->mftmirr_ni); + ntfs_attr_free(&v->mftmirr_na); + if (ntfs_inode_free(&v->mftmirr_ni)) + ntfs_error_set(&err); + if (v->dev) { struct ntfs_device *dev = v->dev; - dev->d_ops->sync(dev); + if (dev->d_ops->sync(dev)) + ntfs_error_set(&err); if (dev->d_ops->close(dev)) - ntfs_log_perror("Failed to close the device"); + ntfs_error_set(&err); } + free(v->vol_name); free(v->upcase); free(v->attrdef); free(v); + + errno = err; + return errno ? -1 : 0; } static void ntfs_attr_setup_flag(ntfs_inode *ni) @@ -457,32 +496,11 @@ ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, unsigned long flags) "sector size. This may affect performance " "but should be harmless otherwise. Error: " "%s\n", strerror(errno)); - /* - * We now initialize the cluster allocator. - * - * FIXME: Move this to its own function? (AIA) - */ + + /* We now initialize the cluster allocator. */ - // TODO: Make this tunable at mount time. (AIA) - vol->mft_zone_multiplier = 1; - - /* Determine the size of the MFT zone. */ - mft_zone_size = vol->nr_clusters; - switch (vol->mft_zone_multiplier) { /* % of volume size in clusters */ - case 4: - mft_zone_size >>= 1; /* 50% */ - break; - case 3: - mft_zone_size = mft_zone_size * 3 >> 3; /* 37.5% */ - break; - case 2: - mft_zone_size >>= 2; /* 25% */ - break; - /* case 1: */ - default: - mft_zone_size >>= 3; /* 12.5% */ - break; - } + mft_zone_size = min(vol->nr_clusters >> 3, /* 12.5% */ + 200 * 1000 * 1024 >> vol->cluster_size_bits); /* Setup the mft zone. */ vol->mft_zone_start = vol->mft_zone_pos = vol->mft_lcn; @@ -573,23 +591,27 @@ static int ntfs_volume_check_logfile(ntfs_volume *vol) RESTART_PAGE_HEADER *rp = NULL; int err = 0; - if ((ni = ntfs_inode_open(vol, FILE_LogFile)) == NULL) { + ni = ntfs_inode_open(vol, FILE_LogFile); + if (!ni) { ntfs_log_perror("Failed to open inode FILE_LogFile"); errno = EIO; return -1; } - if ((na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0)) == NULL) { + + na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); + if (!na) { ntfs_log_perror("Failed to open $FILE_LogFile/$DATA"); err = EIO; - goto exit; + goto out; } + if (!ntfs_check_logfile(na, &rp) || !ntfs_is_logfile_clean(na, rp)) err = EOPNOTSUPP; free(rp); -exit: - if (na) - ntfs_attr_close(na); - ntfs_inode_close(ni); + ntfs_attr_close(na); +out: + if (ntfs_inode_close(ni)) + ntfs_error_set(&err); if (err) { errno = err; return -1; @@ -643,7 +665,10 @@ static ntfs_inode *ntfs_hiberfile_open(ntfs_volume *vol) goto out; } out: - ntfs_inode_close(ni_root); + if (ntfs_inode_close(ni_root)) { + ntfs_inode_close(ni_hibr); + ni_hibr = NULL; + } free(unicode); return ni_hibr; } @@ -663,7 +688,7 @@ static int ntfs_volume_check_hiberfile(ntfs_volume *vol) { ntfs_inode *ni; ntfs_attr *na = NULL; - int i, bytes_read, ret = -1; + int i, bytes_read, err; char *buf = NULL; ni = ntfs_hiberfile_open(vol); @@ -707,13 +732,16 @@ static int ntfs_volume_check_hiberfile(ntfs_volume *vol) } } /* All right, all header bytes are zero */ - ret = 0; + errno = 0; out: if (na) ntfs_attr_close(na); free(buf); - ntfs_inode_close(ni); - return ret; + err = errno; + if (ntfs_inode_close(ni)) + ntfs_error_set(&err); + errno = err; + return errno ? -1 : 0; } /** @@ -785,20 +813,11 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) l = ntfs_attr_mst_pread(vol->mftmirr_na, 0, vol->mftmirr_size, vol->mft_record_size, m2); if (l != vol->mftmirr_size) { - if (l == 4) - vol->mftmirr_size = 4; - else { - if (l == -1) - ntfs_log_perror("Failed to read $MFTMirr"); - else { - ntfs_log_error("Failed to read $MFTMirr " - "unexpected length (%d != %lld)." - "\n", vol->mftmirr_size, - (long long)l); - errno = EIO; - } + if (l == -1) { + ntfs_log_perror("Failed to read $MFTMirr"); goto error_exit; } + vol->mftmirr_size = l; } ntfs_log_debug("Comparing $MFTMirr to $MFT... "); for (i = 0; i < vol->mftmirr_size; ++i) { @@ -929,8 +948,10 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) /* Done with the $UpCase mft record. */ ntfs_log_debug(OK); ntfs_attr_close(na); - if (ntfs_inode_close(ni)) - ntfs_log_perror("Failed to close inode, leaking memory"); + if (ntfs_inode_close(ni)) { + ntfs_log_perror("Failed to close $UpCase"); + goto error_exit; + } /* * Now load $Volume and set the version information and flags in the @@ -1089,17 +1110,24 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) /* Done with the $AttrDef mft record. */ ntfs_log_debug(OK); ntfs_attr_close(na); - if (ntfs_inode_close(ni)) - ntfs_log_perror("Failed to close inode, leaking memory"); + if (ntfs_inode_close(ni)) { + ntfs_log_perror("Failed to close $AttrDef"); + goto error_exit; + } /* * Check for dirty logfile and hibernated Windows. * We care only about read-write mounts. */ if (!(flags & MS_RDONLY)) { - if (ntfs_volume_check_logfile(vol) < 0) - goto error_exit; if (ntfs_volume_check_hiberfile(vol) < 0) goto error_exit; + if (ntfs_volume_check_logfile(vol) < 0) { + if (!(flags & MS_FORCE)) + goto error_exit; + ntfs_log_info("WARNING: Forced mount, reset $LogFile.\n"); + if (ntfs_logfile_reset(vol)) + goto error_exit; + } } return vol; @@ -1172,42 +1200,6 @@ ntfs_volume *ntfs_mount(const char *name __attribute__((unused)), #endif } -/** - * ntfs_device_umount - close ntfs volume - * @vol: address of ntfs_volume structure of volume to close - * @force: if true force close the volume even if it is busy - * - * Deallocate all structures (including @vol itself) associated with the ntfs - * volume @vol. - * - * Note it is up to the caller to destroy the device associated with the volume - * being unmounted after this function returns. - * - * Return 0 on success. On error return -1 with errno set appropriately - * (most likely to one of EAGAIN, EBUSY or EINVAL). The EAGAIN error means that - * an operation is in progress and if you try the close later the operation - * might be completed and the close succeed. - * - * If @force is true (i.e. not zero) this function will close the volume even - * if this means that data might be lost. - * - * @vol must have previously been returned by a call to ntfs_device_mount(). - * - * @vol itself is deallocated and should no longer be dereferenced after this - * function returns success. If it returns an error then nothing has been done - * so it is safe to continue using @vol. - */ -int ntfs_device_umount(ntfs_volume *vol, - const BOOL force __attribute__((unused))) -{ - if (!vol) { - errno = EINVAL; - return -1; - } - __ntfs_volume_release(vol); - return 0; -} - /** * ntfs_umount - close ntfs volume * @vol: address of ntfs_volume structure of volume to close @@ -1230,19 +1222,19 @@ int ntfs_device_umount(ntfs_volume *vol, * function returns success. If it returns an error then nothing has been done * so it is safe to continue using @vol. */ -int ntfs_umount(ntfs_volume *vol, - const BOOL force __attribute__((unused))) +int ntfs_umount(ntfs_volume *vol, const BOOL force __attribute__((unused))) { struct ntfs_device *dev; + int ret; if (!vol) { errno = EINVAL; return -1; } dev = vol->dev; - __ntfs_volume_release(vol); + ret = __ntfs_volume_release(vol); ntfs_device_free(dev); - return 0; + return ret; } #ifdef HAVE_MNTENT_H @@ -1420,12 +1412,14 @@ int ntfs_logfile_reset(ntfs_volume *vol) return -1; } - if ((ni = ntfs_inode_open(vol, FILE_LogFile)) == NULL) { - ntfs_log_perror("Failed to open inode FILE_LogFile."); + ni = ntfs_inode_open(vol, FILE_LogFile); + if (!ni) { + ntfs_log_perror("Failed to open inode FILE_LogFile"); return -1; } - if ((na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0)) == NULL) { + na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); + if (!na) { eo = errno; ntfs_log_perror("Failed to open $FILE_LogFile/$DATA"); goto error_exit; @@ -1433,10 +1427,10 @@ int ntfs_logfile_reset(ntfs_volume *vol) if (ntfs_empty_logfile(na)) { eo = errno; - ntfs_log_perror("Failed to empty $FILE_LogFile/$DATA"); ntfs_attr_close(na); goto error_exit; } + ntfs_attr_close(na); return ntfs_inode_close(ni); diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/volume.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/volume.h index 3b8cde634b..e0d6aa0613 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/volume.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/volume.h @@ -63,6 +63,10 @@ #define MS_EXCLUSIVE 0x08000000 +#ifndef MS_FORCE +#define MS_FORCE 0x10000000 +#endif + /* Forward declaration */ typedef struct _ntfs_volume ntfs_volume; @@ -221,7 +225,6 @@ extern ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, extern ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags); -extern int ntfs_device_umount(ntfs_volume *vol, const BOOL force); extern ntfs_volume *ntfs_mount(const char *name, unsigned long flags); extern int ntfs_umount(ntfs_volume *vol, const BOOL force); diff --git a/src/add-ons/kernel/file_systems/ntfs/settings/ntfs b/src/add-ons/kernel/file_systems/ntfs/settings/ntfs index f39ca1ae67..3a8e574252 100644 --- a/src/add-ons/kernel/file_systems/ntfs/settings/ntfs +++ b/src/add-ons/kernel/file_systems/ntfs/settings/ntfs @@ -1,4 +1,4 @@ -# Sample settings file for the ntfs plugin +# Sample settings file for the ntfs addon # # This file should be moved to the directory # /boot/home/config/settings/kernel/drivers/