diff --git a/src/add-ons/kernel/file_systems/ntfs/Jamfile b/src/add-ons/kernel/file_systems/ntfs/Jamfile index 1b61b11623..d16ff756dc 100644 --- a/src/add-ons/kernel/file_systems/ntfs/Jamfile +++ b/src/add-ons/kernel/file_systems/ntfs/Jamfile @@ -1,12 +1,15 @@ SubDir HAIKU_TOP src add-ons kernel file_systems ntfs ; SubDirHdrs [ FDirName $(SUBDIR) libntfs ] ; +SubDirHdrs [ FDirName $(SUBDIR) utils ] ; SubDirCcFlags -DHAVE_CONFIG_H=1 ; SubDirC++Flags -DHAVE_CONFIG_H=1 ; UsePrivateHeaders kernel ; +SEARCH_SOURCE += [ FDirName $(SUBDIR) utils ] ; + KernelAddon ntfs : attributes.c fake_attributes.c @@ -16,6 +19,10 @@ KernelAddon ntfs : volume_util.c fs_func.c kernel_interface.c + attrdef.c + sd.c + boot.c + mkntfs.c : libntfs.a ; 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 d379a24a40..2916761f4f 100644 --- a/src/add-ons/kernel/file_systems/ntfs/fs_func.c +++ b/src/add-ons/kernel/file_systems/ntfs/fs_func.c @@ -44,7 +44,7 @@ #include "ntfs.h" #include "volume_util.h" -static const char* kNTFSUnnamed = {"NTFS Unnamed"}; +extern int mkntfs_main(const char *devpath, const char *label); typedef struct identify_cookie { NTFS_BOOT_SECTOR boot; @@ -272,27 +272,6 @@ fs_identify_partition(int fd, partition_data *partition, void **_cookie) } } - // generate a more or less descriptive name for unnamed volume - if (cookie->label[0]=='\0') { - double size; - off_t diskSize = sle64_to_cpu(boot.number_of_sectors) - * le16_to_cpu(boot.bpb.bytes_per_sector); - off_t divisor = 1ULL << 40; - char unit = 'T'; - if (diskSize < divisor) { - divisor = 1UL << 30; - unit = 'G'; - if (diskSize < divisor) { - divisor = 1UL << 20; - unit = 'M'; - } - } - - size = (double)((10 * diskSize + divisor - 1) / divisor); - snprintf(cookie->label, MAX_PATH - 1, "%g %cB NTFS Volume", - size / 10, unit); - } - *_cookie = cookie; return 0.8f; @@ -321,6 +300,42 @@ fs_free_identify_partition_cookie(partition_data *partition, void *_cookie) } +uint32 +fs_get_supported_operations(partition_data* partition, uint32 mask) +{ + return B_DISK_SYSTEM_SUPPORTS_INITIALIZING + | B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME + | B_DISK_SYSTEM_SUPPORTS_WRITING; +} + + +status_t +fs_initialize(int fd, partition_id partitionID, const char* name, + const char* parameterString, off_t partitionSize, disk_job_id job) +{ + char devpath[MAX_PATH]; + status_t result = B_OK; + + TRACE("fs_initialize - [%s] - [%s]\n",name, parameterString); + + update_disk_device_job_progress(job, 0); + + if (ioctl(fd, B_GET_PATH_FOR_DEVICE, devpath) != 0) { + mkntfs_main(devpath, name); + } else { + return B_BAD_VALUE; + } + + result = scan_partition(partitionID); + if (result != B_OK) + return result; + + update_disk_device_job_progress(job, 1); + + return result; +} + + status_t fs_mount(fs_volume *_vol, const char *device, ulong flags, const char *args, ino_t *_rootID) @@ -488,9 +503,24 @@ fs_rfsstat(fs_volume *_vol, struct fs_info *fss) if (fss->volume_name[i] != ' ') break; } - if (i < 0) - strcpy(fss->volume_name, kNTFSUnnamed); - else + if (i < 0) { + double size; + off_t diskSize = ns->ntvol->nr_clusters * ns->ntvol->cluster_size; + off_t divisor = 1ULL << 40; + char unit = 'T'; + if (diskSize < divisor) { + divisor = 1UL << 30; + unit = 'G'; + if (diskSize < divisor) { + divisor = 1UL << 20; + unit = 'M'; + } + } + + size = (double)((10 * diskSize + divisor - 1) / divisor); + snprintf(fss->volume_name, sizeof(fss->volume_name), "%g %cB NTFS Volume", + size / 10, unit); + } else fss->volume_name[i + 1] = 0; strcpy(fss->fsh_name, "NTFS"); diff --git a/src/add-ons/kernel/file_systems/ntfs/fs_func.h b/src/add-ons/kernel/file_systems/ntfs/fs_func.h index dfa9fcc2b9..9727977b7c 100644 --- a/src/add-ons/kernel/file_systems/ntfs/fs_func.h +++ b/src/add-ons/kernel/file_systems/ntfs/fs_func.h @@ -106,6 +106,9 @@ status_t fs_fsync(fs_volume *_vol, fs_vnode *_node); status_t fs_rename(fs_volume *volume, fs_vnode *fromDir, const char *fromName, fs_vnode *toDir, const char *toName); status_t fs_unlink(fs_volume *volume, fs_vnode *dir, const char *name); +status_t fs_initialize(int fd, partition_id partitionID, const char* name, + const char* parameterString, off_t partitionSize, disk_job_id job); +uint32 fs_get_supported_operations(partition_data* partition, uint32 mask); #endif // NTFS_FS_FUNC_H diff --git a/src/add-ons/kernel/file_systems/ntfs/kernel_interface.c b/src/add-ons/kernel/file_systems/ntfs/kernel_interface.c index 85d7f18842..d4128fceab 100644 --- a/src/add-ons/kernel/file_systems/ntfs/kernel_interface.c +++ b/src/add-ons/kernel/file_systems/ntfs/kernel_interface.c @@ -170,7 +170,9 @@ static file_system_module_info sNTFSFileSystem = { "ntfs", // short_name "NTFS File System", // pretty_name - B_DISK_SYSTEM_SUPPORTS_WRITING, // DDM flags + B_DISK_SYSTEM_SUPPORTS_INITIALIZING + | B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME + | B_DISK_SYSTEM_SUPPORTS_WRITING, // DDM flags // scanning fs_identify_partition, @@ -179,6 +181,27 @@ static file_system_module_info sNTFSFileSystem = { NULL, // free_partition_content_cookie() &fs_mount, + /* capability querying operations */ + &fs_get_supported_operations, + + NULL, // validate_resize + NULL, // validate_move + NULL, // validate_set_content_name + NULL, // validate_set_content_parameters + NULL, // validate_initialize, + + /* shadow partition modification */ + NULL, // shadow_changed + + /* writing */ + NULL, // defragment + NULL, // repair + NULL, // resize + NULL, // move + NULL, // set_content_name + NULL, // set_content_parameters + fs_initialize, + NULL }; diff --git a/src/add-ons/kernel/file_systems/ntfs/utils/attrdef.c b/src/add-ons/kernel/file_systems/ntfs/utils/attrdef.c new file mode 100644 index 0000000000..36501e5c27 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ntfs/utils/attrdef.c @@ -0,0 +1,168 @@ +#include "attrdef.h" + +/** + * attrdef_ntfs3x_array + */ +const unsigned char attrdef_ntfs3x_array[2560] = { +0x24, 0x00, 0x53, 0x00, 0x54, 0x00, 0x41, 0x00, 0x4E, 0x00, 0x44, 0x00, 0x41, 0x00, 0x52, 0x00, +0x44, 0x00, 0x5F, 0x00, 0x49, 0x00, 0x4E, 0x00, 0x46, 0x00, 0x4F, 0x00, 0x52, 0x00, 0x4D, 0x00, +0x41, 0x00, 0x54, 0x00, 0x49, 0x00, 0x4F, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, +0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x24, 0x00, 0x41, 0x00, 0x54, 0x00, 0x54, 0x00, 0x52, 0x00, 0x49, 0x00, 0x42, 0x00, 0x55, 0x00, +0x54, 0x00, 0x45, 0x00, 0x5F, 0x00, 0x4C, 0x00, 0x49, 0x00, 0x53, 0x00, 0x54, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0x24, 0x00, 0x46, 0x00, 0x49, 0x00, 0x4C, 0x00, 0x45, 0x00, 0x5F, 0x00, 0x4E, 0x00, 0x41, 0x00, +0x4D, 0x00, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, +0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x24, 0x00, 0x4F, 0x00, 0x42, 0x00, 0x4A, 0x00, 0x45, 0x00, 0x43, 0x00, 0x54, 0x00, 0x5F, 0x00, +0x49, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x24, 0x00, 0x53, 0x00, 0x45, 0x00, 0x43, 0x00, 0x55, 0x00, 0x52, 0x00, 0x49, 0x00, 0x54, 0x00, +0x59, 0x00, 0x5F, 0x00, 0x44, 0x00, 0x45, 0x00, 0x53, 0x00, 0x43, 0x00, 0x52, 0x00, 0x49, 0x00, +0x50, 0x00, 0x54, 0x00, 0x4F, 0x00, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0x24, 0x00, 0x56, 0x00, 0x4F, 0x00, 0x4C, 0x00, 0x55, 0x00, 0x4D, 0x00, 0x45, 0x00, 0x5F, 0x00, +0x4E, 0x00, 0x41, 0x00, 0x4D, 0x00, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, +0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x24, 0x00, 0x56, 0x00, 0x4F, 0x00, 0x4C, 0x00, 0x55, 0x00, 0x4D, 0x00, 0x45, 0x00, 0x5F, 0x00, +0x49, 0x00, 0x4E, 0x00, 0x46, 0x00, 0x4F, 0x00, 0x52, 0x00, 0x4D, 0x00, 0x41, 0x00, 0x54, 0x00, +0x49, 0x00, 0x4F, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, +0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x24, 0x00, 0x44, 0x00, 0x41, 0x00, 0x54, 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0x24, 0x00, 0x49, 0x00, 0x4E, 0x00, 0x44, 0x00, 0x45, 0x00, 0x58, 0x00, 0x5F, 0x00, 0x52, 0x00, +0x4F, 0x00, 0x4F, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0x24, 0x00, 0x49, 0x00, 0x4E, 0x00, 0x44, 0x00, 0x45, 0x00, 0x58, 0x00, 0x5F, 0x00, 0x41, 0x00, +0x4C, 0x00, 0x4C, 0x00, 0x4F, 0x00, 0x43, 0x00, 0x41, 0x00, 0x54, 0x00, 0x49, 0x00, 0x4F, 0x00, +0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0x24, 0x00, 0x42, 0x00, 0x49, 0x00, 0x54, 0x00, 0x4D, 0x00, 0x41, 0x00, 0x50, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0x24, 0x00, 0x52, 0x00, 0x45, 0x00, 0x50, 0x00, 0x41, 0x00, 0x52, 0x00, 0x53, 0x00, 0x45, 0x00, +0x5F, 0x00, 0x50, 0x00, 0x4F, 0x00, 0x49, 0x00, 0x4E, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x24, 0x00, 0x45, 0x00, 0x41, 0x00, 0x5F, 0x00, 0x49, 0x00, 0x4E, 0x00, 0x46, 0x00, 0x4F, 0x00, +0x52, 0x00, 0x4D, 0x00, 0x41, 0x00, 0x54, 0x00, 0x49, 0x00, 0x4F, 0x00, 0x4E, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xD0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, +0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x24, 0x00, 0x45, 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, +0x24, 0x00, 0x4C, 0x00, 0x4F, 0x00, 0x47, 0x00, 0x47, 0x00, 0x45, 0x00, 0x44, 0x00, 0x5F, 0x00, +0x55, 0x00, 0x54, 0x00, 0x49, 0x00, 0x4C, 0x00, 0x49, 0x00, 0x54, 0x00, 0x59, 0x00, 0x5F, 0x00, +0x53, 0x00, 0x54, 0x00, 0x52, 0x00, 0x45, 0x00, 0x41, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + diff --git a/src/add-ons/kernel/file_systems/ntfs/utils/attrdef.h b/src/add-ons/kernel/file_systems/ntfs/utils/attrdef.h new file mode 100644 index 0000000000..0c664394e0 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ntfs/utils/attrdef.h @@ -0,0 +1,7 @@ +#ifndef _NTFS_ATTRDEF_H_ +#define _NTFS_ATTRDEF_H_ + +extern const unsigned char attrdef_ntfs3x_array[2560]; + +#endif /* _NTFS_ATTRDEF_H_ */ + diff --git a/src/add-ons/kernel/file_systems/ntfs/utils/boot.c b/src/add-ons/kernel/file_systems/ntfs/utils/boot.c new file mode 100644 index 0000000000..9272be9fb1 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ntfs/utils/boot.c @@ -0,0 +1,268 @@ +#include "boot.h" + +/** + * boot_array - the first 4136 bytes of $Boot + * + * The first 4136 bytes of $Boot. The rest is just zero. Total 8192 bytes. + */ +const unsigned char boot_array[4136] = { +235, 82, 144, 78, 84, 70, 83, 32, 32, 32, 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 250, 51, 192, 142, 208, 188, 0, 124, 251, 104, 192, 7, + 31, 30, 104, 102, 0, 203, 136, 22, 14, 0, 102, 129, 62, 3, 0, 78, + 84, 70, 83, 117, 21, 180, 65, 187, 170, 85, 205, 19, 114, 12, 129, 251, + 85, 170, 117, 6, 247, 193, 1, 0, 117, 3, 233, 210, 0, 30, 131, 236, + 24, 104, 26, 0, 180, 72, 138, 22, 14, 0, 139, 244, 22, 31, 205, 19, +159, 131, 196, 24, 158, 88, 31, 114, 225, 59, 6, 11, 0, 117, 219, 163, + 15, 0, 193, 46, 15, 0, 4, 30, 90, 51, 219, 185, 0, 32, 43, 200, +102, 255, 6, 17, 0, 3, 22, 15, 0, 142, 194, 255, 6, 22, 0, 232, + 64, 0, 43, 200, 119, 239, 184, 0, 187, 205, 26, 102, 35, 192, 117, 45, +102, 129, 251, 84, 67, 80, 65, 117, 36, 129, 249, 2, 1, 114, 30, 22, +104, 7, 187, 22, 104, 112, 14, 22, 104, 9, 0, 102, 83, 102, 83, 102, + 85, 22, 22, 22, 104, 184, 1, 102, 97, 14, 7, 205, 26, 233, 106, 1, +144, 144, 102, 96, 30, 6, 102, 161, 17, 0, 102, 3, 6, 28, 0, 30, +102, 104, 0, 0, 0, 0, 102, 80, 6, 83, 104, 1, 0, 104, 16, 0, +180, 66, 138, 22, 14, 0, 22, 31, 139, 244, 205, 19, 102, 89, 91, 90, +102, 89, 102, 89, 31, 15, 130, 22, 0, 102, 255, 6, 17, 0, 3, 22, + 15, 0, 142, 194, 255, 14, 22, 0, 117, 188, 7, 31, 102, 97, 195, 160, +248, 1, 232, 8, 0, 160, 251, 1, 232, 2, 0, 235, 254, 180, 1, 139, +240, 172, 60, 0, 116, 9, 180, 14, 187, 7, 0, 205, 16, 235, 242, 195, + 13, 10, 65, 32, 100, 105, 115, 107, 32, 114, 101, 97, 100, 32, 101, 114, +114, 111, 114, 32, 111, 99, 99, 117, 114, 114, 101, 100, 0, 13, 10, 66, + 79, 79, 84, 77, 71, 82, 32, 105, 115, 32, 109, 105, 115, 115, 105, 110, +103, 0, 13, 10, 66, 79, 79, 84, 77, 71, 82, 32, 105, 115, 32, 99, +111, 109, 112, 114, 101, 115, 115, 101, 100, 0, 13, 10, 80, 114, 101, 115, +115, 32, 67, 116, 114, 108, 43, 65, 108, 116, 43, 68, 101, 108, 32, 116, +111, 32, 114, 101, 115, 116, 97, 114, 116, 13, 10, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 128, 157, 178, 202, 0, 0, 85, 170, + 7, 0, 66, 0, 79, 0, 79, 0, 84, 0, 77, 0, 71, 0, 82, 0, + 4, 0, 36, 0, 73, 0, 51, 0, 48, 0, 0, 224, 0, 0, 0, 48, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 235, 34, 144, 144, 5, 0, 78, 0, 84, 0, + 76, 0, 68, 0, 82, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 102, 15, 183, 6, 11, 0, +102, 15, 182, 30, 13, 0, 102, 247, 227, 102, 163, 82, 2, 102, 139, 14, + 64, 0, 128, 249, 0, 15, 143, 14, 0, 246, 217, 102, 184, 1, 0, 0, + 0, 102, 211, 224, 235, 8, 144, 102, 161, 82, 2, 102, 247, 225, 102, 163, +102, 2, 102, 15, 183, 30, 11, 0, 102, 51, 210, 102, 247, 243, 102, 163, + 86, 2, 232, 149, 4, 102, 139, 14, 78, 2, 102, 137, 14, 38, 2, 102, + 3, 14, 102, 2, 102, 137, 14, 42, 2, 102, 3, 14, 102, 2, 102, 137, + 14, 46, 2, 102, 3, 14, 102, 2, 102, 137, 14, 62, 2, 102, 3, 14, +102, 2, 102, 137, 14, 70, 2, 102, 184, 144, 0, 0, 0, 102, 139, 14, + 38, 2, 232, 131, 9, 102, 11, 192, 15, 132, 83, 254, 102, 163, 50, 2, +102, 184, 160, 0, 0, 0, 102, 139, 14, 42, 2, 232, 106, 9, 102, 163, + 54, 2, 102, 184, 176, 0, 0, 0, 102, 139, 14, 46, 2, 232, 88, 9, +102, 163, 58, 2, 102, 161, 50, 2, 102, 11, 192, 15, 132, 32, 254, 103, +128, 120, 8, 0, 15, 133, 23, 254, 103, 102, 141, 80, 16, 103, 3, 66, + 4, 103, 102, 15, 182, 72, 12, 102, 137, 14, 114, 2, 103, 102, 139, 72, + 8, 102, 137, 14, 110, 2, 102, 161, 110, 2, 102, 15, 183, 14, 11, 0, +102, 51, 210, 102, 247, 241, 102, 163, 118, 2, 102, 161, 70, 2, 102, 3, + 6, 110, 2, 102, 163, 74, 2, 102, 131, 62, 54, 2, 0, 15, 132, 29, + 0, 102, 131, 62, 58, 2, 0, 15, 132, 196, 253, 102, 139, 30, 58, 2, + 30, 7, 102, 139, 62, 74, 2, 102, 161, 46, 2, 232, 224, 1, 102, 15, +183, 14, 0, 2, 102, 184, 2, 2, 0, 0, 232, 34, 8, 102, 11, 192, + 15, 133, 22, 0, 102, 15, 183, 14, 90, 2, 102, 184, 92, 2, 0, 0, +232, 12, 8, 102, 11, 192, 15, 132, 66, 12, 103, 102, 139, 0, 30, 7, +102, 139, 62, 62, 2, 232, 63, 6, 102, 161, 62, 2, 102, 187, 32, 0, + 0, 0, 102, 185, 0, 0, 0, 0, 102, 186, 0, 0, 0, 0, 232, 228, + 0, 102, 133, 192, 15, 133, 35, 0, 102, 161, 62, 2, 102, 187, 128, 0, + 0, 0, 102, 185, 0, 0, 0, 0, 102, 186, 0, 0, 0, 0, 232, 196, + 0, 102, 11, 192, 15, 133, 68, 0, 233, 241, 11, 102, 51, 210, 102, 185, +128, 0, 0, 0, 102, 161, 62, 2, 232, 202, 8, 102, 11, 192, 15, 132, +218, 11, 30, 7, 102, 139, 62, 62, 2, 232, 219, 5, 102, 161, 62, 2, +102, 187, 128, 0, 0, 0, 102, 185, 0, 0, 0, 0, 102, 186, 0, 0, + 0, 0, 232, 128, 0, 102, 11, 192, 15, 132, 176, 11, 103, 102, 15, 183, + 88, 12, 102, 129, 227, 255, 0, 0, 0, 15, 133, 165, 11, 102, 139, 216, +104, 0, 32, 7, 102, 43, 255, 102, 161, 62, 2, 232, 0, 1, 104, 0, + 32, 7, 102, 43, 255, 102, 161, 62, 2, 232, 172, 10, 138, 22, 14, 0, +184, 232, 3, 142, 192, 141, 54, 11, 0, 43, 192, 104, 0, 32, 80, 203, + 6, 30, 102, 96, 102, 139, 218, 102, 15, 182, 14, 13, 0, 102, 247, 225, +102, 163, 17, 0, 102, 139, 195, 102, 247, 225, 163, 22, 0, 139, 223, 131, +227, 15, 140, 192, 102, 193, 239, 4, 3, 199, 80, 7, 232, 51, 252, 102, + 97, 144, 31, 7, 195, 103, 3, 64, 20, 103, 102, 131, 56, 255, 15, 132, + 76, 0, 103, 102, 57, 24, 15, 133, 51, 0, 102, 11, 201, 15, 133, 10, + 0, 103, 128, 120, 9, 0, 15, 133, 35, 0, 195, 103, 58, 72, 9, 15, +133, 26, 0, 102, 139, 240, 103, 3, 112, 10, 232, 151, 6, 102, 81, 30, + 7, 102, 139, 250, 243, 167, 102, 89, 15, 133, 1, 0, 195, 103, 102, 131, +120, 4, 0, 15, 132, 7, 0, 103, 102, 3, 64, 4, 235, 171, 102, 43, +192, 195, 102, 139, 243, 232, 108, 6, 103, 102, 3, 0, 103, 247, 64, 12, + 2, 0, 15, 133, 52, 0, 103, 102, 141, 80, 16, 103, 58, 74, 64, 15, +133, 24, 0, 103, 102, 141, 114, 66, 232, 73, 6, 102, 81, 30, 7, 102, +139, 251, 243, 167, 102, 89, 15, 133, 1, 0, 195, 103, 131, 120, 8, 0, + 15, 132, 6, 0, 103, 3, 64, 8, 235, 194, 102, 51, 192, 195, 103, 128, +123, 8, 0, 15, 133, 28, 0, 6, 30, 102, 96, 103, 102, 141, 83, 16, +103, 102, 139, 10, 102, 139, 243, 103, 3, 114, 4, 243, 164, 102, 97, 144, + 31, 7, 195, 102, 80, 103, 102, 141, 83, 16, 102, 133, 192, 15, 133, 10, + 0, 103, 102, 139, 74, 8, 102, 65, 235, 17, 144, 103, 102, 139, 66, 24, +102, 51, 210, 102, 247, 54, 82, 2, 102, 139, 200, 102, 43, 192, 102, 94, +232, 1, 0, 195, 6, 30, 102, 96, 103, 128, 123, 8, 1, 15, 132, 3, + 0, 233, 107, 251, 102, 131, 249, 0, 15, 133, 6, 0, 102, 97, 144, 31, + 7, 195, 102, 83, 102, 80, 102, 81, 102, 86, 102, 87, 6, 232, 145, 4, +102, 139, 209, 7, 102, 95, 102, 94, 102, 89, 102, 133, 192, 15, 132, 52, + 0, 102, 59, 202, 15, 141, 3, 0, 102, 139, 209, 232, 130, 254, 102, 43, +202, 102, 139, 218, 102, 139, 194, 102, 15, 182, 22, 13, 0, 102, 247, 226, +102, 15, 183, 22, 11, 0, 102, 247, 226, 102, 3, 248, 102, 88, 102, 3, +195, 102, 91, 235, 159, 102, 133, 246, 15, 132, 3, 251, 102, 81, 102, 87, + 6, 103, 102, 15, 182, 67, 9, 102, 133, 192, 15, 132, 32, 0, 102, 209, +224, 102, 43, 224, 102, 139, 252, 102, 84, 102, 86, 103, 102, 15, 183, 115, + 10, 102, 3, 243, 102, 139, 200, 243, 164, 102, 94, 235, 3, 144, 102, 80, +102, 80, 103, 102, 139, 3, 102, 80, 103, 102, 139, 67, 24, 102, 80, 103, +102, 139, 86, 32, 102, 133, 210, 15, 132, 11, 0, 102, 139, 254, 30, 7, +102, 139, 194, 232, 113, 3, 102, 139, 198, 102, 90, 102, 89, 102, 66, 102, + 81, 102, 86, 232, 63, 6, 102, 133, 192, 15, 132, 146, 250, 102, 94, 102, + 89, 102, 139, 254, 30, 7, 232, 78, 3, 102, 139, 198, 102, 139, 217, 102, + 89, 102, 90, 102, 81, 102, 86, 102, 209, 233, 232, 248, 253, 102, 133, 192, + 15, 132, 107, 250, 102, 94, 102, 89, 102, 3, 225, 7, 102, 95, 102, 89, +102, 139, 208, 102, 88, 102, 91, 102, 139, 218, 233, 245, 254, 6, 30, 102, + 96, 38, 103, 102, 15, 183, 95, 4, 38, 103, 102, 15, 183, 79, 6, 102, + 11, 201, 15, 132, 57, 250, 102, 3, 223, 102, 131, 195, 2, 102, 129, 199, +254, 1, 0, 0, 102, 73, 102, 11, 201, 15, 132, 23, 0, 38, 103, 139, + 3, 38, 103, 137, 7, 102, 131, 195, 2, 102, 129, 199, 0, 2, 0, 0, +102, 73, 235, 226, 102, 97, 144, 31, 7, 195, 6, 30, 102, 96, 102, 184, + 1, 0, 0, 0, 102, 163, 34, 2, 102, 161, 30, 2, 102, 3, 6, 102, + 2, 102, 163, 106, 2, 102, 3, 6, 102, 2, 102, 163, 78, 2, 102, 161, + 48, 0, 102, 15, 182, 30, 13, 0, 102, 247, 227, 102, 139, 30, 78, 2, +102, 137, 7, 102, 163, 17, 0, 131, 195, 4, 102, 161, 86, 2, 102, 137, + 7, 163, 22, 0, 131, 195, 4, 102, 137, 30, 78, 2, 102, 139, 30, 30, + 2, 30, 7, 232, 92, 249, 102, 139, 251, 232, 81, 255, 102, 161, 30, 2, +102, 187, 32, 0, 0, 0, 102, 185, 0, 0, 0, 0, 102, 186, 0, 0, + 0, 0, 232, 16, 253, 102, 11, 192, 15, 132, 25, 1, 102, 139, 216, 30, + 7, 102, 139, 62, 26, 2, 102, 51, 192, 232, 162, 253, 102, 139, 30, 26, + 2, 102, 129, 63, 128, 0, 0, 0, 15, 132, 235, 0, 3, 95, 4, 235, +240, 102, 83, 102, 139, 71, 16, 102, 247, 38, 86, 2, 102, 80, 102, 51, +210, 102, 15, 182, 30, 13, 0, 102, 247, 243, 102, 82, 232, 220, 0, 102, + 11, 192, 15, 132, 57, 249, 102, 139, 14, 86, 2, 102, 15, 182, 30, 13, + 0, 102, 247, 227, 102, 90, 102, 3, 194, 102, 139, 30, 78, 2, 102, 137, + 7, 131, 195, 4, 102, 15, 182, 6, 13, 0, 102, 43, 194, 102, 59, 193, + 15, 134, 3, 0, 102, 139, 193, 102, 137, 7, 102, 43, 200, 102, 90, 15, +132, 117, 0, 102, 3, 194, 102, 80, 102, 51, 210, 102, 15, 182, 30, 13, + 0, 102, 247, 243, 102, 81, 232, 130, 0, 102, 89, 102, 11, 192, 15, 132, +221, 248, 102, 15, 182, 30, 13, 0, 102, 247, 227, 102, 139, 30, 78, 2, +102, 139, 23, 131, 195, 4, 102, 3, 23, 102, 59, 208, 15, 133, 21, 0, +102, 15, 182, 6, 13, 0, 102, 59, 193, 15, 134, 3, 0, 102, 139, 193, +102, 1, 7, 235, 165, 131, 195, 4, 102, 137, 30, 78, 2, 102, 137, 7, +131, 195, 4, 102, 15, 182, 6, 13, 0, 102, 59, 193, 15, 134, 3, 0, +102, 139, 193, 102, 137, 7, 235, 130, 131, 195, 4, 102, 255, 6, 34, 2, +102, 137, 30, 78, 2, 102, 91, 3, 95, 4, 102, 129, 63, 128, 0, 0, + 0, 15, 132, 12, 255, 102, 97, 144, 31, 7, 195, 102, 139, 208, 102, 139, + 14, 34, 2, 102, 139, 54, 106, 2, 102, 3, 54, 102, 2, 102, 82, 102, + 81, 102, 82, 102, 139, 30, 106, 2, 102, 139, 62, 86, 2, 102, 139, 4, +102, 163, 17, 0, 131, 198, 4, 102, 139, 4, 163, 22, 0, 131, 198, 4, + 30, 7, 232, 221, 247, 102, 43, 248, 15, 132, 8, 0, 247, 38, 11, 0, + 3, 216, 235, 217, 102, 139, 62, 106, 2, 30, 7, 232, 191, 253, 102, 161, +106, 2, 102, 187, 128, 0, 0, 0, 102, 185, 0, 0, 0, 0, 102, 139, +209, 232, 129, 251, 102, 11, 192, 15, 132, 244, 247, 102, 139, 216, 102, 88, +102, 86, 232, 44, 1, 102, 94, 102, 11, 192, 15, 132, 5, 0, 102, 91, +102, 91, 195, 102, 89, 102, 90, 226, 132, 102, 51, 192, 195, 6, 30, 102, + 96, 102, 80, 102, 81, 102, 51, 210, 102, 15, 182, 30, 13, 0, 102, 247, +243, 102, 82, 102, 87, 232, 83, 255, 102, 95, 102, 11, 192, 15, 132, 174, +247, 102, 15, 182, 30, 13, 0, 102, 247, 227, 102, 90, 102, 3, 194, 102, +163, 17, 0, 102, 89, 102, 15, 182, 30, 13, 0, 102, 59, 203, 15, 142, + 19, 0, 137, 30, 22, 0, 102, 43, 203, 102, 88, 102, 3, 195, 102, 80, +102, 81, 235, 20, 144, 102, 88, 102, 3, 193, 102, 80, 137, 14, 22, 0, +102, 185, 0, 0, 0, 0, 102, 81, 6, 102, 87, 139, 223, 131, 227, 15, +140, 192, 102, 193, 239, 4, 3, 199, 80, 7, 232, 5, 247, 102, 95, 7, +102, 3, 62, 82, 2, 102, 89, 102, 88, 102, 131, 249, 0, 15, 143, 112, +255, 102, 97, 144, 31, 7, 195, 6, 30, 102, 96, 102, 247, 38, 86, 2, +102, 139, 14, 86, 2, 232, 85, 255, 232, 210, 252, 102, 97, 144, 31, 7, +195, 6, 30, 102, 96, 102, 247, 38, 114, 2, 102, 139, 30, 54, 2, 102, +139, 14, 114, 2, 102, 139, 54, 42, 2, 30, 7, 102, 139, 62, 70, 2, +232, 129, 251, 232, 167, 252, 102, 97, 144, 31, 7, 195, 102, 80, 102, 83, +102, 81, 102, 139, 30, 74, 2, 102, 139, 200, 102, 193, 232, 3, 102, 131, +225, 7, 102, 3, 216, 102, 184, 1, 0, 0, 0, 102, 211, 224, 103, 132, + 3, 15, 132, 4, 0, 248, 235, 2, 144, 249, 102, 89, 102, 91, 102, 88, +195, 103, 128, 123, 8, 1, 15, 132, 4, 0, 102, 43, 192, 195, 103, 102, +141, 115, 16, 103, 102, 139, 86, 8, 102, 59, 194, 15, 135, 11, 0, 103, +102, 139, 22, 102, 59, 194, 15, 131, 4, 0, 102, 43, 192, 195, 103, 3, + 94, 16, 102, 43, 246, 103, 128, 59, 0, 15, 132, 62, 0, 232, 129, 0, +102, 3, 241, 232, 57, 0, 102, 3, 202, 102, 59, 193, 15, 140, 33, 0, +102, 139, 209, 102, 80, 103, 102, 15, 182, 11, 102, 139, 193, 102, 131, 224, + 15, 102, 193, 233, 4, 102, 3, 217, 102, 3, 216, 102, 67, 102, 88, 235, +196, 102, 43, 200, 102, 43, 194, 102, 3, 198, 195, 102, 43, 192, 195, 102, + 43, 201, 103, 138, 11, 128, 225, 15, 102, 131, 249, 0, 15, 133, 4, 0, +102, 43, 201, 195, 102, 83, 102, 82, 102, 3, 217, 103, 102, 15, 190, 19, +102, 73, 102, 75, 102, 131, 249, 0, 15, 132, 13, 0, 102, 193, 226, 8, +103, 138, 19, 102, 75, 102, 73, 235, 235, 102, 139, 202, 102, 90, 102, 91, +195, 102, 83, 102, 82, 102, 43, 210, 103, 138, 19, 102, 131, 226, 15, 102, + 43, 201, 103, 138, 11, 192, 233, 4, 102, 131, 249, 0, 15, 133, 8, 0, +102, 43, 201, 102, 90, 102, 91, 195, 102, 3, 218, 102, 3, 217, 103, 102, + 15, 190, 19, 102, 73, 102, 75, 102, 131, 249, 0, 15, 132, 13, 0, 102, +193, 226, 8, 103, 138, 19, 102, 75, 102, 73, 235, 235, 102, 139, 202, 102, + 90, 102, 91, 195, 102, 11, 201, 15, 133, 1, 0, 195, 102, 81, 102, 86, +103, 131, 62, 97, 15, 140, 12, 0, 103, 131, 62, 122, 15, 143, 4, 0, +103, 131, 46, 32, 102, 131, 198, 2, 226, 230, 102, 94, 102, 89, 195, 102, + 80, 102, 81, 102, 139, 208, 102, 161, 50, 2, 103, 102, 141, 88, 16, 103, + 3, 67, 4, 103, 102, 141, 64, 16, 102, 139, 218, 232, 68, 249, 102, 11, +192, 15, 132, 5, 0, 102, 89, 102, 89, 195, 102, 161, 54, 2, 102, 11, +192, 15, 133, 8, 0, 102, 89, 102, 89, 102, 51, 192, 195, 102, 139, 22, + 54, 2, 103, 102, 141, 82, 16, 103, 102, 139, 66, 24, 102, 51, 210, 102, +247, 54, 110, 2, 102, 51, 246, 102, 80, 102, 86, 102, 88, 102, 94, 102, + 59, 198, 15, 132, 58, 0, 102, 86, 102, 64, 102, 80, 102, 72, 232, 27, +254, 114, 232, 232, 235, 253, 102, 90, 102, 94, 102, 89, 102, 91, 102, 83, +102, 81, 102, 86, 102, 82, 102, 161, 70, 2, 103, 102, 141, 64, 24, 232, +208, 248, 102, 11, 192, 116, 196, 102, 89, 102, 89, 102, 89, 102, 89, 195, +102, 89, 102, 89, 102, 51, 192, 195, 102, 81, 102, 80, 102, 184, 5, 0, + 0, 0, 30, 7, 102, 139, 249, 232, 141, 253, 102, 139, 193, 102, 187, 32, + 0, 0, 0, 102, 185, 0, 0, 0, 0, 102, 186, 0, 0, 0, 0, 232, + 51, 248, 102, 91, 102, 89, 102, 133, 192, 15, 133, 21, 0, 102, 139, 193, +102, 15, 183, 14, 16, 2, 102, 186, 18, 2, 0, 0, 232, 22, 248, 235, + 51, 144, 102, 51, 210, 102, 139, 193, 102, 139, 203, 102, 80, 102, 83, 232, + 35, 0, 102, 91, 102, 95, 102, 11, 192, 15, 132, 23, 0, 30, 7, 232, + 53, 253, 102, 139, 199, 102, 15, 183, 14, 16, 2, 102, 186, 18, 2, 0, + 0, 232, 225, 247, 195, 102, 82, 102, 81, 102, 187, 32, 0, 0, 0, 102, +185, 0, 0, 0, 0, 102, 186, 0, 0, 0, 0, 232, 199, 247, 102, 11, +192, 15, 132, 99, 0, 102, 139, 216, 30, 7, 102, 139, 62, 26, 2, 102, + 51, 192, 232, 89, 248, 30, 7, 102, 139, 30, 26, 2, 102, 89, 102, 90, + 38, 102, 57, 15, 15, 133, 12, 0, 38, 102, 57, 87, 8, 15, 132, 49, + 0, 235, 19, 144, 38, 102, 131, 63, 255, 15, 132, 47, 0, 38, 131, 127, + 4, 0, 15, 132, 38, 0, 38, 102, 15, 183, 71, 4, 3, 216, 139, 195, + 37, 0, 128, 116, 203, 140, 192, 5, 0, 8, 142, 192, 129, 227, 255, 127, +235, 190, 38, 102, 139, 71, 16, 195, 102, 89, 102, 90, 102, 51, 192, 195, +102, 80, 102, 81, 102, 139, 199, 102, 193, 232, 4, 6, 89, 3, 200, 81, + 7, 102, 131, 231, 15, 102, 89, 102, 88, 195, 96, 6, 190, 189, 13, 191, + 0, 32, 30, 7, 185, 13, 0, 144, 243, 165, 7, 97, 195, 1, 35, 69, +103, 137, 171, 205, 239, 254, 220, 186, 152, 118, 84, 50, 16, 240, 225, 210, +195, 0, 0, 0, 0, 32, 32, 96, 139, 54, 24, 32, 38, 138, 5, 136, + 4, 71, 70, 102, 255, 6, 20, 32, 129, 254, 96, 32, 117, 6, 232, 91, + 0, 190, 32, 32, 226, 230, 137, 54, 24, 32, 97, 195, 102, 96, 139, 54, + 24, 32, 176, 128, 136, 4, 70, 50, 192, 129, 254, 96, 32, 117, 6, 232, + 58, 0, 190, 32, 32, 129, 254, 88, 32, 117, 233, 102, 51, 192, 102, 163, + 88, 32, 102, 161, 20, 32, 102, 193, 224, 3, 102, 15, 200, 102, 163, 92, + 32, 232, 24, 0, 187, 0, 32, 102, 139, 7, 102, 15, 200, 102, 137, 7, +131, 195, 4, 129, 251, 52, 32, 117, 238, 102, 97, 195, 102, 96, 187, 32, + 32, 102, 139, 7, 102, 15, 200, 102, 137, 7, 131, 195, 4, 129, 251, 96, + 32, 117, 238, 187, 0, 32, 102, 139, 15, 102, 139, 87, 4, 102, 139, 119, + 8, 102, 139, 127, 12, 102, 139, 111, 16, 187, 32, 32, 199, 6, 26, 32, + 48, 15, 198, 6, 28, 32, 20, 144, 83, 139, 30, 26, 32, 255, 23, 102, + 3, 71, 2, 91, 102, 3, 232, 102, 3, 47, 102, 139, 193, 102, 193, 192, + 5, 102, 3, 197, 102, 139, 239, 102, 139, 254, 102, 139, 242, 102, 193, 198, + 30, 102, 139, 209, 102, 139, 200, 102, 139, 7, 102, 51, 71, 8, 102, 51, + 71, 32, 102, 51, 71, 52, 102, 209, 192, 102, 137, 71, 64, 131, 195, 4, +254, 14, 28, 32, 117, 178, 131, 6, 26, 32, 6, 129, 62, 26, 32, 72, + 15, 117, 159, 187, 0, 32, 102, 1, 15, 102, 1, 87, 4, 102, 1, 119, + 8, 102, 1, 127, 12, 102, 1, 111, 16, 102, 97, 195, 102, 139, 198, 102, + 51, 199, 102, 35, 194, 102, 51, 199, 195, 102, 139, 194, 102, 51, 198, 102, + 51, 199, 195, 102, 83, 102, 139, 194, 102, 35, 198, 102, 139, 218, 102, 35, +223, 102, 11, 195, 102, 139, 222, 102, 35, 223, 102, 11, 195, 102, 91, 195, +252, 14, 153, 121, 130, 90, 9, 15, 161, 235, 217, 110, 19, 15, 220, 188, + 27, 143, 9, 15, 214, 193, 98, 202, 6, 30, 102, 96, 102, 51, 219, 184, + 0, 187, 205, 26, 102, 35, 192, 15, 133, 187, 0, 102, 129, 251, 84, 67, + 80, 65, 15, 133, 176, 0, 129, 249, 2, 1, 15, 130, 168, 0, 102, 97, +144, 31, 7, 6, 30, 102, 96, 103, 128, 123, 8, 0, 15, 133, 12, 0, +103, 102, 141, 83, 16, 103, 102, 139, 10, 235, 37, 144, 103, 102, 141, 83, + 16, 103, 102, 139, 74, 40, 102, 129, 249, 0, 0, 8, 0, 15, 131, 12, + 0, 103, 102, 139, 66, 44, 102, 35, 192, 15, 132, 3, 0, 102, 51, 201, + 14, 31, 232, 245, 253, 102, 35, 201, 15, 132, 50, 0, 102, 186, 0, 128, + 0, 0, 102, 59, 202, 15, 134, 31, 0, 102, 43, 202, 6, 102, 81, 102, + 87, 102, 82, 102, 139, 202, 232, 183, 253, 232, 251, 253, 102, 90, 102, 95, +102, 89, 7, 102, 3, 250, 235, 218, 232, 165, 253, 232, 233, 253, 232, 11, +254, 14, 7, 102, 187, 84, 67, 80, 65, 102, 191, 0, 32, 0, 0, 102, +185, 20, 0, 0, 0, 102, 184, 7, 187, 0, 0, 102, 186, 10, 0, 0, + 0, 102, 51, 246, 205, 26, 102, 97, 144, 31, 7, 195, 160, 249, 1, 233, + 64, 241, 160, 250, 1, 233, 58, 241 +}; diff --git a/src/add-ons/kernel/file_systems/ntfs/utils/boot.h b/src/add-ons/kernel/file_systems/ntfs/utils/boot.h new file mode 100644 index 0000000000..45d79927f4 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ntfs/utils/boot.h @@ -0,0 +1,7 @@ +#ifndef _NTFS_BOOT_H_ +#define _NTFS_BOOT_H_ + +extern const unsigned char boot_array[4136]; + +#endif /* _NTFS_BOOT_H_ */ + diff --git a/src/add-ons/kernel/file_systems/ntfs/utils/mkntfs.c b/src/add-ons/kernel/file_systems/ntfs/utils/mkntfs.c new file mode 100644 index 0000000000..6c4e4f2cb6 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ntfs/utils/mkntfs.c @@ -0,0 +1,4781 @@ +/** + * mkntfs - Part of the Linux-NTFS project. + * + * Copyright (c) 2000-2011 Anton Altaparmakov + * Copyright (c) 2001-2005 Richard Russon + * Copyright (c) 2002-2006 Szabolcs Szakacsits + * Copyright (c) 2005 Erik Sornes + * Copyright (c) 2007 Yura Pakhuchiy + * Copyright (c) 2010 Jean-Pierre Andre + * + * This utility will create an NTFS 1.2 or 3.1 volume on a user + * specified (block) device. + * + * Some things (option handling and determination of mount status) have been + * adapted from e2fsprogs-1.19 and lib/ext2fs/ismounted.c and misc/mke2fs.c in + * particular. + * + * This program 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 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 Linux-NTFS source + * 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_UNISTD_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STDARG_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_TIME_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_LIMITS_H +#include +#endif +#ifdef HAVE_LIBGEN_H +#include +#endif +#ifdef ENABLE_UUID +#include +#endif + + +#ifdef HAVE_GETOPT_H +#include +#else + extern char *optarg; + extern int optind; +#endif + +#ifdef HAVE_LINUX_MAJOR_H +# include +# ifndef MAJOR +# define MAJOR(dev) ((dev) >> 8) +# define MINOR(dev) ((dev) & 0xff) +# endif +# ifndef IDE_DISK_MAJOR +# ifndef IDE0_MAJOR +# define IDE0_MAJOR 3 +# define IDE1_MAJOR 22 +# define IDE2_MAJOR 33 +# define IDE3_MAJOR 34 +# define IDE4_MAJOR 56 +# define IDE5_MAJOR 57 +# define IDE6_MAJOR 88 +# define IDE7_MAJOR 89 +# define IDE8_MAJOR 90 +# define IDE9_MAJOR 91 +# endif +# define IDE_DISK_MAJOR(M) \ + ((M) == IDE0_MAJOR || (M) == IDE1_MAJOR || \ + (M) == IDE2_MAJOR || (M) == IDE3_MAJOR || \ + (M) == IDE4_MAJOR || (M) == IDE5_MAJOR || \ + (M) == IDE6_MAJOR || (M) == IDE7_MAJOR || \ + (M) == IDE8_MAJOR || (M) == IDE9_MAJOR) +# endif +# ifndef SCSI_DISK_MAJOR +# ifndef SCSI_DISK0_MAJOR +# define SCSI_DISK0_MAJOR 8 +# define SCSI_DISK1_MAJOR 65 +# define SCSI_DISK7_MAJOR 71 +# endif +# define SCSI_DISK_MAJOR(M) \ + ((M) == SCSI_DISK0_MAJOR || \ + ((M) >= SCSI_DISK1_MAJOR && \ + (M) <= SCSI_DISK7_MAJOR)) +# endif +#endif + +#include "security.h" +#include "types.h" +#include "attrib.h" +#include "bitmap.h" +#include "bootsect.h" +#include "device.h" +#include "dir.h" +#include "mft.h" +#include "mst.h" +#include "runlist.h" +#include "utils.h" +#include "ntfstime.h" +#include "sd.h" +#include "boot.h" +#include "attrdef.h" +/* #include "version.h" */ +#include "logging.h" +#include "support.h" +#include "unistr.h" +#include "misc.h" + +int mkntfs_main(const char *devpath, const char *label); + +typedef enum { WRITE_STANDARD, WRITE_BITMAP, WRITE_LOGFILE } WRITE_TYPE; + +#ifdef NO_NTFS_DEVICE_DEFAULT_IO_OPS +#error "No default device io operations! Cannot build mkntfs. \ +You need to run ./configure without the --disable-default-device-io-ops \ +switch if you want to be able to build the NTFS utilities." +#endif + +/* Page size on ia32. Can change to 8192 on Alpha. */ +#define NTFS_PAGE_SIZE 4096 + +//static char EXEC_NAME[] = "mkntfs"; + +struct BITMAP_ALLOCATION { + struct BITMAP_ALLOCATION *next; + LCN lcn; /* first allocated cluster */ + s64 length; /* count of consecutive clusters */ +} ; + +/** + * global variables + */ +static u8 *g_buf = NULL; +static int g_mft_bitmap_byte_size = 0; +static u8 *g_mft_bitmap = NULL; +static int g_lcn_bitmap_byte_size = 0; +static int g_dynamic_buf_size = 0; +static u8 *g_dynamic_buf = NULL; +static runlist *g_rl_mft = NULL; +static runlist *g_rl_mft_bmp = NULL; +static runlist *g_rl_mftmirr = NULL; +static runlist *g_rl_logfile = NULL; +static runlist *g_rl_boot = NULL; +static runlist *g_rl_bad = NULL; +static INDEX_ALLOCATION *g_index_block = NULL; +static ntfs_volume *g_vol = NULL; +static int g_mft_size = 0; +static long long g_mft_lcn = 0; /* lcn of $MFT, $DATA attribute */ +static long long g_mftmirr_lcn = 0; /* lcn of $MFTMirr, $DATA */ +static long long g_logfile_lcn = 0; /* lcn of $LogFile, $DATA */ +static int g_logfile_size = 0; /* in bytes, determined from volume_size */ +static long long g_mft_zone_end = 0; /* Determined from volume_size and mft_zone_multiplier, in clusters */ +static long long g_num_bad_blocks = 0; /* Number of bad clusters */ +static long long *g_bad_blocks = NULL; /* Array of bad clusters */ + +static struct BITMAP_ALLOCATION *g_allocation = NULL; /* Head of cluster allocations */ + +/** + * struct mkntfs_options + */ +static struct mkntfs_options { + char *dev_name; /* Name of the device, or file, to use */ + BOOL enable_compression; /* -C, enables compression of all files on the volume by default. */ + BOOL quick_format; /* -f or -Q, fast format, don't zero the volume first. */ + BOOL force; /* -F, force fs creation. */ + long heads; /* -H, number of heads on device */ + BOOL disable_indexing; /* -I, disables indexing of file contents on the volume by default. */ + BOOL no_action; /* -n, do not write to device, only display what would be done. */ + long long part_start_sect; /* -p, start sector of partition on parent device */ + long sector_size; /* -s, in bytes, power of 2, default is 512 bytes. */ + long sectors_per_track; /* -S, number of sectors per track on device */ + BOOL use_epoch_time; /* -T, fake the time to be 00:00:00 UTC, Jan 1, 1970. */ + long mft_zone_multiplier; /* -z, value from 1 to 4. Default is 1. */ + long long num_sectors; /* size of device in sectors */ + long cluster_size; /* -c, format with this cluster-size */ + BOOL with_uuid; /* -U, request setting an uuid */ + char *label; /* -L, volume label */ +} opts; + +/* + * Mark a run of clusters as allocated + * + * Returns FALSE if unsuccessful + */ + +static BOOL bitmap_allocate(LCN lcn, s64 length) +{ + BOOL done; + struct BITMAP_ALLOCATION *p; + struct BITMAP_ALLOCATION *q; + struct BITMAP_ALLOCATION *newall; + + done = TRUE; + if (length) { + p = g_allocation; + q = (struct BITMAP_ALLOCATION*)NULL; + /* locate the first run which starts beyond the requested lcn */ + while (p && (p->lcn <= lcn)) { + q = p; + p = p->next; + } + /* make sure the requested lcns were not allocated */ + if ((q && ((q->lcn + q->length) > lcn)) + || (p && ((lcn + length) > p->lcn))) { + ntfs_log_error("Bitmap allocation error\n"); + done = FALSE; + } + if (q && ((q->lcn + q->length) == lcn)) { + /* extend current run, no overlapping possible */ + q->length += length; + } else { + newall = (struct BITMAP_ALLOCATION*) + ntfs_malloc(sizeof(struct BITMAP_ALLOCATION)); + if (newall) { + newall->lcn = lcn; + newall->length = length; + newall->next = p; + if (q) q->next = newall; + else g_allocation = newall; + } else { + done = FALSE; + ntfs_log_perror("Not enough memory"); + } + } + } + return (done); +} + +/* + * Mark a run of cluster as not allocated + * + * Returns FALSE if unsuccessful + * (freeing free clusters is not considered as an error) + */ + +static BOOL bitmap_deallocate(LCN lcn, s64 length) +{ + BOOL done; + struct BITMAP_ALLOCATION *p; + struct BITMAP_ALLOCATION *q; + LCN first, last; + s64 begin_length, end_length; + + done = TRUE; + if (length) { + p = g_allocation; + q = (struct BITMAP_ALLOCATION*)NULL; + /* locate a run which has a common portion */ + while (p) { + first = (p->lcn > lcn ? p->lcn : lcn); + last = ((p->lcn + p->length) < (lcn + length) + ? p->lcn + p->length : lcn + length); + if (first < last) { + /* get the parts which must be kept */ + begin_length = first - p->lcn; + end_length = p->lcn + p->length - last; + /* delete the entry */ + if (q) + q->next = p->next; + else + g_allocation = p->next; + free(p); + /* reallocate the beginning and the end */ + if (begin_length + && !bitmap_allocate(first - begin_length, + begin_length)) + done = FALSE; + if (end_length + && !bitmap_allocate(last, end_length)) + done = FALSE; + /* restart a full search */ + p = g_allocation; + q = (struct BITMAP_ALLOCATION*)NULL; + } else { + q = p; + p = p->next; + } + } + } + return (done); +} + +/* + * Get the allocation status of a single cluster + * and mark as allocated + * + * Returns 1 if the cluster was previously allocated + */ + +static int bitmap_get_and_set(LCN lcn, unsigned long length) +{ + struct BITMAP_ALLOCATION *p; + struct BITMAP_ALLOCATION *q; + int bit; + + if (length == 1) { + p = g_allocation; + q = (struct BITMAP_ALLOCATION*)NULL; + /* locate the first run which starts beyond the requested lcn */ + while (p && (p->lcn <= lcn)) { + q = p; + p = p->next; + } + if (q && (q->lcn <= lcn) && ((q->lcn + q->length) > lcn)) + bit = 1; /* was allocated */ + else { + bitmap_allocate(lcn, length); + bit = 0; + } + } else { + ntfs_log_error("Can only allocate a single cluster at a time\n"); + bit = 0; + } + return (bit); +} + +/* + * Build a section of the bitmap according to allocation + */ + +static void bitmap_build(u8 *buf, LCN lcn, s64 length) +{ + struct BITMAP_ALLOCATION *p; + LCN first, last; + int j; /* byte number */ + int bn; /* bit number */ + + for (j=0; (8*j)next) { + first = (p->lcn > lcn ? p->lcn : lcn); + last = ((p->lcn + p->length) < (lcn + length) + ? p->lcn + p->length : lcn + length); + if (first < last) { + bn = first - lcn; + /* initial partial byte, if any */ + while ((bn < (last - lcn)) && (bn & 7)) { + buf[bn >> 3] |= 1 << (bn & 7); + bn++; + } + /* full bytes */ + while (bn < (last - lcn - 7)) { + buf[bn >> 3] = 255; + bn += 8; + } + /* final partial byte, if any */ + while (bn < (last - lcn)) { + buf[bn >> 3] |= 1 << (bn & 7); + bn++; + } + } + } +} + +/** + * mkntfs_init_options + */ +static void mkntfs_init_options(struct mkntfs_options *opts2) +{ + if (!opts2) + return; + + memset(opts2, 0, sizeof(*opts2)); + + /* Mark all the numeric options as "unset". */ + opts2->cluster_size = -1; + opts2->heads = -1; + opts2->mft_zone_multiplier = -1; + opts2->num_sectors = -1; + opts2->part_start_sect = -1; + opts2->sector_size = -1; + opts2->sectors_per_track = -1; +} + +/** + * mkntfs_time + */ +static ntfs_time mkntfs_time(void) +{ + struct timespec ts; + + ts.tv_sec = 0; + ts.tv_nsec = 0; + if (!opts.use_epoch_time) + ts.tv_sec = time(NULL); + return timespec2ntfs(ts); +} + +/** + * append_to_bad_blocks + */ +static BOOL append_to_bad_blocks(unsigned long long block) +{ + long long *new_buf; + + if (!(g_num_bad_blocks & 15)) { + new_buf = realloc(g_bad_blocks, (g_num_bad_blocks + 16) * + sizeof(long long)); + if (!new_buf) { + ntfs_log_perror("Reallocating memory for bad blocks " + "list failed"); + return FALSE; + } + g_bad_blocks = new_buf; + } + g_bad_blocks[g_num_bad_blocks++] = block; + return TRUE; +} + +/** + * mkntfs_write + */ +static long long mkntfs_write(struct ntfs_device *dev, + const void *b, long long count) +{ + long long bytes_written, total; + int retry; + + if (opts.no_action) + return count; + total = 0LL; + retry = 0; + do { + bytes_written = dev->d_ops->write(dev, b, count); + if (bytes_written == -1LL) { + retry = errno; + ntfs_log_perror("Error writing to %s", dev->d_name); + errno = retry; + return bytes_written; + } else if (!bytes_written) { + retry++; + } else { + count -= bytes_written; + total += bytes_written; + } + } while (count && retry < 3); + if (count) + ntfs_log_error("Failed to complete writing to %s after three retries." + "\n", dev->d_name); + return total; +} + +/** + * Build and write a part of the global bitmap + * without overflowing from the allocated buffer + * + * mkntfs_bitmap_write + */ +static s64 mkntfs_bitmap_write(struct ntfs_device *dev, + s64 offset, s64 length) +{ + s64 partial_length; + s64 written; + + partial_length = length; + if (partial_length > g_dynamic_buf_size) + partial_length = g_dynamic_buf_size; + /* create a partial bitmap section, and write it */ + bitmap_build(g_dynamic_buf,offset << 3,partial_length << 3); + written = dev->d_ops->write(dev, g_dynamic_buf, partial_length); + return (written); +} + +/** + * Build and write a part of the log file + * without overflowing from the allocated buffer + * + * mkntfs_logfile_write + */ +static s64 mkntfs_logfile_write(struct ntfs_device *dev, + s64 offset __attribute__((unused)), s64 length) +{ + s64 partial_length; + s64 written; + + partial_length = length; + if (partial_length > g_dynamic_buf_size) + partial_length = g_dynamic_buf_size; + /* create a partial bad cluster section, and write it */ + memset(g_dynamic_buf, -1, partial_length); + written = dev->d_ops->write(dev, g_dynamic_buf, partial_length); + return (written); +} + +/** + * ntfs_rlwrite - Write to disk the clusters contained in the runlist @rl + * taking the data from @val. Take @val_len bytes from @val and pad the + * rest with zeroes. + * + * If the @rl specifies a completely sparse file, @val is allowed to be NULL. + * + * @inited_size if not NULL points to an output variable which will contain + * the actual number of bytes written to disk. I.e. this will not include + * sparse bytes for example. + * + * Return the number of bytes written (minus padding) or -1 on error. Errno + * will be set to the error code. + */ +static s64 ntfs_rlwrite(struct ntfs_device *dev, const runlist *rl, + const u8 *val, const s64 val_len, s64 *inited_size, + WRITE_TYPE write_type) +{ + s64 bytes_written, total, length, delta; + int retry, i; + + if (inited_size) + *inited_size = 0LL; + if (opts.no_action) + return val_len; + total = 0LL; + delta = 0LL; + for (i = 0; rl[i].length; i++) { + length = rl[i].length * g_vol->cluster_size; + /* Don't write sparse runs. */ + if (rl[i].lcn == -1) { + total += length; + if (!val) + continue; + /* TODO: Check that *val is really zero at pos and len. */ + continue; + } + /* + * Break up the write into the real data write and then a write + * of zeroes between the end of the real data and the end of + * the (last) run. + */ + if (total + length > val_len) { + delta = length; + length = val_len - total; + delta -= length; + } + if (dev->d_ops->seek(dev, rl[i].lcn * g_vol->cluster_size, + SEEK_SET) == (off_t)-1) + return -1LL; + retry = 0; + do { + /* use specific functions if buffer is not prefilled */ + switch (write_type) { + case WRITE_BITMAP : + bytes_written = mkntfs_bitmap_write(dev, + total, length); + break; + case WRITE_LOGFILE : + bytes_written = mkntfs_logfile_write(dev, + total, length); + break; + default : + bytes_written = dev->d_ops->write(dev, + val + total, length); + break; + } + if (bytes_written == -1LL) { + retry = errno; + ntfs_log_perror("Error writing to %s", + dev->d_name); + errno = retry; + return bytes_written; + } + if (bytes_written) { + length -= bytes_written; + total += bytes_written; + if (inited_size) + *inited_size += bytes_written; + } else { + retry++; + } + } while (length && retry < 3); + if (length) { + ntfs_log_error("Failed to complete writing to %s after three " + "retries.\n", dev->d_name); + return total; + } + } + if (delta) { + int eo; + char *b = ntfs_calloc(delta); + if (!b) + return -1; + bytes_written = mkntfs_write(dev, b, delta); + eo = errno; + free(b); + errno = eo; + if (bytes_written == -1LL) + return bytes_written; + } + return total; +} + +/** + * make_room_for_attribute - make room for an attribute inside an mft record + * @m: mft record + * @pos: position at which to make space + * @size: byte size to make available at this position + * + * @pos points to the attribute in front of which we want to make space. + * + * Return 0 on success or -errno on error. Possible error codes are: + * + * -ENOSPC There is not enough space available to complete + * operation. The caller has to make space before calling + * this. + * -EINVAL Can only occur if mkntfs was compiled with -DDEBUG. Means + * the input parameters were faulty. + */ +static int make_room_for_attribute(MFT_RECORD *m, char *pos, const u32 size) +{ + u32 biu; + + if (!size) + return 0; +#ifdef DEBUG + /* + * Rigorous consistency checks. Always return -EINVAL even if more + * appropriate codes exist for simplicity of parsing the return value. + */ + if (size != ((size + 7) & ~7)) { + ntfs_log_error("make_room_for_attribute() received non 8-byte aligned " + "size.\n"); + return -EINVAL; + } + if (!m || !pos) + return -EINVAL; + if (pos < (char*)m || pos + size < (char*)m || + pos > (char*)m + le32_to_cpu(m->bytes_allocated) || + pos + size > (char*)m + le32_to_cpu(m->bytes_allocated)) + return -EINVAL; + /* The -8 is for the attribute terminator. */ + if (pos - (char*)m > (int)le32_to_cpu(m->bytes_in_use) - 8) + return -EINVAL; +#endif + biu = le32_to_cpu(m->bytes_in_use); + /* Do we have enough space? */ + if (biu + size > le32_to_cpu(m->bytes_allocated)) + return -ENOSPC; + /* Move everything after pos to pos + size. */ + memmove(pos + size, pos, biu - (pos - (char*)m)); + /* Update mft record. */ + m->bytes_in_use = cpu_to_le32(biu + size); + return 0; +} + +/** + * deallocate_scattered_clusters + */ +static void deallocate_scattered_clusters(const runlist *rl) +{ + int i; + + if (!rl) + return; + /* Iterate over all runs in the runlist @rl. */ + for (i = 0; rl[i].length; i++) { + /* Skip sparse runs. */ + if (rl[i].lcn == -1LL) + continue; + /* Deallocate the current run. */ + bitmap_deallocate(rl[i].lcn, rl[i].length); + } +} + +/** + * allocate_scattered_clusters + * @clusters: Amount of clusters to allocate. + * + * Allocate @clusters and create a runlist of the allocated clusters. + * + * Return the allocated runlist. Caller has to free the runlist when finished + * with it. + * + * On error return NULL and errno is set to the error code. + * + * TODO: We should be returning the size as well, but for mkntfs this is not + * necessary. + */ +static runlist * allocate_scattered_clusters(s64 clusters) +{ + runlist *rl = NULL, *rlt; + VCN vcn = 0LL; + LCN lcn, end, prev_lcn = 0LL; + int rlpos = 0; + int rlsize = 0; + s64 prev_run_len = 0LL; + char bit; + + end = g_vol->nr_clusters; + /* Loop until all clusters are allocated. */ + while (clusters) { + /* Loop in current zone until we run out of free clusters. */ + for (lcn = g_mft_zone_end; lcn < end; lcn++) { + bit = bitmap_get_and_set(lcn,1); + if (bit) + continue; + /* + * Reallocate memory if necessary. Make sure we have + * enough for the terminator entry as well. + */ + if ((rlpos + 2) * (int)sizeof(runlist) >= rlsize) { + rlsize += 4096; /* PAGE_SIZE */ + rlt = realloc(rl, rlsize); + if (!rlt) + goto err_end; + rl = rlt; + } + /* Coalesce with previous run if adjacent LCNs. */ + if (prev_lcn == lcn - prev_run_len) { + rl[rlpos - 1].length = ++prev_run_len; + vcn++; + } else { + rl[rlpos].vcn = vcn++; + rl[rlpos].lcn = lcn; + prev_lcn = lcn; + rl[rlpos].length = 1LL; + prev_run_len = 1LL; + rlpos++; + } + /* Done? */ + if (!--clusters) { + /* Add terminator element and return. */ + rl[rlpos].vcn = vcn; + rl[rlpos].lcn = 0LL; + rl[rlpos].length = 0LL; + return rl; + } + + } + /* Switch to next zone, decreasing mft zone by factor 2. */ + end = g_mft_zone_end; + g_mft_zone_end >>= 1; + /* Have we run out of space on the volume? */ + if (g_mft_zone_end <= 0) + goto err_end; + } + return rl; +err_end: + if (rl) { + /* Add terminator element. */ + rl[rlpos].vcn = vcn; + rl[rlpos].lcn = -1LL; + rl[rlpos].length = 0LL; + /* Deallocate all allocated clusters. */ + deallocate_scattered_clusters(rl); + /* Free the runlist. */ + free(rl); + } + return NULL; +} + +/** + * ntfs_attr_find - find (next) attribute in mft record + * @type: attribute type to find + * @name: attribute name to find (optional, i.e. NULL means don't care) + * @name_len: attribute name length (only needed if @name present) + * @ic: IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present) + * @val: attribute value to find (optional, resident attributes only) + * @val_len: attribute value length + * @ctx: search context with mft record and attribute to search from + * + * You shouldn't need to call this function directly. Use lookup_attr() instead. + * + * ntfs_attr_find() takes a search context @ctx as parameter and searches the + * mft record specified by @ctx->mrec, beginning at @ctx->attr, for an + * attribute of @type, optionally @name and @val. If found, ntfs_attr_find() + * returns 0 and @ctx->attr will point to the found attribute. + * + * If not found, ntfs_attr_find() returns -1, with errno set to ENOENT and + * @ctx->attr will point to the attribute before which the attribute being + * searched for would need to be inserted if such an action were to be desired. + * + * On actual error, ntfs_attr_find() returns -1 with errno set to the error + * code but not to ENOENT. In this case @ctx->attr is undefined and in + * particular do not rely on it not changing. + * + * If @ctx->is_first is TRUE, the search begins with @ctx->attr itself. If it + * is FALSE, the search begins after @ctx->attr. + * + * If @type is AT_UNUSED, return the first found attribute, i.e. one can + * enumerate all attributes by setting @type to AT_UNUSED and then calling + * ntfs_attr_find() repeatedly until it returns -1 with errno set to ENOENT to + * indicate that there are no more entries. During the enumeration, each + * successful call of ntfs_attr_find() will return the next attribute in the + * mft record @ctx->mrec. + * + * If @type is AT_END, seek to the end and return -1 with errno set to ENOENT. + * AT_END is not a valid attribute, its length is zero for example, thus it is + * safer to return error instead of success in this case. This also allows us + * to interoperate cleanly with ntfs_external_attr_find(). + * + * If @name is AT_UNNAMED search for an unnamed attribute. If @name is present + * but not AT_UNNAMED search for a named attribute matching @name. Otherwise, + * match both named and unnamed attributes. + * + * If @ic is IGNORE_CASE, the @name comparison is not case sensitive and + * @ctx->ntfs_ino must be set to the ntfs inode to which the mft record + * @ctx->mrec belongs. This is so we can get at the ntfs volume and hence at + * the upcase table. If @ic is CASE_SENSITIVE, the comparison is case + * sensitive. When @name is present, @name_len is the @name length in Unicode + * characters. + * + * If @name is not present (NULL), we assume that the unnamed attribute is + * being searched for. + * + * Finally, the resident attribute value @val is looked for, if present. + * If @val is not present (NULL), @val_len is ignored. + * + * ntfs_attr_find() only searches the specified mft record and it ignores the + * presence of an attribute list attribute (unless it is the one being searched + * for, obviously). If you need to take attribute lists into consideration, use + * ntfs_attr_lookup() instead (see below). This also means that you cannot use + * ntfs_attr_find() to search for extent records of non-resident attributes, as + * extents with lowest_vcn != 0 are usually described by the attribute list + * attribute only. - Note that it is possible that the first extent is only in + * the attribute list while the last extent is in the base mft record, so don't + * rely on being able to find the first extent in the base mft record. + * + * Warning: Never use @val when looking for attribute types which can be + * non-resident as this most likely will result in a crash! + */ +static int mkntfs_attr_find(const ATTR_TYPES type, const ntfschar *name, + const u32 name_len, const IGNORE_CASE_BOOL ic, + const u8 *val, const u32 val_len, ntfs_attr_search_ctx *ctx) +{ + ATTR_RECORD *a; + ntfschar *upcase = g_vol->upcase; + u32 upcase_len = g_vol->upcase_len; + + /* + * Iterate over attributes in mft record starting at @ctx->attr, or the + * attribute following that, if @ctx->is_first is TRUE. + */ + if (ctx->is_first) { + a = ctx->attr; + ctx->is_first = FALSE; + } else { + a = (ATTR_RECORD*)((char*)ctx->attr + + le32_to_cpu(ctx->attr->length)); + } + for (;; a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length))) { + if (p2n(a) < p2n(ctx->mrec) || (char*)a > (char*)ctx->mrec + + le32_to_cpu(ctx->mrec->bytes_allocated)) + break; + ctx->attr = a; + if (((type != AT_UNUSED) && (le32_to_cpu(a->type) > + le32_to_cpu(type))) || + (a->type == AT_END)) { + errno = ENOENT; + return -1; + } + if (!a->length) + break; + /* If this is an enumeration return this attribute. */ + if (type == AT_UNUSED) + return 0; + if (a->type != type) + continue; + /* + * If @name is AT_UNNAMED we want an unnamed attribute. + * If @name is present, compare the two names. + * Otherwise, match any attribute. + */ + if (name == AT_UNNAMED) { + /* The search failed if the found attribute is named. */ + if (a->name_length) { + errno = ENOENT; + return -1; + } + } else if (name && !ntfs_names_are_equal(name, name_len, + (ntfschar*)((char*)a + le16_to_cpu(a->name_offset)), + a->name_length, ic, upcase, upcase_len)) { + int rc; + + rc = ntfs_names_full_collate(name, name_len, + (ntfschar*)((char*)a + + le16_to_cpu(a->name_offset)), + a->name_length, IGNORE_CASE, + upcase, upcase_len); + /* + * If @name collates before a->name, there is no + * matching attribute. + */ + if (rc == -1) { + errno = ENOENT; + return -1; + } + /* If the strings are not equal, continue search. */ + if (rc) + continue; + rc = ntfs_names_full_collate(name, name_len, + (ntfschar*)((char*)a + + le16_to_cpu(a->name_offset)), + a->name_length, CASE_SENSITIVE, + upcase, upcase_len); + if (rc == -1) { + errno = ENOENT; + return -1; + } + if (rc) + continue; + } + /* + * The names match or @name not present and attribute is + * unnamed. If no @val specified, we have found the attribute + * and are done. + */ + if (!val) { + return 0; + /* @val is present; compare values. */ + } else { + int rc; + + rc = memcmp(val, (char*)a +le16_to_cpu(a->value_offset), + min(val_len, + le32_to_cpu(a->value_length))); + /* + * If @val collates before the current attribute's + * value, there is no matching attribute. + */ + if (!rc) { + u32 avl; + avl = le32_to_cpu(a->value_length); + if (val_len == avl) + return 0; + if (val_len < avl) { + errno = ENOENT; + return -1; + } + } else if (rc < 0) { + errno = ENOENT; + return -1; + } + } + } + ntfs_log_trace("File is corrupt. Run chkdsk.\n"); + errno = EIO; + return -1; +} + +/** + * ntfs_attr_lookup - find an attribute in an ntfs inode + * @type: attribute type to find + * @name: attribute name to find (optional, i.e. NULL means don't care) + * @name_len: attribute name length (only needed if @name present) + * @ic: IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present) + * @lowest_vcn: lowest vcn to find (optional, non-resident attributes only) + * @val: attribute value to find (optional, resident attributes only) + * @val_len: attribute value length + * @ctx: search context with mft record and attribute to search from + * + * Find an attribute in an ntfs inode. On first search @ctx->ntfs_ino must + * be the base mft record and @ctx must have been obtained from a call to + * ntfs_attr_get_search_ctx(). + * + * This function transparently handles attribute lists and @ctx is used to + * continue searches where they were left off at. + * + * If @type is AT_UNUSED, return the first found attribute, i.e. one can + * enumerate all attributes by setting @type to AT_UNUSED and then calling + * ntfs_attr_lookup() repeatedly until it returns -1 with errno set to ENOENT + * to indicate that there are no more entries. During the enumeration, each + * successful call of ntfs_attr_lookup() will return the next attribute, with + * the current attribute being described by the search context @ctx. + * + * If @type is AT_END, seek to the end of the base mft record ignoring the + * attribute list completely and return -1 with errno set to ENOENT. AT_END is + * not a valid attribute, its length is zero for example, thus it is safer to + * return error instead of success in this case. It should never be needed to + * do this, but we implement the functionality because it allows for simpler + * code inside ntfs_external_attr_find(). + * + * If @name is AT_UNNAMED search for an unnamed attribute. If @name is present + * but not AT_UNNAMED search for a named attribute matching @name. Otherwise, + * match both named and unnamed attributes. + * + * After finishing with the attribute/mft record you need to call + * ntfs_attr_put_search_ctx() to cleanup the search context (unmapping any + * mapped extent inodes, etc). + * + * Return 0 if the search was successful and -1 if not, with errno set to the + * error code. + * + * On success, @ctx->attr is the found attribute, it is in mft record + * @ctx->mrec, and @ctx->al_entry is the attribute list entry for this + * attribute with @ctx->base_* being the base mft record to which @ctx->attr + * belongs. If no attribute list attribute is present @ctx->al_entry and + * @ctx->base_* are NULL. + * + * On error ENOENT, i.e. attribute not found, @ctx->attr is set to the + * attribute which collates just after the attribute being searched for in the + * base ntfs inode, i.e. if one wants to add the attribute to the mft record + * this is the correct place to insert it into, and if there is not enough + * space, the attribute should be placed in an extent mft record. + * @ctx->al_entry points to the position within @ctx->base_ntfs_ino->attr_list + * at which the new attribute's attribute list entry should be inserted. The + * other @ctx fields, base_ntfs_ino, base_mrec, and base_attr are set to NULL. + * The only exception to this is when @type is AT_END, in which case + * @ctx->al_entry is set to NULL also (see above). + * + * The following error codes are defined: + * ENOENT Attribute not found, not an error as such. + * EINVAL Invalid arguments. + * EIO I/O error or corrupt data structures found. + * ENOMEM Not enough memory to allocate necessary buffers. + */ +static int mkntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name, + const u32 name_len, const IGNORE_CASE_BOOL ic, + const VCN lowest_vcn __attribute__((unused)), const u8 *val, + const u32 val_len, ntfs_attr_search_ctx *ctx) +{ + ntfs_inode *base_ni; + + if (!ctx || !ctx->mrec || !ctx->attr) { + errno = EINVAL; + return -1; + } + if (ctx->base_ntfs_ino) + base_ni = ctx->base_ntfs_ino; + else + base_ni = ctx->ntfs_ino; + if (!base_ni || !NInoAttrList(base_ni) || type == AT_ATTRIBUTE_LIST) + return mkntfs_attr_find(type, name, name_len, ic, val, val_len, + ctx); + errno = EOPNOTSUPP; + return -1; +} + +/** + * insert_positioned_attr_in_mft_record + * + * Create a non-resident attribute with a predefined on disk location + * specified by the runlist @rl. The clusters specified by @rl are assumed to + * be allocated already. + * + * Return 0 on success and -errno on error. + */ +static int insert_positioned_attr_in_mft_record(MFT_RECORD *m, + const ATTR_TYPES type, const char *name, u32 name_len, + const IGNORE_CASE_BOOL ic, const ATTR_FLAGS flags, + const runlist *rl, const u8 *val, const s64 val_len) +{ + ntfs_attr_search_ctx *ctx; + ATTR_RECORD *a; + u16 hdr_size; + int asize, mpa_size, err, i; + s64 bw = 0, inited_size; + VCN highest_vcn; + ntfschar *uname = NULL; + int uname_len = 0; + /* + if (base record) + attr_lookup(); + else + */ + + uname = ntfs_str2ucs(name, &uname_len); + if (!uname) + return -errno; + + /* Check if the attribute is already there. */ + ctx = ntfs_attr_get_search_ctx(NULL, m); + if (!ctx) { + ntfs_log_error("Failed to allocate attribute search context.\n"); + err = -ENOMEM; + goto err_out; + } + if (ic == IGNORE_CASE) { + ntfs_log_error("FIXME: Hit unimplemented code path #1.\n"); + err = -EOPNOTSUPP; + goto err_out; + } + if (!mkntfs_attr_lookup(type, uname, uname_len, ic, 0, NULL, 0, ctx)) { + err = -EEXIST; + goto err_out; + } + if (errno != ENOENT) { + ntfs_log_error("Corrupt inode.\n"); + err = -errno; + goto err_out; + } + a = ctx->attr; + if (flags & ATTR_COMPRESSION_MASK) { + ntfs_log_error("Compressed attributes not supported yet.\n"); + /* FIXME: Compress attribute into a temporary buffer, set */ + /* val accordingly and save the compressed size. */ + err = -EOPNOTSUPP; + goto err_out; + } + if (flags & (ATTR_IS_ENCRYPTED | ATTR_IS_SPARSE)) { + ntfs_log_error("Encrypted/sparse attributes not supported.\n"); + err = -EOPNOTSUPP; + goto err_out; + } + if (flags & ATTR_COMPRESSION_MASK) { + hdr_size = 72; + /* FIXME: This compression stuff is all wrong. Never mind for */ + /* now. (AIA) */ + if (val_len) + mpa_size = 0; /* get_size_for_compressed_mapping_pairs(rl); */ + else + mpa_size = 0; + } else { + hdr_size = 64; + if (val_len) { + mpa_size = ntfs_get_size_for_mapping_pairs(g_vol, rl, 0, INT_MAX); + if (mpa_size < 0) { + err = -errno; + ntfs_log_error("Failed to get size for mapping " + "pairs.\n"); + goto err_out; + } + } else { + mpa_size = 0; + } + } + /* Mapping pairs array and next attribute must be 8-byte aligned. */ + asize = (((int)hdr_size + ((name_len + 7) & ~7) + mpa_size) + 7) & ~7; + /* Get the highest vcn. */ + for (i = 0, highest_vcn = 0LL; rl[i].length; i++) + highest_vcn += rl[i].length; + /* Does the value fit inside the allocated size? */ + if (highest_vcn * g_vol->cluster_size < val_len) { + ntfs_log_error("BUG: Allocated size is smaller than data size!\n"); + err = -EINVAL; + goto err_out; + } + err = make_room_for_attribute(m, (char*)a, asize); + if (err == -ENOSPC) { + /* + * FIXME: Make space! (AIA) + * can we make it non-resident? if yes, do that. + * does it fit now? yes -> do it. + * m's $DATA or $BITMAP+$INDEX_ALLOCATION resident? + * yes -> make non-resident + * does it fit now? yes -> do it. + * make all attributes non-resident + * does it fit now? yes -> do it. + * m is a base record? yes -> allocate extension record + * does the new attribute fit in there? yes -> do it. + * split up runlist into extents and place each in an extension + * record. + * FIXME: the check for needing extension records should be + * earlier on as it is very quick: asize > m->bytes_allocated? + */ + err = -EOPNOTSUPP; + goto err_out; +#ifdef DEBUG + } else if (err == -EINVAL) { + ntfs_log_error("BUG(): in insert_positioned_attribute_in_mft_" + "record(): make_room_for_attribute() returned " + "error: EINVAL!\n"); + goto err_out; +#endif + } + a->type = type; + a->length = cpu_to_le32(asize); + a->non_resident = 1; + a->name_length = name_len; + a->name_offset = cpu_to_le16(hdr_size); + a->flags = flags; + a->instance = m->next_attr_instance; + m->next_attr_instance = cpu_to_le16((le16_to_cpu(m->next_attr_instance) + + 1) & 0xffff); + a->lowest_vcn = cpu_to_le64(0); + a->highest_vcn = cpu_to_sle64(highest_vcn - 1LL); + a->mapping_pairs_offset = cpu_to_le16(hdr_size + ((name_len + 7) & ~7)); + memset(a->reserved1, 0, sizeof(a->reserved1)); + /* FIXME: Allocated size depends on compression. */ + a->allocated_size = cpu_to_sle64(highest_vcn * g_vol->cluster_size); + a->data_size = cpu_to_sle64(val_len); + if (name_len) + memcpy((char*)a + hdr_size, uname, name_len << 1); + if (flags & ATTR_COMPRESSION_MASK) { + if (flags & ATTR_COMPRESSION_MASK & ~ATTR_IS_COMPRESSED) { + ntfs_log_error("Unknown compression format. Reverting " + "to standard compression.\n"); + a->flags &= ~ATTR_COMPRESSION_MASK; + a->flags |= ATTR_IS_COMPRESSED; + } + a->compression_unit = 4; + inited_size = val_len; + /* FIXME: Set the compressed size. */ + a->compressed_size = cpu_to_le64(0); + /* FIXME: Write out the compressed data. */ + /* FIXME: err = build_mapping_pairs_compressed(); */ + err = -EOPNOTSUPP; + } else { + a->compression_unit = 0; + if ((type == AT_DATA) + && (m->mft_record_number + == const_cpu_to_le32(FILE_LogFile))) + bw = ntfs_rlwrite(g_vol->dev, rl, val, val_len, + &inited_size, WRITE_LOGFILE); + else + bw = ntfs_rlwrite(g_vol->dev, rl, val, val_len, + &inited_size, WRITE_STANDARD); + if (bw != val_len) { + ntfs_log_error("Error writing non-resident attribute " + "value.\n"); + return -errno; + } + err = ntfs_mapping_pairs_build(g_vol, (u8*)a + hdr_size + + ((name_len + 7) & ~7), mpa_size, rl, 0, NULL); + } + a->initialized_size = cpu_to_sle64(inited_size); + if (err < 0 || bw != val_len) { + /* FIXME: Handle error. */ + /* deallocate clusters */ + /* remove attribute */ + if (err >= 0) + err = -EIO; + ntfs_log_error("insert_positioned_attr_in_mft_record failed " + "with error %i.\n", err < 0 ? err : (int)bw); + } +err_out: + if (ctx) + ntfs_attr_put_search_ctx(ctx); + ntfs_ucsfree(uname); + return err; +} + +/** + * insert_non_resident_attr_in_mft_record + * + * Return 0 on success and -errno on error. + */ +static int insert_non_resident_attr_in_mft_record(MFT_RECORD *m, + const ATTR_TYPES type, const char *name, u32 name_len, + const IGNORE_CASE_BOOL ic, const ATTR_FLAGS flags, + const u8 *val, const s64 val_len, + WRITE_TYPE write_type) +{ + ntfs_attr_search_ctx *ctx; + ATTR_RECORD *a; + u16 hdr_size; + int asize, mpa_size, err, i; + runlist *rl = NULL; + s64 bw = 0; + ntfschar *uname = NULL; + int uname_len = 0; + /* + if (base record) + attr_lookup(); + else + */ + + uname = ntfs_str2ucs(name, &uname_len); + if (!uname) + return -errno; + + /* Check if the attribute is already there. */ + ctx = ntfs_attr_get_search_ctx(NULL, m); + if (!ctx) { + ntfs_log_error("Failed to allocate attribute search context.\n"); + err = -ENOMEM; + goto err_out; + } + if (ic == IGNORE_CASE) { + ntfs_log_error("FIXME: Hit unimplemented code path #2.\n"); + err = -EOPNOTSUPP; + goto err_out; + } + if (!mkntfs_attr_lookup(type, uname, uname_len, ic, 0, NULL, 0, ctx)) { + err = -EEXIST; + goto err_out; + } + if (errno != ENOENT) { + ntfs_log_error("Corrupt inode.\n"); + err = -errno; + goto err_out; + } + a = ctx->attr; + if (flags & ATTR_COMPRESSION_MASK) { + ntfs_log_error("Compressed attributes not supported yet.\n"); + /* FIXME: Compress attribute into a temporary buffer, set */ + /* val accordingly and save the compressed size. */ + err = -EOPNOTSUPP; + goto err_out; + } + if (flags & (ATTR_IS_ENCRYPTED | ATTR_IS_SPARSE)) { + ntfs_log_error("Encrypted/sparse attributes not supported.\n"); + err = -EOPNOTSUPP; + goto err_out; + } + if (val_len) { + rl = allocate_scattered_clusters((val_len + + g_vol->cluster_size - 1) / g_vol->cluster_size); + if (!rl) { + err = -errno; + ntfs_log_perror("Failed to allocate scattered clusters"); + goto err_out; + } + } else { + rl = NULL; + } + if (flags & ATTR_COMPRESSION_MASK) { + hdr_size = 72; + /* FIXME: This compression stuff is all wrong. Never mind for */ + /* now. (AIA) */ + if (val_len) + mpa_size = 0; /* get_size_for_compressed_mapping_pairs(rl); */ + else + mpa_size = 0; + } else { + hdr_size = 64; + if (val_len) { + mpa_size = ntfs_get_size_for_mapping_pairs(g_vol, rl, 0, INT_MAX); + if (mpa_size < 0) { + err = -errno; + ntfs_log_error("Failed to get size for mapping " + "pairs.\n"); + goto err_out; + } + } else { + mpa_size = 0; + } + } + /* Mapping pairs array and next attribute must be 8-byte aligned. */ + asize = (((int)hdr_size + ((name_len + 7) & ~7) + mpa_size) + 7) & ~7; + err = make_room_for_attribute(m, (char*)a, asize); + if (err == -ENOSPC) { + /* + * FIXME: Make space! (AIA) + * can we make it non-resident? if yes, do that. + * does it fit now? yes -> do it. + * m's $DATA or $BITMAP+$INDEX_ALLOCATION resident? + * yes -> make non-resident + * does it fit now? yes -> do it. + * make all attributes non-resident + * does it fit now? yes -> do it. + * m is a base record? yes -> allocate extension record + * does the new attribute fit in there? yes -> do it. + * split up runlist into extents and place each in an extension + * record. + * FIXME: the check for needing extension records should be + * earlier on as it is very quick: asize > m->bytes_allocated? + */ + err = -EOPNOTSUPP; + goto err_out; +#ifdef DEBUG + } else if (err == -EINVAL) { + ntfs_log_error("BUG(): in insert_non_resident_attribute_in_" + "mft_record(): make_room_for_attribute() " + "returned error: EINVAL!\n"); + goto err_out; +#endif + } + a->type = type; + a->length = cpu_to_le32(asize); + a->non_resident = 1; + a->name_length = name_len; + a->name_offset = cpu_to_le16(hdr_size); + a->flags = flags; + a->instance = m->next_attr_instance; + m->next_attr_instance = cpu_to_le16((le16_to_cpu(m->next_attr_instance) + + 1) & 0xffff); + a->lowest_vcn = cpu_to_le64(0); + for (i = 0; rl[i].length; i++) + ; + a->highest_vcn = cpu_to_sle64(rl[i].vcn - 1); + a->mapping_pairs_offset = cpu_to_le16(hdr_size + ((name_len + 7) & ~7)); + memset(a->reserved1, 0, sizeof(a->reserved1)); + /* FIXME: Allocated size depends on compression. */ + a->allocated_size = cpu_to_sle64((val_len + (g_vol->cluster_size - 1)) & + ~(g_vol->cluster_size - 1)); + a->data_size = cpu_to_sle64(val_len); + a->initialized_size = cpu_to_sle64(val_len); + if (name_len) + memcpy((char*)a + hdr_size, uname, name_len << 1); + if (flags & ATTR_COMPRESSION_MASK) { + if (flags & ATTR_COMPRESSION_MASK & ~ATTR_IS_COMPRESSED) { + ntfs_log_error("Unknown compression format. Reverting " + "to standard compression.\n"); + a->flags &= ~ATTR_COMPRESSION_MASK; + a->flags |= ATTR_IS_COMPRESSED; + } + a->compression_unit = 4; + /* FIXME: Set the compressed size. */ + a->compressed_size = cpu_to_le64(0); + /* FIXME: Write out the compressed data. */ + /* FIXME: err = build_mapping_pairs_compressed(); */ + err = -EOPNOTSUPP; + } else { + a->compression_unit = 0; + bw = ntfs_rlwrite(g_vol->dev, rl, val, val_len, NULL, + write_type); + if (bw != val_len) { + ntfs_log_error("Error writing non-resident attribute " + "value.\n"); + return -errno; + } + err = ntfs_mapping_pairs_build(g_vol, (u8*)a + hdr_size + + ((name_len + 7) & ~7), mpa_size, rl, 0, NULL); + } + if (err < 0 || bw != val_len) { + /* FIXME: Handle error. */ + /* deallocate clusters */ + /* remove attribute */ + if (err >= 0) + err = -EIO; + ntfs_log_error("insert_non_resident_attr_in_mft_record failed with " + "error %lld.\n", (long long) (err < 0 ? err : bw)); + } +err_out: + if (ctx) + ntfs_attr_put_search_ctx(ctx); + ntfs_ucsfree(uname); + free(rl); + return err; +} + +/** + * insert_resident_attr_in_mft_record + * + * Return 0 on success and -errno on error. + */ +static int insert_resident_attr_in_mft_record(MFT_RECORD *m, + const ATTR_TYPES type, const char *name, u32 name_len, + const IGNORE_CASE_BOOL ic, const ATTR_FLAGS flags, + const RESIDENT_ATTR_FLAGS res_flags, + const u8 *val, const u32 val_len) +{ + ntfs_attr_search_ctx *ctx; + ATTR_RECORD *a; + int asize, err; + ntfschar *uname = NULL; + int uname_len = 0; + /* + if (base record) + mkntfs_attr_lookup(); + else + */ + + uname = ntfs_str2ucs(name, &uname_len); + if (!uname) + return -errno; + + /* Check if the attribute is already there. */ + ctx = ntfs_attr_get_search_ctx(NULL, m); + if (!ctx) { + ntfs_log_error("Failed to allocate attribute search context.\n"); + err = -ENOMEM; + goto err_out; + } + if (ic == IGNORE_CASE) { + ntfs_log_error("FIXME: Hit unimplemented code path #3.\n"); + err = -EOPNOTSUPP; + goto err_out; + } + if (!mkntfs_attr_lookup(type, uname, uname_len, ic, 0, val, val_len, + ctx)) { + err = -EEXIST; + goto err_out; + } + if (errno != ENOENT) { + ntfs_log_error("Corrupt inode.\n"); + err = -errno; + goto err_out; + } + a = ctx->attr; + /* sizeof(resident attribute record header) == 24 */ + asize = ((24 + ((name_len + 7) & ~7) + val_len) + 7) & ~7; + err = make_room_for_attribute(m, (char*)a, asize); + if (err == -ENOSPC) { + /* + * FIXME: Make space! (AIA) + * can we make it non-resident? if yes, do that. + * does it fit now? yes -> do it. + * m's $DATA or $BITMAP+$INDEX_ALLOCATION resident? + * yes -> make non-resident + * does it fit now? yes -> do it. + * make all attributes non-resident + * does it fit now? yes -> do it. + * m is a base record? yes -> allocate extension record + * does the new attribute fit in there? yes -> do it. + * split up runlist into extents and place each in an extension + * record. + * FIXME: the check for needing extension records should be + * earlier on as it is very quick: asize > m->bytes_allocated? + */ + err = -EOPNOTSUPP; + goto err_out; + } +#ifdef DEBUG + if (err == -EINVAL) { + ntfs_log_error("BUG(): in insert_resident_attribute_in_mft_" + "record(): make_room_for_attribute() returned " + "error: EINVAL!\n"); + goto err_out; + } +#endif + a->type = type; + a->length = cpu_to_le32(asize); + a->non_resident = 0; + a->name_length = name_len; + if (type == AT_OBJECT_ID) + a->name_offset = const_cpu_to_le16(0); + else + a->name_offset = const_cpu_to_le16(24); + a->flags = flags; + a->instance = m->next_attr_instance; + m->next_attr_instance = cpu_to_le16((le16_to_cpu(m->next_attr_instance) + + 1) & 0xffff); + a->value_length = cpu_to_le32(val_len); + a->value_offset = cpu_to_le16(24 + ((name_len + 7) & ~7)); + a->resident_flags = res_flags; + a->reservedR = 0; + if (name_len) + memcpy((char*)a + 24, uname, name_len << 1); + if (val_len) + memcpy((char*)a + le16_to_cpu(a->value_offset), val, val_len); +err_out: + if (ctx) + ntfs_attr_put_search_ctx(ctx); + ntfs_ucsfree(uname); + return err; +} + + +/** + * add_attr_std_info + * + * Return 0 on success or -errno on error. + */ +static int add_attr_std_info(MFT_RECORD *m, const FILE_ATTR_FLAGS flags, + le32 security_id) +{ + STANDARD_INFORMATION si; + int err, sd_size; + + sd_size = 48; + + si.creation_time = mkntfs_time(); + si.last_data_change_time = si.creation_time; + si.last_mft_change_time = si.creation_time; + si.last_access_time = si.creation_time; + si.file_attributes = flags; /* already LE */ + si.maximum_versions = cpu_to_le32(0); + si.version_number = cpu_to_le32(0); + si.class_id = cpu_to_le32(0); + si.security_id = security_id; + if (si.security_id != const_cpu_to_le32(0)) + sd_size = 72; + /* FIXME: $Quota support... */ + si.owner_id = cpu_to_le32(0); + si.quota_charged = cpu_to_le64(0ULL); + /* FIXME: $UsnJrnl support... Not needed on fresh w2k3-volume */ + si.usn = cpu_to_le64(0ULL); + /* NTFS 1.2: size of si = 48, NTFS 3.[01]: size of si = 72 */ + err = insert_resident_attr_in_mft_record(m, AT_STANDARD_INFORMATION, + NULL, 0, CASE_SENSITIVE, const_cpu_to_le16(0), + 0, (u8*)&si, sd_size); + if (err < 0) + ntfs_log_perror("add_attr_std_info failed"); + return err; +} + +/* + * Tell whether the unnamed data is non resident + */ + +static BOOL non_resident_unnamed_data(MFT_RECORD *m) +{ + ATTR_RECORD *a; + ntfs_attr_search_ctx *ctx; + BOOL nonres; + + ctx = ntfs_attr_get_search_ctx(NULL, m); + if (ctx && !mkntfs_attr_find(AT_DATA, + (const ntfschar*)NULL, 0, CASE_SENSITIVE, + (u8*)NULL, 0, ctx)) { + a = ctx->attr; + nonres = a->non_resident != 0; + } else { + ntfs_log_error("BUG: Unnamed data not found\n"); + nonres = TRUE; + } + if (ctx) + ntfs_attr_put_search_ctx(ctx); + return (nonres); +} + +/* + * Get the time stored in the standard information attribute + */ + +static ntfs_time stdinfo_time(MFT_RECORD *m) +{ + STANDARD_INFORMATION *si; + ntfs_attr_search_ctx *ctx; + ntfs_time info_time; + + ctx = ntfs_attr_get_search_ctx(NULL, m); + if (ctx && !mkntfs_attr_find(AT_STANDARD_INFORMATION, + (const ntfschar*)NULL, 0, CASE_SENSITIVE, + (u8*)NULL, 0, ctx)) { + si = (STANDARD_INFORMATION*)((char*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + info_time = si->creation_time; + } else { + ntfs_log_error("BUG: Standard information not found\n"); + info_time = mkntfs_time(); + } + if (ctx) + ntfs_attr_put_search_ctx(ctx); + return (info_time); +} + +/** + * add_attr_file_name + * + * Return 0 on success or -errno on error. + */ +static int add_attr_file_name(MFT_RECORD *m, const leMFT_REF parent_dir, + const s64 allocated_size, const s64 data_size, + const FILE_ATTR_FLAGS flags, const u16 packed_ea_size, + const u32 reparse_point_tag, const char *file_name, + const FILE_NAME_TYPE_FLAGS file_name_type) +{ + ntfs_attr_search_ctx *ctx; + STANDARD_INFORMATION *si; + FILE_NAME_ATTR *fn; + int i, fn_size; + ntfschar *uname; + + /* Check if the attribute is already there. */ + ctx = ntfs_attr_get_search_ctx(NULL, m); + if (!ctx) { + ntfs_log_error("Failed to get attribute search context.\n"); + return -ENOMEM; + } + if (mkntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED, 0, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + int eo = errno; + ntfs_log_error("BUG: Standard information attribute not " + "present in file record.\n"); + ntfs_attr_put_search_ctx(ctx); + return -eo; + } + si = (STANDARD_INFORMATION*)((char*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + i = (strlen(file_name) + 1) * sizeof(ntfschar); + fn_size = sizeof(FILE_NAME_ATTR) + i; + fn = ntfs_malloc(fn_size); + if (!fn) { + ntfs_attr_put_search_ctx(ctx); + return -errno; + } + fn->parent_directory = parent_dir; + + fn->creation_time = si->creation_time; + fn->last_data_change_time = si->last_data_change_time; + fn->last_mft_change_time = si->last_mft_change_time; + fn->last_access_time = si->last_access_time; + ntfs_attr_put_search_ctx(ctx); + + fn->allocated_size = cpu_to_sle64(allocated_size); + fn->data_size = cpu_to_sle64(data_size); + fn->file_attributes = flags; + /* These are in a union so can't have both. */ + if (packed_ea_size && reparse_point_tag) { + free(fn); + return -EINVAL; + } + if (packed_ea_size) { + fn->packed_ea_size = cpu_to_le16(packed_ea_size); + fn->reserved = cpu_to_le16(0); + } else { + fn->reparse_point_tag = cpu_to_le32(reparse_point_tag); + } + fn->file_name_type = file_name_type; + uname = fn->file_name; + i = ntfs_mbstoucs_libntfscompat(file_name, &uname, i); + if (i < 1) { + free(fn); + return -EINVAL; + } + if (i > 0xff) { + free(fn); + return -ENAMETOOLONG; + } + /* No terminating null in file names. */ + fn->file_name_length = i; + fn_size = sizeof(FILE_NAME_ATTR) + i * sizeof(ntfschar); + i = insert_resident_attr_in_mft_record(m, AT_FILE_NAME, NULL, 0, + CASE_SENSITIVE, const_cpu_to_le16(0), + RESIDENT_ATTR_IS_INDEXED, (u8*)fn, fn_size); + free(fn); + if (i < 0) + ntfs_log_error("add_attr_file_name failed: %s\n", strerror(-i)); + return i; +} + +/** + * add_attr_object_id - + * + * Note we insert only a basic object id which only has the GUID and none of + * the extended fields. This is because we currently only use this function + * when creating the object id for the volume. + * + * Return 0 on success or -errno on error. + */ +static int add_attr_object_id(MFT_RECORD *m, const GUID *object_id) +{ + OBJECT_ID_ATTR oi; + int err; + + oi = (OBJECT_ID_ATTR) { + .object_id = *object_id, + }; + err = insert_resident_attr_in_mft_record(m, AT_OBJECT_ID, NULL, + 0, CASE_SENSITIVE, const_cpu_to_le16(0), + 0, (u8*)&oi, sizeof(oi.object_id)); + if (err < 0) + ntfs_log_error("add_attr_vol_info failed: %s\n", strerror(-err)); + return err; +} + +/** + * add_attr_sd + * + * Create the security descriptor attribute adding the security descriptor @sd + * of length @sd_len to the mft record @m. + * + * Return 0 on success or -errno on error. + */ +static int add_attr_sd(MFT_RECORD *m, const u8 *sd, const s64 sd_len) +{ + int err; + + /* Does it fit? NO: create non-resident. YES: create resident. */ + if (le32_to_cpu(m->bytes_in_use) + 24 + sd_len > + le32_to_cpu(m->bytes_allocated)) + err = insert_non_resident_attr_in_mft_record(m, + AT_SECURITY_DESCRIPTOR, NULL, 0, + CASE_SENSITIVE, const_cpu_to_le16(0), sd, + sd_len, WRITE_STANDARD); + else + err = insert_resident_attr_in_mft_record(m, + AT_SECURITY_DESCRIPTOR, NULL, 0, + CASE_SENSITIVE, const_cpu_to_le16(0), 0, sd, + sd_len); + if (err < 0) + ntfs_log_error("add_attr_sd failed: %s\n", strerror(-err)); + return err; +} + +/** + * add_attr_data + * + * Return 0 on success or -errno on error. + */ +static int add_attr_data(MFT_RECORD *m, const char *name, const u32 name_len, + const IGNORE_CASE_BOOL ic, const ATTR_FLAGS flags, + const u8 *val, const s64 val_len) +{ + int err; + + /* + * Does it fit? NO: create non-resident. YES: create resident. + * + * FIXME: Introduced arbitrary limit of mft record allocated size - 512. + * This is to get around the problem that if $Bitmap/$DATA becomes too + * big, but is just small enough to be resident, we would make it + * resident, and later run out of space when creating the other + * attributes and this would cause us to abort as making resident + * attributes non-resident is not supported yet. + * The proper fix is to support making resident attribute non-resident. + */ + if (le32_to_cpu(m->bytes_in_use) + 24 + val_len > + min(le32_to_cpu(m->bytes_allocated), + le32_to_cpu(m->bytes_allocated) - 512)) + err = insert_non_resident_attr_in_mft_record(m, AT_DATA, name, + name_len, ic, flags, val, val_len, + WRITE_STANDARD); + else + err = insert_resident_attr_in_mft_record(m, AT_DATA, name, + name_len, ic, flags, 0, val, val_len); + + if (err < 0) + ntfs_log_error("add_attr_data failed: %s\n", strerror(-err)); + return err; +} + +/** + * add_attr_data_positioned + * + * Create a non-resident data attribute with a predefined on disk location + * specified by the runlist @rl. The clusters specified by @rl are assumed to + * be allocated already. + * + * Return 0 on success or -errno on error. + */ +static int add_attr_data_positioned(MFT_RECORD *m, const char *name, + const u32 name_len, const IGNORE_CASE_BOOL ic, + const ATTR_FLAGS flags, const runlist *rl, + const u8 *val, const s64 val_len) +{ + int err; + + err = insert_positioned_attr_in_mft_record(m, AT_DATA, name, name_len, + ic, flags, rl, val, val_len); + if (err < 0) + ntfs_log_error("add_attr_data_positioned failed: %s\n", + strerror(-err)); + return err; +} + +/** + * add_attr_vol_name + * + * Create volume name attribute specifying the volume name @vol_name as a null + * terminated char string of length @vol_name_len (number of characters not + * including the terminating null), which is converted internally to a little + * endian ntfschar string. The name is at least 1 character long (though + * Windows accepts zero characters), and at most 128 characters long (not + * counting the terminating null). + * + * Return 0 on success or -errno on error. + */ +static int add_attr_vol_name(MFT_RECORD *m, const char *vol_name, + const int vol_name_len __attribute__((unused))) +{ + ntfschar *uname = NULL; + int uname_len = 0; + int i; + + if (vol_name) { + uname_len = ntfs_mbstoucs(vol_name, &uname); + if (uname_len < 0) + return -errno; + if (uname_len > 128) { + free(uname); + return -ENAMETOOLONG; + } + } + i = insert_resident_attr_in_mft_record(m, AT_VOLUME_NAME, NULL, 0, + CASE_SENSITIVE, const_cpu_to_le16(0), + 0, (u8*)uname, uname_len*sizeof(ntfschar)); + free(uname); + if (i < 0) + ntfs_log_error("add_attr_vol_name failed: %s\n", strerror(-i)); + return i; +} + +/** + * add_attr_vol_info + * + * Return 0 on success or -errno on error. + */ +static int add_attr_vol_info(MFT_RECORD *m, const VOLUME_FLAGS flags, + const u8 major_ver, const u8 minor_ver) +{ + VOLUME_INFORMATION vi; + int err; + + memset(&vi, 0, sizeof(vi)); + vi.major_ver = major_ver; + vi.minor_ver = minor_ver; + vi.flags = flags & VOLUME_FLAGS_MASK; + err = insert_resident_attr_in_mft_record(m, AT_VOLUME_INFORMATION, NULL, + 0, CASE_SENSITIVE, const_cpu_to_le16(0), + 0, (u8*)&vi, sizeof(vi)); + if (err < 0) + ntfs_log_error("add_attr_vol_info failed: %s\n", strerror(-err)); + return err; +} + +/** + * add_attr_index_root + * + * Return 0 on success or -errno on error. + */ +static int add_attr_index_root(MFT_RECORD *m, const char *name, + const u32 name_len, const IGNORE_CASE_BOOL ic, + const ATTR_TYPES indexed_attr_type, + const COLLATION_RULES collation_rule, + const u32 index_block_size) +{ + INDEX_ROOT *r; + INDEX_ENTRY_HEADER *e; + int err, val_len; + + val_len = sizeof(INDEX_ROOT) + sizeof(INDEX_ENTRY_HEADER); + r = ntfs_malloc(val_len); + if (!r) + return -errno; + r->type = (indexed_attr_type == AT_FILE_NAME) + ? AT_FILE_NAME : const_cpu_to_le32(0); + if (indexed_attr_type == AT_FILE_NAME && + collation_rule != COLLATION_FILE_NAME) { + free(r); + ntfs_log_error("add_attr_index_root: indexed attribute is $FILE_NAME " + "but collation rule is not COLLATION_FILE_NAME.\n"); + return -EINVAL; + } + r->collation_rule = collation_rule; + r->index_block_size = cpu_to_le32(index_block_size); + if (index_block_size >= g_vol->cluster_size) { + if (index_block_size % g_vol->cluster_size) { + ntfs_log_error("add_attr_index_root: index block size is not " + "a multiple of the cluster size.\n"); + free(r); + return -EINVAL; + } + r->clusters_per_index_block = index_block_size / + g_vol->cluster_size; + } else { /* if (g_vol->cluster_size > index_block_size) */ + if (index_block_size & (index_block_size - 1)) { + ntfs_log_error("add_attr_index_root: index block size is not " + "a power of 2.\n"); + free(r); + return -EINVAL; + } + if (index_block_size < (u32)opts.sector_size) { + ntfs_log_error("add_attr_index_root: index block size " + "is smaller than the sector size.\n"); + free(r); + return -EINVAL; + } + r->clusters_per_index_block = index_block_size + >> NTFS_BLOCK_SIZE_BITS; + } + memset(&r->reserved, 0, sizeof(r->reserved)); + r->index.entries_offset = const_cpu_to_le32(sizeof(INDEX_HEADER)); + r->index.index_length = const_cpu_to_le32(sizeof(INDEX_HEADER) + + sizeof(INDEX_ENTRY_HEADER)); + r->index.allocated_size = r->index.index_length; + r->index.ih_flags = SMALL_INDEX; + memset(&r->index.reserved, 0, sizeof(r->index.reserved)); + e = (INDEX_ENTRY_HEADER*)((u8*)&r->index + + le32_to_cpu(r->index.entries_offset)); + /* + * No matter whether this is a file index or a view as this is a + * termination entry, hence no key value / data is associated with it + * at all. Thus, we just need the union to be all zero. + */ + e->indexed_file = const_cpu_to_le64(0LL); + e->length = const_cpu_to_le16(sizeof(INDEX_ENTRY_HEADER)); + e->key_length = const_cpu_to_le16(0); + e->flags = INDEX_ENTRY_END; + e->reserved = const_cpu_to_le16(0); + err = insert_resident_attr_in_mft_record(m, AT_INDEX_ROOT, name, + name_len, ic, const_cpu_to_le16(0), 0, + (u8*)r, val_len); + free(r); + if (err < 0) + ntfs_log_error("add_attr_index_root failed: %s\n", strerror(-err)); + return err; +} + +/** + * add_attr_index_alloc + * + * Return 0 on success or -errno on error. + */ +static int add_attr_index_alloc(MFT_RECORD *m, const char *name, + const u32 name_len, const IGNORE_CASE_BOOL ic, + const u8 *index_alloc_val, const u32 index_alloc_val_len) +{ + int err; + + err = insert_non_resident_attr_in_mft_record(m, AT_INDEX_ALLOCATION, + name, name_len, ic, const_cpu_to_le16(0), + index_alloc_val, index_alloc_val_len, WRITE_STANDARD); + if (err < 0) + ntfs_log_error("add_attr_index_alloc failed: %s\n", strerror(-err)); + return err; +} + +/** + * add_attr_bitmap + * + * Return 0 on success or -errno on error. + */ +static int add_attr_bitmap(MFT_RECORD *m, const char *name, const u32 name_len, + const IGNORE_CASE_BOOL ic, const u8 *bitmap, + const u32 bitmap_len) +{ + int err; + + /* Does it fit? NO: create non-resident. YES: create resident. */ + if (le32_to_cpu(m->bytes_in_use) + 24 + bitmap_len > + le32_to_cpu(m->bytes_allocated)) + err = insert_non_resident_attr_in_mft_record(m, AT_BITMAP, name, + name_len, ic, const_cpu_to_le16(0), bitmap, + bitmap_len, WRITE_STANDARD); + else + err = insert_resident_attr_in_mft_record(m, AT_BITMAP, name, + name_len, ic, const_cpu_to_le16(0), 0, + bitmap, bitmap_len); + + if (err < 0) + ntfs_log_error("add_attr_bitmap failed: %s\n", strerror(-err)); + return err; +} + +/** + * add_attr_bitmap_positioned + * + * Create a non-resident bitmap attribute with a predefined on disk location + * specified by the runlist @rl. The clusters specified by @rl are assumed to + * be allocated already. + * + * Return 0 on success or -errno on error. + */ +static int add_attr_bitmap_positioned(MFT_RECORD *m, const char *name, + const u32 name_len, const IGNORE_CASE_BOOL ic, + const runlist *rl, const u8 *bitmap, const u32 bitmap_len) +{ + int err; + + err = insert_positioned_attr_in_mft_record(m, AT_BITMAP, name, name_len, + ic, const_cpu_to_le16(0), rl, bitmap, bitmap_len); + if (err < 0) + ntfs_log_error("add_attr_bitmap_positioned failed: %s\n", + strerror(-err)); + return err; +} + + +/** + * upgrade_to_large_index + * + * Create bitmap and index allocation attributes, modify index root + * attribute accordingly and move all of the index entries from the index root + * into the index allocation. + * + * Return 0 on success or -errno on error. + */ +static int upgrade_to_large_index(MFT_RECORD *m, const char *name, + u32 name_len, const IGNORE_CASE_BOOL ic, + INDEX_ALLOCATION **idx) +{ + ntfs_attr_search_ctx *ctx; + ATTR_RECORD *a; + INDEX_ROOT *r; + INDEX_ENTRY *re; + INDEX_ALLOCATION *ia_val = NULL; + ntfschar *uname = NULL; + int uname_len = 0; + u8 bmp[8]; + char *re_start, *re_end; + int i, err, index_block_size; + + uname = ntfs_str2ucs(name, &uname_len); + if (!uname) + return -errno; + + /* Find the index root attribute. */ + ctx = ntfs_attr_get_search_ctx(NULL, m); + if (!ctx) { + ntfs_log_error("Failed to allocate attribute search context.\n"); + ntfs_ucsfree(uname); + return -ENOMEM; + } + if (ic == IGNORE_CASE) { + ntfs_log_error("FIXME: Hit unimplemented code path #4.\n"); + err = -EOPNOTSUPP; + ntfs_ucsfree(uname); + goto err_out; + } + err = mkntfs_attr_lookup(AT_INDEX_ROOT, uname, uname_len, ic, 0, NULL, 0, + ctx); + ntfs_ucsfree(uname); + if (err) { + err = -ENOTDIR; + goto err_out; + } + a = ctx->attr; + if (a->non_resident || a->flags) { + err = -EINVAL; + goto err_out; + } + r = (INDEX_ROOT*)((char*)a + le16_to_cpu(a->value_offset)); + re_end = (char*)r + le32_to_cpu(a->value_length); + re_start = (char*)&r->index + le32_to_cpu(r->index.entries_offset); + re = (INDEX_ENTRY*)re_start; + index_block_size = le32_to_cpu(r->index_block_size); + memset(bmp, 0, sizeof(bmp)); + ntfs_bit_set(bmp, 0ULL, 1); + /* Bitmap has to be at least 8 bytes in size. */ + err = add_attr_bitmap(m, name, name_len, ic, bmp, sizeof(bmp)); + if (err) + goto err_out; + ia_val = ntfs_calloc(index_block_size); + if (!ia_val) { + err = -errno; + goto err_out; + } + /* Setup header. */ + ia_val->magic = magic_INDX; + ia_val->usa_ofs = cpu_to_le16(sizeof(INDEX_ALLOCATION)); + if (index_block_size >= NTFS_BLOCK_SIZE) { + ia_val->usa_count = cpu_to_le16(index_block_size / + NTFS_BLOCK_SIZE + 1); + } else { + ia_val->usa_count = cpu_to_le16(1); + ntfs_log_error("Sector size is bigger than index block size. " + "Setting usa_count to 1. If Windows chkdsk " + "reports this as corruption, please email %s " + "stating that you saw this message and that " + "the filesystem created was corrupt. " + "Thank you.", NTFS_DEV_LIST); + } + /* Set USN to 1. */ + *(le16*)((char*)ia_val + le16_to_cpu(ia_val->usa_ofs)) = + cpu_to_le16(1); + ia_val->lsn = cpu_to_le64(0); + ia_val->index_block_vcn = cpu_to_le64(0); + ia_val->index.ih_flags = LEAF_NODE; + /* Align to 8-byte boundary. */ + ia_val->index.entries_offset = cpu_to_le32((sizeof(INDEX_HEADER) + + le16_to_cpu(ia_val->usa_count) * 2 + 7) & ~7); + ia_val->index.allocated_size = cpu_to_le32(index_block_size - + (sizeof(INDEX_ALLOCATION) - sizeof(INDEX_HEADER))); + /* Find the last entry in the index root and save it in re. */ + while ((char*)re < re_end && !(re->ie_flags & INDEX_ENTRY_END)) { + /* Next entry in index root. */ + re = (INDEX_ENTRY*)((char*)re + le16_to_cpu(re->length)); + } + /* Copy all the entries including the termination entry. */ + i = (char*)re - re_start + le16_to_cpu(re->length); + memcpy((char*)&ia_val->index + + le32_to_cpu(ia_val->index.entries_offset), re_start, i); + /* Finish setting up index allocation. */ + ia_val->index.index_length = cpu_to_le32(i + + le32_to_cpu(ia_val->index.entries_offset)); + /* Move the termination entry forward to the beginning if necessary. */ + if ((char*)re > re_start) { + memmove(re_start, (char*)re, le16_to_cpu(re->length)); + re = (INDEX_ENTRY*)re_start; + } + /* Now fixup empty index root with pointer to index allocation VCN 0. */ + r->index.ih_flags = LARGE_INDEX; + re->ie_flags |= INDEX_ENTRY_NODE; + if (le16_to_cpu(re->length) < sizeof(INDEX_ENTRY_HEADER) + sizeof(VCN)) + re->length = cpu_to_le16(le16_to_cpu(re->length) + sizeof(VCN)); + r->index.index_length = cpu_to_le32(le32_to_cpu(r->index.entries_offset) + + le16_to_cpu(re->length)); + r->index.allocated_size = r->index.index_length; + /* Resize index root attribute. */ + if (ntfs_resident_attr_value_resize(m, a, sizeof(INDEX_ROOT) - + sizeof(INDEX_HEADER) + + le32_to_cpu(r->index.allocated_size))) { + /* TODO: Remove the added bitmap! */ + /* Revert index root from index allocation. */ + err = -errno; + goto err_out; + } + /* Set VCN pointer to 0LL. */ + *(leVCN*)((char*)re + cpu_to_le16(re->length) - sizeof(VCN)) = + cpu_to_le64(0); + err = ntfs_mst_pre_write_fixup((NTFS_RECORD*)ia_val, index_block_size); + if (err) { + err = -errno; + ntfs_log_error("ntfs_mst_pre_write_fixup() failed in " + "upgrade_to_large_index.\n"); + goto err_out; + } + err = add_attr_index_alloc(m, name, name_len, ic, (u8*)ia_val, + index_block_size); + ntfs_mst_post_write_fixup((NTFS_RECORD*)ia_val); + if (err) { + /* TODO: Remove the added bitmap! */ + /* Revert index root from index allocation. */ + goto err_out; + } + *idx = ia_val; + ntfs_attr_put_search_ctx(ctx); + return 0; +err_out: + ntfs_attr_put_search_ctx(ctx); + free(ia_val); + return err; +} + +/** + * make_room_for_index_entry_in_index_block + * + * Create space of @size bytes at position @pos inside the index block @idx. + * + * Return 0 on success or -errno on error. + */ +static int make_room_for_index_entry_in_index_block(INDEX_BLOCK *idx, + INDEX_ENTRY *pos, u32 size) +{ + u32 biu; + + if (!size) + return 0; +#ifdef DEBUG + /* + * Rigorous consistency checks. Always return -EINVAL even if more + * appropriate codes exist for simplicity of parsing the return value. + */ + if (size != ((size + 7) & ~7)) { + ntfs_log_error("make_room_for_index_entry_in_index_block() received " + "non 8-byte aligned size.\n"); + return -EINVAL; + } + if (!idx || !pos) + return -EINVAL; + if ((char*)pos < (char*)idx || (char*)pos + size < (char*)idx || + (char*)pos > (char*)idx + sizeof(INDEX_BLOCK) - + sizeof(INDEX_HEADER) + + le32_to_cpu(idx->index.allocated_size) || + (char*)pos + size > (char*)idx + sizeof(INDEX_BLOCK) - + sizeof(INDEX_HEADER) + + le32_to_cpu(idx->index.allocated_size)) + return -EINVAL; + /* The - sizeof(INDEX_ENTRY_HEADER) is for the index terminator. */ + if ((char*)pos - (char*)&idx->index > + (int)le32_to_cpu(idx->index.index_length) + - (int)sizeof(INDEX_ENTRY_HEADER)) + return -EINVAL; +#endif + biu = le32_to_cpu(idx->index.index_length); + /* Do we have enough space? */ + if (biu + size > le32_to_cpu(idx->index.allocated_size)) + return -ENOSPC; + /* Move everything after pos to pos + size. */ + memmove((char*)pos + size, (char*)pos, biu - ((char*)pos - + (char*)&idx->index)); + /* Update index block. */ + idx->index.index_length = cpu_to_le32(biu + size); + return 0; +} + +/** + * ntfs_index_keys_compare + * + * not all types of COLLATION_RULES supported yet... + * added as needed.. (remove this comment when all are added) + */ +static int ntfs_index_keys_compare(u8 *key1, u8 *key2, int key1_length, + int key2_length, COLLATION_RULES collation_rule) +{ + u32 u1, u2; + int i; + + if (collation_rule == COLLATION_NTOFS_ULONG) { + /* i.e. $SII or $QUOTA-$Q */ + u1 = le32_to_cpup((const le32*)key1); + u2 = le32_to_cpup((const le32*)key2); + if (u1 < u2) + return -1; + if (u1 > u2) + return 1; + /* u1 == u2 */ + return 0; + } + if (collation_rule == COLLATION_NTOFS_ULONGS) { + /* i.e $OBJID-$O */ + i = 0; + while (i < min(key1_length, key2_length)) { + u1 = le32_to_cpup((const le32*)(key1 + i)); + u2 = le32_to_cpup((const le32*)(key2 + i)); + if (u1 < u2) + return -1; + if (u1 > u2) + return 1; + /* u1 == u2 */ + i += sizeof(u32); + } + if (key1_length < key2_length) + return -1; + if (key1_length > key2_length) + return 1; + return 0; + } + if (collation_rule == COLLATION_NTOFS_SECURITY_HASH) { + /* i.e. $SDH */ + u1 = le32_to_cpu(((SDH_INDEX_KEY*)key1)->hash); + u2 = le32_to_cpu(((SDH_INDEX_KEY*)key2)->hash); + if (u1 < u2) + return -1; + if (u1 > u2) + return 1; + /* u1 == u2 */ + u1 = le32_to_cpu(((SDH_INDEX_KEY*)key1)->security_id); + u2 = le32_to_cpu(((SDH_INDEX_KEY*)key2)->security_id); + if (u1 < u2) + return -1; + if (u1 > u2) + return 1; + return 0; + } + if (collation_rule == COLLATION_NTOFS_SID) { + /* i.e. $QUOTA-O */ + i = memcmp(key1, key2, min(key1_length, key2_length)); + if (!i) { + if (key1_length < key2_length) + return -1; + if (key1_length > key2_length) + return 1; + } + return i; + } + ntfs_log_critical("ntfs_index_keys_compare called without supported " + "collation rule.\n"); + return 0; /* Claim they're equal. What else can we do? */ +} + +/** + * insert_index_entry_in_res_dir_index + * + * i.e. insert an index_entry in some named index_root + * simplified search method, works for mkntfs + */ +static int insert_index_entry_in_res_dir_index(INDEX_ENTRY *idx, u32 idx_size, + MFT_RECORD *m, ntfschar *name, u32 name_size, ATTR_TYPES type) +{ + ntfs_attr_search_ctx *ctx; + INDEX_HEADER *idx_header; + INDEX_ENTRY *idx_entry, *idx_end; + ATTR_RECORD *a; + COLLATION_RULES collation_rule; + int err, i; + + err = 0; + /* does it fit ?*/ + if (g_vol->mft_record_size > idx_size + le32_to_cpu(m->bytes_allocated)) + return -ENOSPC; + /* find the INDEX_ROOT attribute:*/ + ctx = ntfs_attr_get_search_ctx(NULL, m); + if (!ctx) { + ntfs_log_error("Failed to allocate attribute search " + "context.\n"); + err = -ENOMEM; + goto err_out; + } + if (mkntfs_attr_lookup(AT_INDEX_ROOT, name, name_size, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + err = -EEXIST; + goto err_out; + } + /* found attribute */ + a = (ATTR_RECORD*)ctx->attr; + collation_rule = ((INDEX_ROOT*)((u8*)a + + le16_to_cpu(a->value_offset)))->collation_rule; + idx_header = (INDEX_HEADER*)((u8*)a + le16_to_cpu(a->value_offset) + + 0x10); + idx_entry = (INDEX_ENTRY*)((u8*)idx_header + + le32_to_cpu(idx_header->entries_offset)); + idx_end = (INDEX_ENTRY*)((u8*)idx_entry + + le32_to_cpu(idx_header->index_length)); + /* + * Loop until we exceed valid memory (corruption case) or until we + * reach the last entry. + */ + if (type == AT_FILE_NAME) { + while (((u8*)idx_entry < (u8*)idx_end) && + !(idx_entry->ie_flags & INDEX_ENTRY_END)) { + /* + i = ntfs_file_values_compare(&idx->key.file_name, + &idx_entry->key.file_name, 1, + IGNORE_CASE, g_vol->upcase, + g_vol->upcase_len); + */ + i = ntfs_names_full_collate(idx->key.file_name.file_name, idx->key.file_name.file_name_length, + idx_entry->key.file_name.file_name, idx_entry->key.file_name.file_name_length, + IGNORE_CASE, g_vol->upcase, + g_vol->upcase_len); + /* + * If @file_name collates before ie->key.file_name, + * there is no matching index entry. + */ + if (i == -1) + break; + /* If file names are not equal, continue search. */ + if (i) + goto do_next; + if (idx->key.file_name.file_name_type != + FILE_NAME_POSIX || + idx_entry->key.file_name.file_name_type + != FILE_NAME_POSIX) + return -EEXIST; + /* + i = ntfs_file_values_compare(&idx->key.file_name, + &idx_entry->key.file_name, 1, + CASE_SENSITIVE, g_vol->upcase, + g_vol->upcase_len); + */ + i = ntfs_names_full_collate(idx->key.file_name.file_name, idx->key.file_name.file_name_length, + idx_entry->key.file_name.file_name, idx_entry->key.file_name.file_name_length, + CASE_SENSITIVE, g_vol->upcase, + g_vol->upcase_len); + if (!i) + return -EEXIST; + if (i == -1) + break; +do_next: + idx_entry = (INDEX_ENTRY*)((u8*)idx_entry + + le16_to_cpu(idx_entry->length)); + } + } else if (type == AT_UNUSED) { /* case view */ + while (((u8*)idx_entry < (u8*)idx_end) && + !(idx_entry->ie_flags & INDEX_ENTRY_END)) { + i = ntfs_index_keys_compare((u8*)idx + 0x10, + (u8*)idx_entry + 0x10, + le16_to_cpu(idx->key_length), + le16_to_cpu(idx_entry->key_length), + collation_rule); + if (!i) + return -EEXIST; + if (i == -1) + break; + idx_entry = (INDEX_ENTRY*)((u8*)idx_entry + + le16_to_cpu(idx_entry->length)); + } + } else + return -EINVAL; + memmove((u8*)idx_entry + idx_size, (u8*)idx_entry, + le32_to_cpu(m->bytes_in_use) - + ((u8*)idx_entry - (u8*)m)); + memcpy((u8*)idx_entry, (u8*)idx, idx_size); + /* Adjust various offsets, etc... */ + m->bytes_in_use = cpu_to_le32(le32_to_cpu(m->bytes_in_use) + idx_size); + a->length = cpu_to_le32(le32_to_cpu(a->length) + idx_size); + a->value_length = cpu_to_le32(le32_to_cpu(a->value_length) + idx_size); + idx_header->index_length = cpu_to_le32( + le32_to_cpu(idx_header->index_length) + idx_size); + idx_header->allocated_size = cpu_to_le32( + le32_to_cpu(idx_header->allocated_size) + idx_size); +err_out: + if (ctx) + ntfs_attr_put_search_ctx(ctx); + return err; +} + +/** + * initialize_secure + * + * initializes $Secure's $SDH and $SII indexes from $SDS datastream + */ +static int initialize_secure(char *sds, u32 sds_size, MFT_RECORD *m) +{ + int err, sdh_size, sii_size; + SECURITY_DESCRIPTOR_HEADER *sds_header; + INDEX_ENTRY *idx_entry_sdh, *idx_entry_sii; + SDH_INDEX_DATA *sdh_data; + SII_INDEX_DATA *sii_data; + + sds_header = (SECURITY_DESCRIPTOR_HEADER*)sds; + sdh_size = sizeof(INDEX_ENTRY_HEADER); + sdh_size += sizeof(SDH_INDEX_KEY) + sizeof(SDH_INDEX_DATA); + sii_size = sizeof(INDEX_ENTRY_HEADER); + sii_size += sizeof(SII_INDEX_KEY) + sizeof(SII_INDEX_DATA); + idx_entry_sdh = ntfs_calloc(sizeof(INDEX_ENTRY)); + if (!idx_entry_sdh) + return -errno; + idx_entry_sii = ntfs_calloc(sizeof(INDEX_ENTRY)); + if (!idx_entry_sii) { + free(idx_entry_sdh); + return -errno; + } + err = 0; + + while ((char*)sds_header < (char*)sds + sds_size) { + if (!sds_header->length) + break; + /* SDH index entry */ + idx_entry_sdh->data_offset = const_cpu_to_le16(0x18); + idx_entry_sdh->data_length = const_cpu_to_le16(0x14); + idx_entry_sdh->reservedV = const_cpu_to_le32(0x00); + idx_entry_sdh->length = const_cpu_to_le16(0x30); + idx_entry_sdh->key_length = const_cpu_to_le16(0x08); + idx_entry_sdh->ie_flags = const_cpu_to_le16(0x00); + idx_entry_sdh->reserved = const_cpu_to_le16(0x00); + idx_entry_sdh->key.sdh.hash = sds_header->hash; + idx_entry_sdh->key.sdh.security_id = sds_header->security_id; + sdh_data = (SDH_INDEX_DATA*)((u8*)idx_entry_sdh + + le16_to_cpu(idx_entry_sdh->data_offset)); + sdh_data->hash = sds_header->hash; + sdh_data->security_id = sds_header->security_id; + sdh_data->offset = sds_header->offset; + sdh_data->length = sds_header->length; + sdh_data->reserved_II = const_cpu_to_le32(0x00490049); + + /* SII index entry */ + idx_entry_sii->data_offset = const_cpu_to_le16(0x14); + idx_entry_sii->data_length = const_cpu_to_le16(0x14); + idx_entry_sii->reservedV = const_cpu_to_le32(0x00); + idx_entry_sii->length = const_cpu_to_le16(0x28); + idx_entry_sii->key_length = const_cpu_to_le16(0x04); + idx_entry_sii->ie_flags = const_cpu_to_le16(0x00); + idx_entry_sii->reserved = const_cpu_to_le16(0x00); + idx_entry_sii->key.sii.security_id = sds_header->security_id; + sii_data = (SII_INDEX_DATA*)((u8*)idx_entry_sii + + le16_to_cpu(idx_entry_sii->data_offset)); + sii_data->hash = sds_header->hash; + sii_data->security_id = sds_header->security_id; + sii_data->offset = sds_header->offset; + sii_data->length = sds_header->length; + if ((err = insert_index_entry_in_res_dir_index(idx_entry_sdh, + sdh_size, m, NTFS_INDEX_SDH, 4, AT_UNUSED))) + break; + if ((err = insert_index_entry_in_res_dir_index(idx_entry_sii, + sii_size, m, NTFS_INDEX_SII, 4, AT_UNUSED))) + break; + sds_header = (SECURITY_DESCRIPTOR_HEADER*)((u8*)sds_header + + ((le32_to_cpu(sds_header->length) + 15) & ~15)); + } + free(idx_entry_sdh); + free(idx_entry_sii); + return err; +} + +/** + * initialize_quota + * + * initialize $Quota with the default quota index-entries. + */ +static int initialize_quota(MFT_RECORD *m) +{ + int o_size, q1_size, q2_size, err, i; + INDEX_ENTRY *idx_entry_o, *idx_entry_q1, *idx_entry_q2; + QUOTA_O_INDEX_DATA *idx_entry_o_data; + QUOTA_CONTROL_ENTRY *idx_entry_q1_data, *idx_entry_q2_data; + + err = 0; + /* q index entry num 1 */ + q1_size = 0x48; + idx_entry_q1 = ntfs_calloc(q1_size); + if (!idx_entry_q1) + return errno; + idx_entry_q1->data_offset = const_cpu_to_le16(0x14); + idx_entry_q1->data_length = const_cpu_to_le16(0x30); + idx_entry_q1->reservedV = const_cpu_to_le32(0x00); + idx_entry_q1->length = const_cpu_to_le16(0x48); + idx_entry_q1->key_length = const_cpu_to_le16(0x04); + idx_entry_q1->ie_flags = const_cpu_to_le16(0x00); + idx_entry_q1->reserved = const_cpu_to_le16(0x00); + idx_entry_q1->key.owner_id = const_cpu_to_le32(0x01); + idx_entry_q1_data = (QUOTA_CONTROL_ENTRY*)((char*)idx_entry_q1 + + le16_to_cpu(idx_entry_q1->data_offset)); + idx_entry_q1_data->version = const_cpu_to_le32(0x02); + idx_entry_q1_data->flags = QUOTA_FLAG_DEFAULT_LIMITS; + idx_entry_q1_data->bytes_used = const_cpu_to_le64(0x00); + idx_entry_q1_data->change_time = mkntfs_time(); + idx_entry_q1_data->threshold = cpu_to_sle64(-1); + idx_entry_q1_data->limit = cpu_to_sle64(-1); + idx_entry_q1_data->exceeded_time = const_cpu_to_le64(0); + err = insert_index_entry_in_res_dir_index(idx_entry_q1, q1_size, m, + NTFS_INDEX_Q, 2, AT_UNUSED); + free(idx_entry_q1); + if (err) + return err; + /* q index entry num 2 */ + q2_size = 0x58; + idx_entry_q2 = ntfs_calloc(q2_size); + if (!idx_entry_q2) + return errno; + idx_entry_q2->data_offset = const_cpu_to_le16(0x14); + idx_entry_q2->data_length = const_cpu_to_le16(0x40); + idx_entry_q2->reservedV = const_cpu_to_le32(0x00); + idx_entry_q2->length = const_cpu_to_le16(0x58); + idx_entry_q2->key_length = const_cpu_to_le16(0x04); + idx_entry_q2->ie_flags = const_cpu_to_le16(0x00); + idx_entry_q2->reserved = const_cpu_to_le16(0x00); + idx_entry_q2->key.owner_id = QUOTA_FIRST_USER_ID; + idx_entry_q2_data = (QUOTA_CONTROL_ENTRY*)((char*)idx_entry_q2 + + le16_to_cpu(idx_entry_q2->data_offset)); + idx_entry_q2_data->version = const_cpu_to_le32(0x02); + idx_entry_q2_data->flags = QUOTA_FLAG_DEFAULT_LIMITS; + idx_entry_q2_data->bytes_used = const_cpu_to_le64(0x00); + idx_entry_q2_data->change_time = mkntfs_time(); + idx_entry_q2_data->threshold = cpu_to_sle64(-1); + idx_entry_q2_data->limit = cpu_to_sle64(-1); + idx_entry_q2_data->exceeded_time = const_cpu_to_le64(0); + idx_entry_q2_data->sid.revision = 1; + idx_entry_q2_data->sid.sub_authority_count = 2; + for (i = 0; i < 5; i++) + idx_entry_q2_data->sid.identifier_authority.value[i] = 0; + idx_entry_q2_data->sid.identifier_authority.value[5] = 0x05; + idx_entry_q2_data->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + idx_entry_q2_data->sid.sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + err = insert_index_entry_in_res_dir_index(idx_entry_q2, q2_size, m, + NTFS_INDEX_Q, 2, AT_UNUSED); + free(idx_entry_q2); + if (err) + return err; + o_size = 0x28; + idx_entry_o = ntfs_calloc(o_size); + if (!idx_entry_o) + return errno; + idx_entry_o->data_offset = const_cpu_to_le16(0x20); + idx_entry_o->data_length = const_cpu_to_le16(0x04); + idx_entry_o->reservedV = const_cpu_to_le32(0x00); + idx_entry_o->length = const_cpu_to_le16(0x28); + idx_entry_o->key_length = const_cpu_to_le16(0x10); + idx_entry_o->ie_flags = const_cpu_to_le16(0x00); + idx_entry_o->reserved = const_cpu_to_le16(0x00); + idx_entry_o->key.sid.revision = 0x01; + idx_entry_o->key.sid.sub_authority_count = 0x02; + for (i = 0; i < 5; i++) + idx_entry_o->key.sid.identifier_authority.value[i] = 0; + idx_entry_o->key.sid.identifier_authority.value[5] = 0x05; + idx_entry_o->key.sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + idx_entry_o->key.sid.sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + idx_entry_o_data = (QUOTA_O_INDEX_DATA*)((char*)idx_entry_o + + le16_to_cpu(idx_entry_o->data_offset)); + idx_entry_o_data->owner_id = QUOTA_FIRST_USER_ID; + /* 20 00 00 00 padding after here on ntfs 3.1. 3.0 is unchecked. */ + idx_entry_o_data->unknown = const_cpu_to_le32(32); + err = insert_index_entry_in_res_dir_index(idx_entry_o, o_size, m, + NTFS_INDEX_O, 2, AT_UNUSED); + free(idx_entry_o); + + return err; +} + +/** + * insert_file_link_in_dir_index + * + * Insert the fully completed FILE_NAME_ATTR @file_name which is inside + * the file with mft reference @file_ref into the index (allocation) block + * @idx (which belongs to @file_ref's parent directory). + * + * Return 0 on success or -errno on error. + */ +static int insert_file_link_in_dir_index(INDEX_BLOCK *idx, leMFT_REF file_ref, + FILE_NAME_ATTR *file_name, u32 file_name_size) +{ + int err, i; + INDEX_ENTRY *ie; + char *index_end; + + /* + * Lookup dir entry @file_name in dir @idx to determine correct + * insertion location. FIXME: Using a very oversimplified lookup + * method which is sufficient for mkntfs but no good whatsoever in + * real world scenario. (AIA) + */ + + index_end = (char*)&idx->index + le32_to_cpu(idx->index.index_length); + ie = (INDEX_ENTRY*)((char*)&idx->index + + le32_to_cpu(idx->index.entries_offset)); + /* + * Loop until we exceed valid memory (corruption case) or until we + * reach the last entry. + */ + while ((char*)ie < index_end && !(ie->ie_flags & INDEX_ENTRY_END)) { +#if 0 +#ifdef DEBUG + ntfs_log_debug("file_name_attr1->file_name_length = %i\n", + file_name->file_name_length); + if (file_name->file_name_length) { + char *__buf = NULL; + i = ntfs_ucstombs((ntfschar*)&file_name->file_name, + file_name->file_name_length, &__buf, 0); + if (i < 0) + ntfs_log_debug("Name contains non-displayable " + "Unicode characters.\n"); + ntfs_log_debug("file_name_attr1->file_name = %s\n", + __buf); + free(__buf); + } + ntfs_log_debug("file_name_attr2->file_name_length = %i\n", + ie->key.file_name.file_name_length); + if (ie->key.file_name.file_name_length) { + char *__buf = NULL; + i = ntfs_ucstombs(ie->key.file_name.file_name, + ie->key.file_name.file_name_length + 1, &__buf, + 0); + if (i < 0) + ntfs_log_debug("Name contains non-displayable " + "Unicode characters.\n"); + ntfs_log_debug("file_name_attr2->file_name = %s\n", + __buf); + free(__buf); + } +#endif +#endif + /* + i = ntfs_file_values_compare(file_name, + (FILE_NAME_ATTR*)&ie->key.file_name, 1, + IGNORE_CASE, g_vol->upcase, g_vol->upcase_len); + */ + i = ntfs_names_full_collate(file_name->file_name, file_name->file_name_length, + ((FILE_NAME_ATTR*)&ie->key.file_name)->file_name, ((FILE_NAME_ATTR*)&ie->key.file_name)->file_name_length, + IGNORE_CASE, g_vol->upcase, g_vol->upcase_len); + /* + * If @file_name collates before ie->key.file_name, there is no + * matching index entry. + */ + if (i == -1) + break; + /* If file names are not equal, continue search. */ + if (i) + goto do_next; + /* File names are equal when compared ignoring case. */ + /* + * If BOTH file names are in the POSIX namespace, do a case + * sensitive comparison as well. Otherwise the names match so + * we return -EEXIST. FIXME: There are problems with this in a + * real world scenario, when one is POSIX and one isn't, but + * fine for mkntfs where we don't use POSIX namespace at all + * and hence this following code is luxury. (AIA) + */ + if (file_name->file_name_type != FILE_NAME_POSIX || + ie->key.file_name.file_name_type != FILE_NAME_POSIX) + return -EEXIST; + /* + i = ntfs_file_values_compare(file_name, + (FILE_NAME_ATTR*)&ie->key.file_name, 1, + CASE_SENSITIVE, g_vol->upcase, + g_vol->upcase_len); + */ + i = ntfs_names_full_collate(file_name->file_name, file_name->file_name_length, + ((FILE_NAME_ATTR*)&ie->key.file_name)->file_name, ((FILE_NAME_ATTR*)&ie->key.file_name)->file_name_length, + CASE_SENSITIVE, g_vol->upcase, g_vol->upcase_len); + if (i == -1) + break; + /* Complete match. Bugger. Can't insert. */ + if (!i) + return -EEXIST; +do_next: +#ifdef DEBUG + /* Next entry. */ + if (!ie->length) { + ntfs_log_debug("BUG: ie->length is zero, breaking out " + "of loop.\n"); + break; + } +#endif + ie = (INDEX_ENTRY*)((char*)ie + le16_to_cpu(ie->length)); + }; + i = (sizeof(INDEX_ENTRY_HEADER) + file_name_size + 7) & ~7; + err = make_room_for_index_entry_in_index_block(idx, ie, i); + if (err) { + ntfs_log_error("make_room_for_index_entry_in_index_block " + "failed: %s\n", strerror(-err)); + return err; + } + /* Create entry in place and copy file name attribute value. */ + ie->indexed_file = file_ref; + ie->length = cpu_to_le16(i); + ie->key_length = cpu_to_le16(file_name_size); + ie->ie_flags = cpu_to_le16(0); + ie->reserved = cpu_to_le16(0); + memcpy((char*)&ie->key.file_name, (char*)file_name, file_name_size); + return 0; +} + +/** + * create_hardlink_res + * + * Create a file_name_attribute in the mft record @m_file which points to the + * parent directory with mft reference @ref_parent. + * + * Then, insert an index entry with this file_name_attribute in the index + * root @idx of the index_root attribute of the parent directory. + * + * @ref_file is the mft reference of @m_file. + * + * Return 0 on success or -errno on error. + */ +static int create_hardlink_res(MFT_RECORD *m_parent, const leMFT_REF ref_parent, + MFT_RECORD *m_file, const leMFT_REF ref_file, + const s64 allocated_size, const s64 data_size, + const FILE_ATTR_FLAGS flags, const u16 packed_ea_size, + const u32 reparse_point_tag, const char *file_name, + const FILE_NAME_TYPE_FLAGS file_name_type) +{ + FILE_NAME_ATTR *fn; + int i, fn_size, idx_size; + INDEX_ENTRY *idx_entry_new; + ntfschar *uname; + + /* Create the file_name attribute. */ + i = (strlen(file_name) + 1) * sizeof(ntfschar); + fn_size = sizeof(FILE_NAME_ATTR) + i; + fn = ntfs_malloc(fn_size); + if (!fn) + return -errno; + fn->parent_directory = ref_parent; + fn->creation_time = stdinfo_time(m_file); + fn->last_data_change_time = fn->creation_time; + fn->last_mft_change_time = fn->creation_time; + fn->last_access_time = fn->creation_time; + fn->allocated_size = cpu_to_sle64(allocated_size); + fn->data_size = cpu_to_sle64(data_size); + fn->file_attributes = flags; + /* These are in a union so can't have both. */ + if (packed_ea_size && reparse_point_tag) { + free(fn); + return -EINVAL; + } + if (packed_ea_size) { + free(fn); + return -EINVAL; + } + if (packed_ea_size) { + fn->packed_ea_size = cpu_to_le16(packed_ea_size); + fn->reserved = cpu_to_le16(0); + } else { + fn->reparse_point_tag = cpu_to_le32(reparse_point_tag); + } + fn->file_name_type = file_name_type; + uname = fn->file_name; + i = ntfs_mbstoucs_libntfscompat(file_name, &uname, i); + if (i < 1) { + free(fn); + return -EINVAL; + } + if (i > 0xff) { + free(fn); + return -ENAMETOOLONG; + } + /* No terminating null in file names. */ + fn->file_name_length = i; + fn_size = sizeof(FILE_NAME_ATTR) + i * sizeof(ntfschar); + /* Increment the link count of @m_file. */ + i = le16_to_cpu(m_file->link_count); + if (i == 0xffff) { + ntfs_log_error("Too many hardlinks present already.\n"); + free(fn); + return -EINVAL; + } + m_file->link_count = cpu_to_le16(i + 1); + /* Add the file_name to @m_file. */ + i = insert_resident_attr_in_mft_record(m_file, AT_FILE_NAME, NULL, 0, + CASE_SENSITIVE, const_cpu_to_le16(0), + RESIDENT_ATTR_IS_INDEXED, (u8*)fn, fn_size); + if (i < 0) { + ntfs_log_error("create_hardlink failed adding file name " + "attribute: %s\n", strerror(-i)); + free(fn); + /* Undo link count increment. */ + m_file->link_count = cpu_to_le16( + le16_to_cpu(m_file->link_count) - 1); + return i; + } + /* Insert the index entry for file_name in @idx. */ + idx_size = (fn_size + 7) & ~7; + idx_entry_new = ntfs_calloc(idx_size + 0x10); + if (!idx_entry_new) + return -errno; + idx_entry_new->indexed_file = ref_file; + idx_entry_new->length = cpu_to_le16(idx_size + 0x10); + idx_entry_new->key_length = cpu_to_le16(fn_size); + memcpy((u8*)idx_entry_new + 0x10, (u8*)fn, fn_size); + i = insert_index_entry_in_res_dir_index(idx_entry_new, idx_size + 0x10, + m_parent, NTFS_INDEX_I30, 4, AT_FILE_NAME); + if (i < 0) { + ntfs_log_error("create_hardlink failed inserting index entry: " + "%s\n", strerror(-i)); + /* FIXME: Remove the file name attribute from @m_file. */ + free(idx_entry_new); + free(fn); + /* Undo link count increment. */ + m_file->link_count = cpu_to_le16( + le16_to_cpu(m_file->link_count) - 1); + return i; + } + free(idx_entry_new); + free(fn); + return 0; +} + +/** + * create_hardlink + * + * Create a file_name_attribute in the mft record @m_file which points to the + * parent directory with mft reference @ref_parent. + * + * Then, insert an index entry with this file_name_attribute in the index + * block @idx of the index allocation attribute of the parent directory. + * + * @ref_file is the mft reference of @m_file. + * + * Return 0 on success or -errno on error. + */ +static int create_hardlink(INDEX_BLOCK *idx, const leMFT_REF ref_parent, + MFT_RECORD *m_file, const leMFT_REF ref_file, + const s64 allocated_size, const s64 data_size, + const FILE_ATTR_FLAGS flags, const u16 packed_ea_size, + const u32 reparse_point_tag, const char *file_name, + const FILE_NAME_TYPE_FLAGS file_name_type) +{ + FILE_NAME_ATTR *fn; + int i, fn_size; + ntfschar *uname; + + /* Create the file_name attribute. */ + i = (strlen(file_name) + 1) * sizeof(ntfschar); + fn_size = sizeof(FILE_NAME_ATTR) + i; + fn = ntfs_malloc(fn_size); + if (!fn) + return -errno; + fn->parent_directory = ref_parent; + fn->creation_time = stdinfo_time(m_file); + fn->last_data_change_time = fn->creation_time; + fn->last_mft_change_time = fn->creation_time; + fn->last_access_time = fn->creation_time; + /* allocated size depends on unnamed data being resident */ + if (allocated_size && non_resident_unnamed_data(m_file)) + fn->allocated_size = cpu_to_sle64(allocated_size); + else + fn->allocated_size = cpu_to_sle64((data_size + 7) & -8); + fn->data_size = cpu_to_sle64(data_size); + fn->file_attributes = flags; + /* These are in a union so can't have both. */ + if (packed_ea_size && reparse_point_tag) { + free(fn); + return -EINVAL; + } + if (packed_ea_size) { + fn->packed_ea_size = cpu_to_le16(packed_ea_size); + fn->reserved = cpu_to_le16(0); + } else { + fn->reparse_point_tag = cpu_to_le32(reparse_point_tag); + } + fn->file_name_type = file_name_type; + uname = fn->file_name; + i = ntfs_mbstoucs_libntfscompat(file_name, &uname, i); + if (i < 1) { + free(fn); + return -EINVAL; + } + if (i > 0xff) { + free(fn); + return -ENAMETOOLONG; + } + /* No terminating null in file names. */ + fn->file_name_length = i; + fn_size = sizeof(FILE_NAME_ATTR) + i * sizeof(ntfschar); + /* Increment the link count of @m_file. */ + i = le16_to_cpu(m_file->link_count); + if (i == 0xffff) { + ntfs_log_error("Too many hardlinks present already.\n"); + free(fn); + return -EINVAL; + } + m_file->link_count = cpu_to_le16(i + 1); + /* Add the file_name to @m_file. */ + i = insert_resident_attr_in_mft_record(m_file, AT_FILE_NAME, NULL, 0, + CASE_SENSITIVE, cpu_to_le16(0), + RESIDENT_ATTR_IS_INDEXED, (u8*)fn, fn_size); + if (i < 0) { + ntfs_log_error("create_hardlink failed adding file name attribute: " + "%s\n", strerror(-i)); + free(fn); + /* Undo link count increment. */ + m_file->link_count = cpu_to_le16( + le16_to_cpu(m_file->link_count) - 1); + return i; + } + /* Insert the index entry for file_name in @idx. */ + i = insert_file_link_in_dir_index(idx, ref_file, fn, fn_size); + if (i < 0) { + ntfs_log_error("create_hardlink failed inserting index entry: %s\n", + strerror(-i)); + /* FIXME: Remove the file name attribute from @m_file. */ + free(fn); + /* Undo link count increment. */ + m_file->link_count = cpu_to_le16( + le16_to_cpu(m_file->link_count) - 1); + return i; + } + free(fn); + return 0; +} + +/** + * index_obj_id_insert + * + * Insert an index entry with the key @guid and data pointing to the mft record + * @ref in the $O index root of the mft record @m (which must be the mft record + * for $ObjId). + * + * Return 0 on success or -errno on error. + */ +static int index_obj_id_insert(MFT_RECORD *m, const GUID *guid, + const leMFT_REF ref) +{ + INDEX_ENTRY *idx_entry_new; + int data_ofs, idx_size, err; + OBJ_ID_INDEX_DATA *oi; + + /* + * Insert the index entry for the object id in the index. + * + * First determine the size of the index entry to be inserted. This + * consists of the index entry header, followed by the index key, i.e. + * the GUID, followed by the index data, i.e. OBJ_ID_INDEX_DATA. + */ + data_ofs = (sizeof(INDEX_ENTRY_HEADER) + sizeof(GUID) + 7) & ~7; + idx_size = (data_ofs + sizeof(OBJ_ID_INDEX_DATA) + 7) & ~7; + idx_entry_new = ntfs_calloc(idx_size); + if (!idx_entry_new) + return -errno; + idx_entry_new->data_offset = cpu_to_le16(data_ofs); + idx_entry_new->data_length = cpu_to_le16(sizeof(OBJ_ID_INDEX_DATA)); + idx_entry_new->length = cpu_to_le16(idx_size); + idx_entry_new->key_length = cpu_to_le16(sizeof(GUID)); + idx_entry_new->key.object_id = *guid; + oi = (OBJ_ID_INDEX_DATA*)((u8*)idx_entry_new + data_ofs); + oi->mft_reference = ref; + err = insert_index_entry_in_res_dir_index(idx_entry_new, idx_size, m, + NTFS_INDEX_O, 2, AT_UNUSED); + free(idx_entry_new); + if (err < 0) { + ntfs_log_error("index_obj_id_insert failed inserting index " + "entry: %s\n", strerror(-err)); + return err; + } + return 0; +} + +/** + * mkntfs_cleanup + */ +static void mkntfs_cleanup(void) +{ + struct BITMAP_ALLOCATION *p, *q; + + /* Close the volume */ + if (g_vol) { + if (g_vol->dev) { + if (NDevOpen(g_vol->dev) && g_vol->dev->d_ops->close(g_vol->dev)) + ntfs_log_perror("Warning: Could not close %s", g_vol->dev->d_name); + ntfs_device_free(g_vol->dev); + } + free(g_vol->vol_name); + free(g_vol->attrdef); + free(g_vol->upcase); + free(g_vol); + g_vol = NULL; + } + + /* Free any memory we've used */ + free(g_bad_blocks); g_bad_blocks = NULL; + free(g_buf); g_buf = NULL; + free(g_index_block); g_index_block = NULL; + free(g_dynamic_buf); g_dynamic_buf = NULL; + free(g_mft_bitmap); g_mft_bitmap = NULL; + free(g_rl_bad); g_rl_bad = NULL; + free(g_rl_boot); g_rl_boot = NULL; + free(g_rl_logfile); g_rl_logfile = NULL; + free(g_rl_mft); g_rl_mft = NULL; + free(g_rl_mft_bmp); g_rl_mft_bmp = NULL; + free(g_rl_mftmirr); g_rl_mftmirr = NULL; + + p = g_allocation; + while (p) { + q = p->next; + free(p); + p = q; + } +} + + +/** + * mkntfs_open_partition - + */ +static BOOL mkntfs_open_partition(ntfs_volume *vol) +{ + BOOL result = FALSE; + int i; + struct stat sbuf; + unsigned long mnt_flags; + + /* + * Allocate and initialize an ntfs device structure and attach it to + * the volume. + */ + vol->dev = ntfs_device_alloc(opts.dev_name, 0, &ntfs_device_default_io_ops, NULL); + if (!vol->dev) { + ntfs_log_perror("Could not create device"); + goto done; + } + + /* Open the device for reading or reading and writing. */ + if (opts.no_action) { + ntfs_log_quiet("Running in READ-ONLY mode!\n"); + i = O_RDONLY; + } else { + i = O_RDWR; + } + if (vol->dev->d_ops->open(vol->dev, i)) { + if (errno == ENOENT) + ntfs_log_error("The device doesn't exist; did you specify it correctly?\n"); + else + ntfs_log_perror("Could not open %s", vol->dev->d_name); + goto done; + } + /* Verify we are dealing with a block device. */ + if (vol->dev->d_ops->stat(vol->dev, &sbuf)) { + ntfs_log_perror("Error getting information about %s", vol->dev->d_name); + goto done; + } + + if (!S_ISBLK(sbuf.st_mode)) { + ntfs_log_error("%s is not a block device.\n", vol->dev->d_name); + if (!opts.force) { + ntfs_log_error("Refusing to make a filesystem here!\n"); + goto done; + } + if (!opts.num_sectors) { + if (!sbuf.st_size && !sbuf.st_blocks) { + ntfs_log_error("You must specify the number of sectors.\n"); + goto done; + } + if (opts.sector_size) { + if (sbuf.st_size) + opts.num_sectors = sbuf.st_size / opts.sector_size; + else + opts.num_sectors = ((s64)sbuf.st_blocks << 9) / opts.sector_size; + } else { + if (sbuf.st_size) + opts.num_sectors = sbuf.st_size / 512; + else + opts.num_sectors = sbuf.st_blocks; + opts.sector_size = 512; + } + } + ntfs_log_warning("mkntfs forced anyway.\n"); +#ifdef HAVE_LINUX_MAJOR_H + } else if ((IDE_DISK_MAJOR(MAJOR(sbuf.st_rdev)) && + MINOR(sbuf.st_rdev) % 64 == 0) || + (SCSI_DISK_MAJOR(MAJOR(sbuf.st_rdev)) && + MINOR(sbuf.st_rdev) % 16 == 0)) { + ntfs_log_error("%s is entire device, not just one partition.\n", vol->dev->d_name); + if (!opts.force) { + ntfs_log_error("Refusing to make a filesystem here!\n"); + goto done; + } + ntfs_log_warning("mkntfs forced anyway.\n"); +#endif + } + /* Make sure the file system is not mounted. */ + if (ntfs_check_if_mounted(vol->dev->d_name, &mnt_flags)) { + ntfs_log_perror("Failed to determine whether %s is mounted", vol->dev->d_name); + } else if (mnt_flags & NTFS_MF_MOUNTED) { + ntfs_log_error("%s is mounted.\n", vol->dev->d_name); + if (!opts.force) { + ntfs_log_error("Refusing to make a filesystem here!\n"); + goto done; + } + ntfs_log_warning("mkntfs forced anyway. Hope /etc/mtab is incorrect.\n"); + } + result = TRUE; +done: + return result; +} + +/** + * mkntfs_get_page_size - detect the system's memory page size. + */ +static long mkntfs_get_page_size(void) +{ + return NTFS_PAGE_SIZE; +} + +/** + * mkntfs_override_vol_params - + */ +static BOOL mkntfs_override_vol_params(ntfs_volume *vol) +{ + s64 volume_size; + long page_size; + int i; + BOOL winboot = TRUE; + + /* If user didn't specify the sector size, determine it now. */ + if (opts.sector_size < 0) { + opts.sector_size = ntfs_device_sector_size_get(vol->dev); + if (opts.sector_size < 0) { + ntfs_log_warning("The sector size was not specified " + "for %s and it could not be obtained " + "automatically. It has been set to 512 " + "bytes.\n", vol->dev->d_name); + opts.sector_size = 512; + } + } + /* Validate sector size. */ + if ((opts.sector_size - 1) & opts.sector_size) { + ntfs_log_error("The sector size is invalid. It must be a " + "power of two, e.g. 512, 1024.\n"); + return FALSE; + } + if (opts.sector_size < 256 || opts.sector_size > 4096) { + ntfs_log_error("The sector size is invalid. The minimum size " + "is 256 bytes and the maximum is 4096 bytes.\n"); + return FALSE; + } + ntfs_log_debug("sector size = %ld bytes\n", opts.sector_size); + /* Now set the device block size to the sector size. */ + if (ntfs_device_block_size_set(vol->dev, opts.sector_size)) + ntfs_log_debug("Failed to set the device block size to the " + "sector size. This may cause problems when " + "creating the backup boot sector and also may " + "affect performance but should be harmless " + "otherwise. Error: %s\n", strerror(errno)); + /* If user didn't specify the number of sectors, determine it now. */ + if (opts.num_sectors < 0) { + opts.num_sectors = ntfs_device_size_get(vol->dev, + opts.sector_size); + if (opts.num_sectors <= 0) { + ntfs_log_error("Couldn't determine the size of %s. " + "Please specify the number of sectors " + "manually.\n", vol->dev->d_name); + return FALSE; + } + } + ntfs_log_debug("number of sectors = %lld (0x%llx)\n", opts.num_sectors, + opts.num_sectors); + /* + * Reserve the last sector for the backup boot sector unless the + * sector size is less than 512 bytes in which case reserve 512 bytes + * worth of sectors. + */ + i = 1; + if (opts.sector_size < 512) + i = 512 / opts.sector_size; + opts.num_sectors -= i; + /* If user didn't specify the partition start sector, determine it. */ + if (opts.part_start_sect < 0) { + opts.part_start_sect = ntfs_device_partition_start_sector_get( + vol->dev); + if (opts.part_start_sect < 0) { + ntfs_log_warning("The partition start sector was not " + "specified for %s and it could not be obtained " + "automatically. It has been set to 0.\n", + vol->dev->d_name); + opts.part_start_sect = 0; + winboot = FALSE; + } else if (opts.part_start_sect >> 32) { + ntfs_log_warning("The partition start sector specified " + "for %s and the automatically determined value " + "is too large. It has been set to 0.\n", + vol->dev->d_name); + opts.part_start_sect = 0; + winboot = FALSE; + } + } else if (opts.part_start_sect >> 32) { + ntfs_log_error("Invalid partition start sector. Maximum is " + "4294967295 (2^32-1).\n"); + return FALSE; + } + /* If user didn't specify the sectors per track, determine it now. */ + if (opts.sectors_per_track < 0) { + opts.sectors_per_track = ntfs_device_sectors_per_track_get( + vol->dev); + if (opts.sectors_per_track < 0) { + ntfs_log_warning("The number of sectors per track was " + "not specified for %s and it could not be " + "obtained automatically. It has been set to " + "0.\n", vol->dev->d_name); + opts.sectors_per_track = 0; + winboot = FALSE; + } else if (opts.sectors_per_track > 65535) { + ntfs_log_warning("The number of sectors per track was " + "not specified for %s and the automatically " + "determined value is too large. It has been " + "set to 0.\n", vol->dev->d_name); + opts.sectors_per_track = 0; + winboot = FALSE; + } + } else if (opts.sectors_per_track > 65535) { + ntfs_log_error("Invalid number of sectors per track. Maximum " + "is 65535.\n"); + return FALSE; + } + /* If user didn't specify the number of heads, determine it now. */ + if (opts.heads < 0) { + opts.heads = ntfs_device_heads_get(vol->dev); + if (opts.heads < 0) { + ntfs_log_warning("The number of heads was not " + "specified for %s and it could not be obtained " + "automatically. It has been set to 0.\n", + vol->dev->d_name); + opts.heads = 0; + winboot = FALSE; + } else if (opts.heads > 65535) { + ntfs_log_warning("The number of heads was not " + "specified for %s and the automatically " + "determined value is too large. It has been " + "set to 0.\n", vol->dev->d_name); + opts.heads = 0; + winboot = FALSE; + } + } else if (opts.heads > 65535) { + ntfs_log_error("Invalid number of heads. Maximum is 65535.\n"); + return FALSE; + } + volume_size = opts.num_sectors * opts.sector_size; + /* Validate volume size. */ + if (volume_size < (1 << 20)) { /* 1MiB */ + ntfs_log_error("Device is too small (%llikiB). Minimum NTFS " + "volume size is 1MiB.\n", + (long long)(volume_size / 1024)); + return FALSE; + } + ntfs_log_debug("volume size = %llikiB\n", volume_size / 1024); + /* If user didn't specify the cluster size, determine it now. */ + if (!vol->cluster_size) { + /* + * Windows Vista always uses 4096 bytes as the default cluster + * size regardless of the volume size so we do it, too. + */ + vol->cluster_size = 4096; + /* For small volumes on devices with large sector sizes. */ + if (vol->cluster_size < (u32)opts.sector_size) + vol->cluster_size = opts.sector_size; + /* + * For huge volumes, grow the cluster size until the number of + * clusters fits into 32 bits or the cluster size exceeds the + * maximum limit of 64kiB. + */ + while (volume_size >> (ffs(vol->cluster_size) - 1 + 32)) { + vol->cluster_size <<= 1; + if (vol->cluster_size > 65535) { + ntfs_log_error("Device is too large to hold an " + "NTFS volume (maximum size is " + "256TiB).\n"); + return FALSE; + } + } + ntfs_log_quiet("Cluster size has been automatically set to %u " + "bytes.\n", (unsigned)vol->cluster_size); + } + /* Validate cluster size. */ + if (vol->cluster_size & (vol->cluster_size - 1)) { + ntfs_log_error("The cluster size is invalid. It must be a " + "power of two, e.g. 1024, 4096.\n"); + return FALSE; + } + if (vol->cluster_size < (u32)opts.sector_size) { + ntfs_log_error("The cluster size is invalid. It must be equal " + "to, or larger than, the sector size.\n"); + return FALSE; + } + if (vol->cluster_size > 128 * (u32)opts.sector_size) { + ntfs_log_error("The cluster size is invalid. It cannot be " + "more that 128 times the size of the sector " + "size.\n"); + return FALSE; + } + if (vol->cluster_size > 65536) { + ntfs_log_error("The cluster size is invalid. The maximum " + "cluster size is 65536 bytes (64kiB).\n"); + return FALSE; + } + vol->cluster_size_bits = ffs(vol->cluster_size) - 1; + ntfs_log_debug("cluster size = %u bytes\n", + (unsigned int)vol->cluster_size); + if (vol->cluster_size > 4096) { + if (opts.enable_compression) { + if (!opts.force) { + ntfs_log_error("Windows cannot use compression " + "when the cluster size is " + "larger than 4096 bytes.\n"); + return FALSE; + } + opts.enable_compression = 0; + } + ntfs_log_warning("Windows cannot use compression when the " + "cluster size is larger than 4096 bytes. " + "Compression has been disabled for this " + "volume.\n"); + } + vol->nr_clusters = volume_size / vol->cluster_size; + /* + * Check the cluster_size and num_sectors for consistency with + * sector_size and num_sectors. And check both of these for consistency + * with volume_size. + */ + if ((vol->nr_clusters != ((opts.num_sectors * opts.sector_size) / + vol->cluster_size) || + (volume_size / opts.sector_size) != opts.num_sectors || + (volume_size / vol->cluster_size) != + vol->nr_clusters)) { + /* XXX is this code reachable? */ + ntfs_log_error("Illegal combination of volume/cluster/sector " + "size and/or cluster/sector number.\n"); + return FALSE; + } + ntfs_log_debug("number of clusters = %llu (0x%llx)\n", + vol->nr_clusters, vol->nr_clusters); + /* Number of clusters must fit within 32 bits (Win2k limitation). */ + if (vol->nr_clusters >> 32) { + if (vol->cluster_size >= 65536) { + ntfs_log_error("Device is too large to hold an NTFS " + "volume (maximum size is 256TiB).\n"); + return FALSE; + } + ntfs_log_error("Number of clusters exceeds 32 bits. Please " + "try again with a larger\ncluster size or " + "leave the cluster size unspecified and the " + "smallest possible cluster size for the size " + "of the device will be used.\n"); + return FALSE; + } + page_size = mkntfs_get_page_size(); + /* + * Set the mft record size. By default this is 1024 but it has to be + * at least as big as a sector and not bigger than a page on the system + * or the NTFS kernel driver will not be able to mount the volume. + * TODO: The mft record size should be user specifiable just like the + * "inode size" can be specified on other Linux/Unix file systems. + */ + vol->mft_record_size = 1024; + if (vol->mft_record_size < (u32)opts.sector_size) + vol->mft_record_size = opts.sector_size; + if (vol->mft_record_size > (unsigned long)page_size) + ntfs_log_warning("Mft record size (%u bytes) exceeds system " + "page size (%li bytes). You will not be able " + "to mount this volume using the NTFS kernel " + "driver.\n", (unsigned)vol->mft_record_size, + page_size); + vol->mft_record_size_bits = ffs(vol->mft_record_size) - 1; + ntfs_log_debug("mft record size = %u bytes\n", + (unsigned)vol->mft_record_size); + /* + * Set the index record size. By default this is 4096 but it has to be + * at least as big as a sector and not bigger than a page on the system + * or the NTFS kernel driver will not be able to mount the volume. + * FIXME: Should we make the index record size to be user specifiable? + */ + vol->indx_record_size = 4096; + if (vol->indx_record_size < (u32)opts.sector_size) + vol->indx_record_size = opts.sector_size; + if (vol->indx_record_size > (unsigned long)page_size) + ntfs_log_warning("Index record size (%u bytes) exceeds system " + "page size (%li bytes). You will not be able " + "to mount this volume using the NTFS kernel " + "driver.\n", (unsigned)vol->indx_record_size, + page_size); + vol->indx_record_size_bits = ffs(vol->indx_record_size) - 1; + ntfs_log_debug("index record size = %u bytes\n", + (unsigned)vol->indx_record_size); + if (!winboot) { + ntfs_log_warning("To boot from a device, Windows needs the " + "'partition start sector', the 'sectors per " + "track' and the 'number of heads' to be " + "set.\n"); + ntfs_log_warning("Windows will not be able to boot from this " + "device.\n"); + } + return TRUE; +} + +/** + * mkntfs_initialize_bitmaps - + */ +static BOOL mkntfs_initialize_bitmaps(void) +{ + u64 i; + int mft_bitmap_size; + + /* Determine lcn bitmap byte size and allocate it. */ + g_lcn_bitmap_byte_size = (g_vol->nr_clusters + 7) >> 3; + /* Needs to be multiple of 8 bytes. */ + g_lcn_bitmap_byte_size = (g_lcn_bitmap_byte_size + 7) & ~7; + i = (g_lcn_bitmap_byte_size + g_vol->cluster_size - 1) & + ~(g_vol->cluster_size - 1); + ntfs_log_debug("g_lcn_bitmap_byte_size = %i, allocated = %llu\n", + g_lcn_bitmap_byte_size, i); + g_dynamic_buf_size = mkntfs_get_page_size(); + g_dynamic_buf = (u8*)ntfs_calloc(g_dynamic_buf_size); + if (!g_dynamic_buf) + return FALSE; + /* + * $Bitmap can overlap the end of the volume. Any bits in this region + * must be set. This region also encompasses the backup boot sector. + */ + if (!bitmap_allocate(g_vol->nr_clusters, + ((s64)g_lcn_bitmap_byte_size << 3) - g_vol->nr_clusters)) + return (FALSE); + /* + * Mft size is 27 (NTFS 3.0+) mft records or one cluster, whichever is + * bigger. + */ + g_mft_size = 27; + g_mft_size *= g_vol->mft_record_size; + if (g_mft_size < (s32)g_vol->cluster_size) + g_mft_size = g_vol->cluster_size; + ntfs_log_debug("MFT size = %i (0x%x) bytes\n", g_mft_size, g_mft_size); + /* Determine mft bitmap size and allocate it. */ + mft_bitmap_size = g_mft_size / g_vol->mft_record_size; + /* Convert to bytes, at least one. */ + g_mft_bitmap_byte_size = (mft_bitmap_size + 7) >> 3; + /* Mft bitmap is allocated in multiples of 8 bytes. */ + g_mft_bitmap_byte_size = (g_mft_bitmap_byte_size + 7) & ~7; + ntfs_log_debug("mft_bitmap_size = %i, g_mft_bitmap_byte_size = %i\n", + mft_bitmap_size, g_mft_bitmap_byte_size); + g_mft_bitmap = ntfs_calloc(g_mft_bitmap_byte_size); + if (!g_mft_bitmap) + return FALSE; + /* Create runlist for mft bitmap. */ + g_rl_mft_bmp = ntfs_malloc(2 * sizeof(runlist)); + if (!g_rl_mft_bmp) + return FALSE; + + g_rl_mft_bmp[0].vcn = 0LL; + /* Mft bitmap is right after $Boot's data. */ + i = (8192 + g_vol->cluster_size - 1) / g_vol->cluster_size; + g_rl_mft_bmp[0].lcn = i; + /* + * Size is always one cluster, even though valid data size and + * initialized data size are only 8 bytes. + */ + g_rl_mft_bmp[1].vcn = 1LL; + g_rl_mft_bmp[0].length = 1LL; + g_rl_mft_bmp[1].lcn = -1LL; + g_rl_mft_bmp[1].length = 0LL; + /* Allocate cluster for mft bitmap. */ + return (bitmap_allocate(i,1)); +} + +/** + * mkntfs_initialize_rl_mft - + */ +static BOOL mkntfs_initialize_rl_mft(void) +{ + int j; + BOOL done; + + /* If user didn't specify the mft lcn, determine it now. */ + if (!g_mft_lcn) { + /* + * We start at the higher value out of 16kiB and just after the + * mft bitmap. + */ + g_mft_lcn = g_rl_mft_bmp[0].lcn + g_rl_mft_bmp[0].length; + if (g_mft_lcn * g_vol->cluster_size < 16 * 1024) + g_mft_lcn = (16 * 1024 + g_vol->cluster_size - 1) / + g_vol->cluster_size; + } + ntfs_log_debug("$MFT logical cluster number = 0x%llx\n", g_mft_lcn); + /* Determine MFT zone size. */ + g_mft_zone_end = g_vol->nr_clusters; + switch (opts.mft_zone_multiplier) { /* % of volume size in clusters */ + case 4: + g_mft_zone_end = g_mft_zone_end >> 1; /* 50% */ + break; + case 3: + g_mft_zone_end = g_mft_zone_end * 3 >> 3;/* 37.5% */ + break; + case 2: + g_mft_zone_end = g_mft_zone_end >> 2; /* 25% */ + break; + case 1: + default: + g_mft_zone_end = g_mft_zone_end >> 3; /* 12.5% */ + break; + } + ntfs_log_debug("MFT zone size = %lldkiB\n", g_mft_zone_end << + g_vol->cluster_size_bits >> 10 /* >> 10 == / 1024 */); + /* + * The mft zone begins with the mft data attribute, not at the beginning + * of the device. + */ + g_mft_zone_end += g_mft_lcn; + /* Create runlist for mft. */ + g_rl_mft = ntfs_malloc(2 * sizeof(runlist)); + if (!g_rl_mft) + return FALSE; + + g_rl_mft[0].vcn = 0LL; + g_rl_mft[0].lcn = g_mft_lcn; + /* rounded up division by cluster size */ + j = (g_mft_size + g_vol->cluster_size - 1) / g_vol->cluster_size; + g_rl_mft[1].vcn = j; + g_rl_mft[0].length = j; + g_rl_mft[1].lcn = -1LL; + g_rl_mft[1].length = 0LL; + /* Allocate clusters for mft. */ + bitmap_allocate(g_mft_lcn,j); + /* Determine mftmirr_lcn (middle of volume). */ + g_mftmirr_lcn = (opts.num_sectors * opts.sector_size >> 1) + / g_vol->cluster_size; + ntfs_log_debug("$MFTMirr logical cluster number = 0x%llx\n", + g_mftmirr_lcn); + /* Create runlist for mft mirror. */ + g_rl_mftmirr = ntfs_malloc(2 * sizeof(runlist)); + if (!g_rl_mftmirr) + return FALSE; + + g_rl_mftmirr[0].vcn = 0LL; + g_rl_mftmirr[0].lcn = g_mftmirr_lcn; + /* + * The mft mirror is either 4kb (the first four records) or one cluster + * in size, which ever is bigger. In either case, it contains a + * byte-for-byte identical copy of the beginning of the mft (i.e. either + * the first four records (4kb) or the first cluster worth of records, + * whichever is bigger). + */ + j = (4 * g_vol->mft_record_size + g_vol->cluster_size - 1) / g_vol->cluster_size; + g_rl_mftmirr[1].vcn = j; + g_rl_mftmirr[0].length = j; + g_rl_mftmirr[1].lcn = -1LL; + g_rl_mftmirr[1].length = 0LL; + /* Allocate clusters for mft mirror. */ + done = bitmap_allocate(g_mftmirr_lcn,j); + g_logfile_lcn = g_mftmirr_lcn + j; + ntfs_log_debug("$LogFile logical cluster number = 0x%llx\n", + g_logfile_lcn); + return (done); +} + +/** + * mkntfs_initialize_rl_logfile - + */ +static BOOL mkntfs_initialize_rl_logfile(void) +{ + int j; + u64 volume_size; + + /* Create runlist for log file. */ + g_rl_logfile = ntfs_malloc(2 * sizeof(runlist)); + if (!g_rl_logfile) + return FALSE; + + + volume_size = g_vol->nr_clusters << g_vol->cluster_size_bits; + + g_rl_logfile[0].vcn = 0LL; + g_rl_logfile[0].lcn = g_logfile_lcn; + /* + * Determine logfile_size from volume_size (rounded up to a cluster), + * making sure it does not overflow the end of the volume. + */ + if (volume_size < 2048LL * 1024) /* < 2MiB */ + g_logfile_size = 256LL * 1024; /* -> 256kiB */ + else if (volume_size < 4000000LL) /* < 4MB */ + g_logfile_size = 512LL * 1024; /* -> 512kiB */ + else if (volume_size <= 200LL * 1024 * 1024) /* < 200MiB */ + g_logfile_size = 2048LL * 1024; /* -> 2MiB */ + else { + /* + * FIXME: The $LogFile size is 64 MiB upwards from 12GiB but + * the "200" divider below apparently approximates "100" or + * some other value as the volume size decreases. For example: + * Volume size LogFile size Ratio + * 8799808 46048 191.100 + * 8603248 45072 190.877 + * 7341704 38768 189.375 + * 6144828 32784 187.433 + * 4192932 23024 182.111 + */ + if (volume_size >= 12LL << 30) /* > 12GiB */ + g_logfile_size = 64 << 20; /* -> 64MiB */ + else + g_logfile_size = (volume_size / 200) & + ~(g_vol->cluster_size - 1); + } + j = g_logfile_size / g_vol->cluster_size; + while (g_rl_logfile[0].lcn + j >= g_vol->nr_clusters) { + /* + * $Logfile would overflow volume. Need to make it smaller than + * the standard size. It's ok as we are creating a non-standard + * volume anyway if it is that small. + */ + g_logfile_size >>= 1; + j = g_logfile_size / g_vol->cluster_size; + } + g_logfile_size = (g_logfile_size + g_vol->cluster_size - 1) & + ~(g_vol->cluster_size - 1); + ntfs_log_debug("$LogFile (journal) size = %ikiB\n", + g_logfile_size / 1024); + /* + * FIXME: The 256kiB limit is arbitrary. Should find out what the real + * minimum requirement for Windows is so it doesn't blue screen. + */ + if (g_logfile_size < 256 << 10) { + ntfs_log_error("$LogFile would be created with invalid size. " + "This is not allowed as it would cause Windows " + "to blue screen and during boot.\n"); + return FALSE; + } + g_rl_logfile[1].vcn = j; + g_rl_logfile[0].length = j; + g_rl_logfile[1].lcn = -1LL; + g_rl_logfile[1].length = 0LL; + /* Allocate clusters for log file. */ + return (bitmap_allocate(g_logfile_lcn,j)); +} + +/** + * mkntfs_initialize_rl_boot - + */ +static BOOL mkntfs_initialize_rl_boot(void) +{ + int j; + /* Create runlist for $Boot. */ + g_rl_boot = ntfs_malloc(2 * sizeof(runlist)); + if (!g_rl_boot) + return FALSE; + + g_rl_boot[0].vcn = 0LL; + g_rl_boot[0].lcn = 0LL; + /* + * $Boot is always 8192 (0x2000) bytes or 1 cluster, whichever is + * bigger. + */ + j = (8192 + g_vol->cluster_size - 1) / g_vol->cluster_size; + g_rl_boot[1].vcn = j; + g_rl_boot[0].length = j; + g_rl_boot[1].lcn = -1LL; + g_rl_boot[1].length = 0LL; + /* Allocate clusters for $Boot. */ + return (bitmap_allocate(0,j)); +} + +/** + * mkntfs_initialize_rl_bad - + */ +static BOOL mkntfs_initialize_rl_bad(void) +{ + /* Create runlist for $BadClus, $DATA named stream $Bad. */ + g_rl_bad = ntfs_malloc(2 * sizeof(runlist)); + if (!g_rl_bad) + return FALSE; + + g_rl_bad[0].vcn = 0LL; + g_rl_bad[0].lcn = -1LL; + /* + * $BadClus named stream $Bad contains the whole volume as a single + * sparse runlist entry. + */ + g_rl_bad[1].vcn = g_vol->nr_clusters; + g_rl_bad[0].length = g_vol->nr_clusters; + g_rl_bad[1].lcn = -1LL; + g_rl_bad[1].length = 0LL; + + /* TODO: Mark bad blocks as such. */ + return TRUE; +} + +/** + * mkntfs_fill_device_with_zeroes - + */ +static BOOL mkntfs_fill_device_with_zeroes(void) +{ + /* + * If not quick format, fill the device with 0s. + * FIXME: Except bad blocks! (AIA) + */ + int i; + ssize_t bw; + unsigned long long position; + float progress_inc = (float)g_vol->nr_clusters / 100; + u64 volume_size; + + volume_size = g_vol->nr_clusters << g_vol->cluster_size_bits; + + ntfs_log_progress("Initializing device with zeroes: 0%%"); + for (position = 0; position < (unsigned long long)g_vol->nr_clusters; + position++) { + if (!(position % (int)(progress_inc+1))) { + ntfs_log_progress("\b\b\b\b%3.0f%%", position / + progress_inc); + } + bw = mkntfs_write(g_vol->dev, g_buf, g_vol->cluster_size); + if (bw != (ssize_t)g_vol->cluster_size) { + if (bw != -1 || errno != EIO) { + ntfs_log_error("This should not happen.\n"); + return FALSE; + } + if (!position) { + ntfs_log_error("Error: Cluster zero is bad. " + "Cannot create NTFS file " + "system.\n"); + return FALSE; + } + /* Add the baddie to our bad blocks list. */ + if (!append_to_bad_blocks(position)) + return FALSE; + ntfs_log_quiet("\nFound bad cluster (%lld). Adding to " + "list of bad blocks.\nInitializing " + "device with zeroes: %3.0f%%", position, + position / progress_inc); + /* Seek to next cluster. */ + g_vol->dev->d_ops->seek(g_vol->dev, + ((off_t)position + 1) * + g_vol->cluster_size, SEEK_SET); + } + } + ntfs_log_progress("\b\b\b\b100%%"); + position = (volume_size & (g_vol->cluster_size - 1)) / + opts.sector_size; + for (i = 0; (unsigned long)i < position; i++) { + bw = mkntfs_write(g_vol->dev, g_buf, opts.sector_size); + if (bw != opts.sector_size) { + if (bw != -1 || errno != EIO) { + ntfs_log_error("This should not happen.\n"); + return FALSE; + } else if (i + 1ull == position) { + ntfs_log_error("Error: Bad cluster found in " + "location reserved for system " + "file $Boot.\n"); + return FALSE; + } + /* Seek to next sector. */ + g_vol->dev->d_ops->seek(g_vol->dev, + opts.sector_size, SEEK_CUR); + } + } + ntfs_log_progress(" - Done.\n"); + return TRUE; +} + +/** + * mkntfs_sync_index_record + * + * (ERSO) made a function out of this, but the reason for doing that + * disappeared during coding.... + */ +static BOOL mkntfs_sync_index_record(INDEX_ALLOCATION* idx, MFT_RECORD* m, + ntfschar* name, u32 name_len) +{ + int i, err; + ntfs_attr_search_ctx *ctx; + ATTR_RECORD *a; + long long lw; + runlist *rl_index = NULL; + + i = 5 * sizeof(ntfschar); + ctx = ntfs_attr_get_search_ctx(NULL, m); + if (!ctx) { + ntfs_log_perror("Failed to allocate attribute search context"); + return FALSE; + } + /* FIXME: This should be IGNORE_CASE! */ + if (mkntfs_attr_lookup(AT_INDEX_ALLOCATION, name, name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + ntfs_attr_put_search_ctx(ctx); + ntfs_log_error("BUG: $INDEX_ALLOCATION attribute not found.\n"); + return FALSE; + } + a = ctx->attr; + rl_index = ntfs_mapping_pairs_decompress(g_vol, a, NULL); + if (!rl_index) { + ntfs_attr_put_search_ctx(ctx); + ntfs_log_error("Failed to decompress runlist of $INDEX_ALLOCATION " + "attribute.\n"); + return FALSE; + } + if (sle64_to_cpu(a->initialized_size) < i) { + ntfs_attr_put_search_ctx(ctx); + free(rl_index); + ntfs_log_error("BUG: $INDEX_ALLOCATION attribute too short.\n"); + return FALSE; + } + ntfs_attr_put_search_ctx(ctx); + i = sizeof(INDEX_BLOCK) - sizeof(INDEX_HEADER) + + le32_to_cpu(idx->index.allocated_size); + err = ntfs_mst_pre_write_fixup((NTFS_RECORD*)idx, i); + if (err) { + free(rl_index); + ntfs_log_error("ntfs_mst_pre_write_fixup() failed while " + "syncing index block.\n"); + return FALSE; + } + lw = ntfs_rlwrite(g_vol->dev, rl_index, (u8*)idx, i, NULL, + WRITE_STANDARD); + free(rl_index); + if (lw != i) { + ntfs_log_error("Error writing $INDEX_ALLOCATION.\n"); + return FALSE; + } + /* No more changes to @idx below here so no need for fixup: */ + /* ntfs_mst_post_write_fixup((NTFS_RECORD*)idx); */ + return TRUE; +} + +/** + * create_file_volume - + */ +static BOOL create_file_volume(MFT_RECORD *m, leMFT_REF root_ref, + VOLUME_FLAGS fl, const GUID *volume_guid) +{ + int i, err; + u8 *sd; + + ntfs_log_verbose("Creating $Volume (mft record 3)\n"); + m = (MFT_RECORD*)(g_buf + 3 * g_vol->mft_record_size); + err = create_hardlink(g_index_block, root_ref, m, + MK_LE_MREF(FILE_Volume, FILE_Volume), 0LL, 0LL, + FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0, + "$Volume", FILE_NAME_WIN32_AND_DOS); + if (!err) { + init_system_file_sd(FILE_Volume, &sd, &i); + err = add_attr_sd(m, sd, i); + } + if (!err) + err = add_attr_data(m, NULL, 0, CASE_SENSITIVE, + const_cpu_to_le16(0), NULL, 0); + if (!err) + err = add_attr_vol_name(m, g_vol->vol_name, g_vol->vol_name ? + strlen(g_vol->vol_name) : 0); + if (!err) { + if (fl & VOLUME_IS_DIRTY) + ntfs_log_quiet("Setting the volume dirty so check " + "disk runs on next reboot into " + "Windows.\n"); + err = add_attr_vol_info(m, fl, g_vol->major_ver, + g_vol->minor_ver); + } + if (!err && opts.with_uuid) + err = add_attr_object_id(m, volume_guid); + if (err < 0) { + ntfs_log_error("Couldn't create $Volume: %s\n", + strerror(-err)); + return FALSE; + } + return TRUE; +} + +/** + * create_backup_boot_sector + * + * Return 0 on success or -1 if it couldn't be created. + */ +static int create_backup_boot_sector(u8 *buff) +{ + const char *s; + ssize_t bw; + int size, e; + + ntfs_log_verbose("Creating backup boot sector.\n"); + /* + * Write the first max(512, opts.sector_size) bytes from buf to the + * last sector, but limit that to 8192 bytes of written data since that + * is how big $Boot is (and how big our buffer is).. + */ + size = 512; + if (size < opts.sector_size) + size = opts.sector_size; + if (g_vol->dev->d_ops->seek(g_vol->dev, (opts.num_sectors + 1) * + opts.sector_size - size, SEEK_SET) == (off_t)-1) { + ntfs_log_perror("Seek failed"); + goto bb_err; + } + if (size > 8192) + size = 8192; + bw = mkntfs_write(g_vol->dev, buff, size); + if (bw == size) + return 0; + e = errno; + if (bw == -1LL) + s = strerror(e); + else + s = "unknown error"; + /* At least some 2.4 kernels return EIO instead of ENOSPC. */ + if (bw != -1LL || (bw == -1LL && e != ENOSPC && e != EIO)) { + ntfs_log_critical("Couldn't write backup boot sector: %s\n", s); + return -1; + } +bb_err: + ntfs_log_error("Couldn't write backup boot sector. This is due to a " + "limitation in the\nLinux kernel. This is not a major " + "problem as Windows check disk will create the\n" + "backup boot sector when it is run on your next boot " + "into Windows.\n"); + return -1; +} + +/** + * mkntfs_create_root_structures - + */ +static BOOL mkntfs_create_root_structures(void) +{ + NTFS_BOOT_SECTOR *bs; + MFT_RECORD *m; + leMFT_REF root_ref; + leMFT_REF extend_ref; + int i; + int j; + int err; + u8 *sd; + FILE_ATTR_FLAGS extend_flags; + VOLUME_FLAGS volume_flags = const_cpu_to_le16(0); + int nr_sysfiles; + int buf_sds_first_size; + char *buf_sds; + GUID vol_guid; + + ntfs_log_quiet("Creating NTFS volume structures.\n"); + nr_sysfiles = 27; + /* + * Setup an empty mft record. Note, we can just give 0 as the mft + * reference as we are creating an NTFS 1.2 volume for which the mft + * reference is ignored by ntfs_mft_record_layout(). + * + * Copy the mft record onto all 16 records in the buffer and setup the + * sequence numbers of each system file to equal the mft record number + * of that file (only for $MFT is the sequence number 1 rather than 0). + */ + for (i = 0; i < nr_sysfiles; i++) { + if (ntfs_mft_record_layout(g_vol, 0, m = (MFT_RECORD *)(g_buf + + i * g_vol->mft_record_size))) { + ntfs_log_error("Failed to layout system mft records." + "\n"); + return FALSE; + } + if (i == 0 || i > 23) + m->sequence_number = cpu_to_le16(1); + else + m->sequence_number = cpu_to_le16(i); + } + /* + * If only one cluster contains all system files then + * fill the rest of it with empty, formatted records. + */ + if (nr_sysfiles * (s32)g_vol->mft_record_size < g_mft_size) { + for (i = nr_sysfiles; + i * (s32)g_vol->mft_record_size < g_mft_size; i++) { + m = (MFT_RECORD *)(g_buf + i * g_vol->mft_record_size); + if (ntfs_mft_record_layout(g_vol, 0, m)) { + ntfs_log_error("Failed to layout mft record." + "\n"); + return FALSE; + } + m->flags = cpu_to_le16(0); + m->sequence_number = cpu_to_le16(i); + } + } + /* + * Create the 16 system files, adding the system information attribute + * to each as well as marking them in use in the mft bitmap. + */ + for (i = 0; i < nr_sysfiles; i++) { + le32 file_attrs; + + m = (MFT_RECORD*)(g_buf + i * g_vol->mft_record_size); + if (i < 16 || i > 23) { + m->mft_record_number = cpu_to_le32(i); + m->flags |= MFT_RECORD_IN_USE; + ntfs_bit_set(g_mft_bitmap, 0LL + i, 1); + } + file_attrs = FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM; + if (i == FILE_root) { + file_attrs |= FILE_ATTR_ARCHIVE; + if (opts.disable_indexing) + file_attrs |= FILE_ATTR_NOT_CONTENT_INDEXED; + if (opts.enable_compression) + file_attrs |= FILE_ATTR_COMPRESSED; + } + /* setting specific security_id flag and */ + /* file permissions for ntfs 3.x */ + if (i == 0 || i == 1 || i == 2 || i == 6 || i == 8 || + i == 10) { + add_attr_std_info(m, file_attrs, + cpu_to_le32(0x0100)); + } else if (i == 9) { + file_attrs |= FILE_ATTR_VIEW_INDEX_PRESENT; + add_attr_std_info(m, file_attrs, + cpu_to_le32(0x0101)); + } else if (i == 11) { + add_attr_std_info(m, file_attrs, + cpu_to_le32(0x0101)); + } else if (i == 24 || i == 25 || i == 26) { + file_attrs |= FILE_ATTR_ARCHIVE; + file_attrs |= FILE_ATTR_VIEW_INDEX_PRESENT; + add_attr_std_info(m, file_attrs, + cpu_to_le32(0x0101)); + } else { + add_attr_std_info(m, file_attrs, + cpu_to_le32(0x00)); + } + } + /* The root directory mft reference. */ + root_ref = MK_LE_MREF(FILE_root, FILE_root); + extend_ref = MK_LE_MREF(11,11); + ntfs_log_verbose("Creating root directory (mft record 5)\n"); + m = (MFT_RECORD*)(g_buf + 5 * g_vol->mft_record_size); + m->flags |= MFT_RECORD_IS_DIRECTORY; + m->link_count = cpu_to_le16(le16_to_cpu(m->link_count) + 1); + err = add_attr_file_name(m, root_ref, 0LL, 0LL, + FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM | + FILE_ATTR_I30_INDEX_PRESENT, 0, 0, ".", + FILE_NAME_WIN32_AND_DOS); + if (!err) { + init_root_sd(&sd, &i); + err = add_attr_sd(m, sd, i); + } + /* FIXME: This should be IGNORE_CASE */ + if (!err) + err = add_attr_index_root(m, "$I30", 4, CASE_SENSITIVE, + AT_FILE_NAME, COLLATION_FILE_NAME, + g_vol->indx_record_size); + /* FIXME: This should be IGNORE_CASE */ + if (!err) + err = upgrade_to_large_index(m, "$I30", 4, CASE_SENSITIVE, + &g_index_block); + if (!err) { + ntfs_attr_search_ctx *ctx; + ATTR_RECORD *a; + ctx = ntfs_attr_get_search_ctx(NULL, m); + if (!ctx) { + ntfs_log_perror("Failed to allocate attribute search " + "context"); + return FALSE; + } + /* There is exactly one file name so this is ok. */ + if (mkntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + ntfs_attr_put_search_ctx(ctx); + ntfs_log_error("BUG: $FILE_NAME attribute not found." + "\n"); + return FALSE; + } + a = ctx->attr; + err = insert_file_link_in_dir_index(g_index_block, root_ref, + (FILE_NAME_ATTR*)((char*)a + + le16_to_cpu(a->value_offset)), + le32_to_cpu(a->value_length)); + ntfs_attr_put_search_ctx(ctx); + } + if (err) { + ntfs_log_error("Couldn't create root directory: %s\n", + strerror(-err)); + return FALSE; + } + /* Add all other attributes, on a per-file basis for clarity. */ + ntfs_log_verbose("Creating $MFT (mft record 0)\n"); + m = (MFT_RECORD*)g_buf; + err = add_attr_data_positioned(m, NULL, 0, CASE_SENSITIVE, + const_cpu_to_le16(0), g_rl_mft, g_buf, g_mft_size); + if (!err) + err = create_hardlink(g_index_block, root_ref, m, + MK_LE_MREF(FILE_MFT, 1), + ((g_mft_size - 1) + | (g_vol->cluster_size - 1)) + 1, + g_mft_size, FILE_ATTR_HIDDEN | + FILE_ATTR_SYSTEM, 0, 0, "$MFT", + FILE_NAME_WIN32_AND_DOS); + /* mft_bitmap is not modified in mkntfs; no need to sync it later. */ + if (!err) + err = add_attr_bitmap_positioned(m, NULL, 0, CASE_SENSITIVE, + g_rl_mft_bmp, + g_mft_bitmap, g_mft_bitmap_byte_size); + if (err < 0) { + ntfs_log_error("Couldn't create $MFT: %s\n", strerror(-err)); + return FALSE; + } + ntfs_log_verbose("Creating $MFTMirr (mft record 1)\n"); + m = (MFT_RECORD*)(g_buf + 1 * g_vol->mft_record_size); + err = add_attr_data_positioned(m, NULL, 0, CASE_SENSITIVE, + const_cpu_to_le16(0), g_rl_mftmirr, g_buf, + g_rl_mftmirr[0].length * g_vol->cluster_size); + if (!err) + err = create_hardlink(g_index_block, root_ref, m, + MK_LE_MREF(FILE_MFTMirr, FILE_MFTMirr), + g_rl_mftmirr[0].length * g_vol->cluster_size, + g_rl_mftmirr[0].length * g_vol->cluster_size, + FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0, + "$MFTMirr", FILE_NAME_WIN32_AND_DOS); + if (err < 0) { + ntfs_log_error("Couldn't create $MFTMirr: %s\n", + strerror(-err)); + return FALSE; + } + ntfs_log_verbose("Creating $LogFile (mft record 2)\n"); + m = (MFT_RECORD*)(g_buf + 2 * g_vol->mft_record_size); + err = add_attr_data_positioned(m, NULL, 0, CASE_SENSITIVE, + const_cpu_to_le16(0), g_rl_logfile, + (const u8*)NULL, g_logfile_size); + if (!err) + err = create_hardlink(g_index_block, root_ref, m, + MK_LE_MREF(FILE_LogFile, FILE_LogFile), + g_logfile_size, g_logfile_size, + FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0, + "$LogFile", FILE_NAME_WIN32_AND_DOS); + if (err < 0) { + ntfs_log_error("Couldn't create $LogFile: %s\n", + strerror(-err)); + return FALSE; + } + ntfs_log_verbose("Creating $AttrDef (mft record 4)\n"); + m = (MFT_RECORD*)(g_buf + 4 * g_vol->mft_record_size); + err = add_attr_data(m, NULL, 0, CASE_SENSITIVE, const_cpu_to_le16(0), + (u8*)g_vol->attrdef, g_vol->attrdef_len); + if (!err) + err = create_hardlink(g_index_block, root_ref, m, + MK_LE_MREF(FILE_AttrDef, FILE_AttrDef), + (g_vol->attrdef_len + g_vol->cluster_size - 1) & + ~(g_vol->cluster_size - 1), g_vol->attrdef_len, + FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0, + "$AttrDef", FILE_NAME_WIN32_AND_DOS); + if (!err) { + init_system_file_sd(FILE_AttrDef, &sd, &i); + err = add_attr_sd(m, sd, i); + } + if (err < 0) { + ntfs_log_error("Couldn't create $AttrDef: %s\n", + strerror(-err)); + return FALSE; + } + ntfs_log_verbose("Creating $Bitmap (mft record 6)\n"); + m = (MFT_RECORD*)(g_buf + 6 * g_vol->mft_record_size); + /* the data attribute of $Bitmap must be non-resident or otherwise */ + /* windows 2003 will regard the volume as corrupt (ERSO) */ + if (!err) + err = insert_non_resident_attr_in_mft_record(m, + AT_DATA, NULL, 0, CASE_SENSITIVE, + const_cpu_to_le16(0), (const u8*)NULL, + g_lcn_bitmap_byte_size, WRITE_BITMAP); + + + if (!err) + err = create_hardlink(g_index_block, root_ref, m, + MK_LE_MREF(FILE_Bitmap, FILE_Bitmap), + (g_lcn_bitmap_byte_size + g_vol->cluster_size - + 1) & ~(g_vol->cluster_size - 1), + g_lcn_bitmap_byte_size, + FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0, + "$Bitmap", FILE_NAME_WIN32_AND_DOS); + if (err < 0) { + ntfs_log_error("Couldn't create $Bitmap: %s\n", strerror(-err)); + return FALSE; + } + ntfs_log_verbose("Creating $Boot (mft record 7)\n"); + m = (MFT_RECORD*)(g_buf + 7 * g_vol->mft_record_size); + bs = ntfs_calloc(8192); + if (!bs) + return FALSE; + memcpy(bs, boot_array, sizeof(boot_array)); + /* + * Create the boot sector in bs. Note, that bs is already zeroed + * in the boot sector section and that it has the NTFS OEM id/magic + * already inserted, so no need to worry about these things. + */ + bs->bpb.bytes_per_sector = cpu_to_le16(opts.sector_size); + bs->bpb.sectors_per_cluster = (u8)(g_vol->cluster_size / + opts.sector_size); + bs->bpb.media_type = 0xf8; /* hard disk */ + bs->bpb.sectors_per_track = cpu_to_le16(opts.sectors_per_track); + ntfs_log_debug("sectors per track = %ld (0x%lx)\n", + opts.sectors_per_track, opts.sectors_per_track); + bs->bpb.heads = cpu_to_le16(opts.heads); + ntfs_log_debug("heads = %ld (0x%lx)\n", opts.heads, opts.heads); + bs->bpb.hidden_sectors = cpu_to_le32(opts.part_start_sect); + ntfs_log_debug("hidden sectors = %llu (0x%llx)\n", opts.part_start_sect, + opts.part_start_sect); + bs->physical_drive = 0x80; /* boot from hard disk */ + bs->extended_boot_signature = 0x80; /* everybody sets this, so we do */ + bs->number_of_sectors = cpu_to_sle64(opts.num_sectors); + bs->mft_lcn = cpu_to_sle64(g_mft_lcn); + bs->mftmirr_lcn = cpu_to_sle64(g_mftmirr_lcn); + if (g_vol->mft_record_size >= g_vol->cluster_size) { + bs->clusters_per_mft_record = g_vol->mft_record_size / + g_vol->cluster_size; + } else { + bs->clusters_per_mft_record = -(ffs(g_vol->mft_record_size) - + 1); + if ((u32)(1 << -bs->clusters_per_mft_record) != + g_vol->mft_record_size) { + free(bs); + ntfs_log_error("BUG: calculated clusters_per_mft_record" + " is wrong (= 0x%x)\n", + bs->clusters_per_mft_record); + return FALSE; + } + } + ntfs_log_debug("clusters per mft record = %i (0x%x)\n", + bs->clusters_per_mft_record, + bs->clusters_per_mft_record); + if (g_vol->indx_record_size >= g_vol->cluster_size) { + bs->clusters_per_index_record = g_vol->indx_record_size / + g_vol->cluster_size; + } else { + bs->clusters_per_index_record = -g_vol->indx_record_size_bits; + if ((1 << -bs->clusters_per_index_record) != + (s32)g_vol->indx_record_size) { + free(bs); + ntfs_log_error("BUG: calculated " + "clusters_per_index_record is wrong " + "(= 0x%x)\n", + bs->clusters_per_index_record); + return FALSE; + } + } + ntfs_log_debug("clusters per index block = %i (0x%x)\n", + bs->clusters_per_index_record, + bs->clusters_per_index_record); + /* Generate a 64-bit random number for the serial number. */ + bs->volume_serial_number = cpu_to_le64(((u64)random() << 32) | + ((u64)random() & 0xffffffff)); + /* + * Leave zero for now as NT4 leaves it zero, too. If want it later, see + * ../libntfs/bootsect.c for how to calculate it. + */ + bs->checksum = cpu_to_le32(0); + /* Make sure the bootsector is ok. */ + if (!ntfs_boot_sector_is_ntfs(bs)) { + free(bs); + ntfs_log_error("FATAL: Generated boot sector is invalid!\n"); + return FALSE; + } + err = add_attr_data_positioned(m, NULL, 0, CASE_SENSITIVE, + const_cpu_to_le16(0), g_rl_boot, (u8*)bs, 8192); + if (!err) + err = create_hardlink(g_index_block, root_ref, m, + MK_LE_MREF(FILE_Boot, FILE_Boot), + (8192 + g_vol->cluster_size - 1) & + ~(g_vol->cluster_size - 1), 8192, + FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0, + "$Boot", FILE_NAME_WIN32_AND_DOS); + if (!err) { + init_system_file_sd(FILE_Boot, &sd, &i); + err = add_attr_sd(m, sd, i); + } + if (err < 0) { + free(bs); + ntfs_log_error("Couldn't create $Boot: %s\n", strerror(-err)); + return FALSE; + } + if (create_backup_boot_sector((u8*)bs)) { + /* + * Pre-2.6 kernels couldn't access the last sector if it was + * odd and we failed to set the device block size to the sector + * size, hence we schedule chkdsk to create it. + */ + volume_flags |= VOLUME_IS_DIRTY; + } + free(bs); + /* + * We cheat a little here and if the user has requested all times to be + * set to zero then we set the GUID to zero as well. This options is + * only used for development purposes so that should be fine. + */ + if (!opts.use_epoch_time) { + /* Generate a GUID for the volume. */ +#ifdef ENABLE_UUID + uuid_generate((void*)&vol_guid); +#else + ntfs_generate_guid(&vol_guid); +#endif + } else + memset(&vol_guid, 0, sizeof(vol_guid)); + if (!create_file_volume(m, root_ref, volume_flags, &vol_guid)) + return FALSE; + ntfs_log_verbose("Creating $BadClus (mft record 8)\n"); + m = (MFT_RECORD*)(g_buf + 8 * g_vol->mft_record_size); + /* FIXME: This should be IGNORE_CASE */ + /* Create a sparse named stream of size equal to the volume size. */ + err = add_attr_data_positioned(m, "$Bad", 4, CASE_SENSITIVE, + const_cpu_to_le16(0), g_rl_bad, NULL, + g_vol->nr_clusters * g_vol->cluster_size); + if (!err) { + err = add_attr_data(m, NULL, 0, CASE_SENSITIVE, + const_cpu_to_le16(0), NULL, 0); + } + if (!err) { + err = create_hardlink(g_index_block, root_ref, m, + MK_LE_MREF(FILE_BadClus, FILE_BadClus), + 0LL, 0LL, FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, + 0, 0, "$BadClus", FILE_NAME_WIN32_AND_DOS); + } + if (err < 0) { + ntfs_log_error("Couldn't create $BadClus: %s\n", + strerror(-err)); + return FALSE; + } + /* create $Secure (NTFS 3.0+) */ + ntfs_log_verbose("Creating $Secure (mft record 9)\n"); + m = (MFT_RECORD*)(g_buf + 9 * g_vol->mft_record_size); + m->flags |= MFT_RECORD_IS_VIEW_INDEX; + if (!err) + err = create_hardlink(g_index_block, root_ref, m, + MK_LE_MREF(9, 9), 0LL, 0LL, + FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM | + FILE_ATTR_VIEW_INDEX_PRESENT, 0, 0, + "$Secure", FILE_NAME_WIN32_AND_DOS); + buf_sds = NULL; + buf_sds_first_size = 0; + if (!err) { + int buf_sds_size; + + buf_sds_first_size = 0xfc; + buf_sds_size = 0x40000 + buf_sds_first_size; + buf_sds = ntfs_calloc(buf_sds_size); + if (!buf_sds) + return FALSE; + init_secure_sds(buf_sds); + memcpy(buf_sds + 0x40000, buf_sds, buf_sds_first_size); + err = add_attr_data(m, "$SDS", 4, CASE_SENSITIVE, + const_cpu_to_le16(0), (u8*)buf_sds, + buf_sds_size); + } + /* FIXME: This should be IGNORE_CASE */ + if (!err) + err = add_attr_index_root(m, "$SDH", 4, CASE_SENSITIVE, + AT_UNUSED, COLLATION_NTOFS_SECURITY_HASH, + g_vol->indx_record_size); + /* FIXME: This should be IGNORE_CASE */ + if (!err) + err = add_attr_index_root(m, "$SII", 4, CASE_SENSITIVE, + AT_UNUSED, COLLATION_NTOFS_ULONG, + g_vol->indx_record_size); + if (!err) + err = initialize_secure(buf_sds, buf_sds_first_size, m); + free(buf_sds); + if (err < 0) { + ntfs_log_error("Couldn't create $Secure: %s\n", + strerror(-err)); + return FALSE; + } + ntfs_log_verbose("Creating $UpCase (mft record 0xa)\n"); + m = (MFT_RECORD*)(g_buf + 0xa * g_vol->mft_record_size); + err = add_attr_data(m, NULL, 0, CASE_SENSITIVE, const_cpu_to_le16(0), + (u8*)g_vol->upcase, g_vol->upcase_len << 1); + if (!err) + err = create_hardlink(g_index_block, root_ref, m, + MK_LE_MREF(FILE_UpCase, FILE_UpCase), + ((g_vol->upcase_len << 1) + + g_vol->cluster_size - 1) & + ~(g_vol->cluster_size - 1), + g_vol->upcase_len << 1, + FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0, + "$UpCase", FILE_NAME_WIN32_AND_DOS); + if (err < 0) { + ntfs_log_error("Couldn't create $UpCase: %s\n", strerror(-err)); + return FALSE; + } + ntfs_log_verbose("Creating $Extend (mft record 11)\n"); + /* + * $Extend index must be resident. Otherwise, w2k3 will regard the + * volume as corrupt. (ERSO) + */ + m = (MFT_RECORD*)(g_buf + 11 * g_vol->mft_record_size); + m->flags |= MFT_RECORD_IS_DIRECTORY; + if (!err) + err = create_hardlink(g_index_block, root_ref, m, + MK_LE_MREF(11, 11), 0LL, 0LL, + FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM | + FILE_ATTR_I30_INDEX_PRESENT, 0, 0, + "$Extend", FILE_NAME_WIN32_AND_DOS); + /* FIXME: This should be IGNORE_CASE */ + if (!err) + err = add_attr_index_root(m, "$I30", 4, CASE_SENSITIVE, + AT_FILE_NAME, COLLATION_FILE_NAME, + g_vol->indx_record_size); + if (err < 0) { + ntfs_log_error("Couldn't create $Extend: %s\n", + strerror(-err)); + return FALSE; + } + /* NTFS reserved system files (mft records 0xc-0xf) */ + for (i = 0xc; i < 0x10; i++) { + ntfs_log_verbose("Creating system file (mft record 0x%x)\n", i); + m = (MFT_RECORD*)(g_buf + i * g_vol->mft_record_size); + err = add_attr_data(m, NULL, 0, CASE_SENSITIVE, + const_cpu_to_le16(0), NULL, 0); + if (!err) { + init_system_file_sd(i, &sd, &j); + err = add_attr_sd(m, sd, j); + } + if (err < 0) { + ntfs_log_error("Couldn't create system file %i (0x%x): " + "%s\n", i, i, strerror(-err)); + return FALSE; + } + } + /* create systemfiles for ntfs volumes (3.1) */ + /* starting with file 24 (ignoring file 16-23) */ + extend_flags = FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM | + FILE_ATTR_ARCHIVE | FILE_ATTR_VIEW_INDEX_PRESENT; + ntfs_log_verbose("Creating $Quota (mft record 24)\n"); + m = (MFT_RECORD*)(g_buf + 24 * g_vol->mft_record_size); + m->flags |= MFT_RECORD_IS_4; + m->flags |= MFT_RECORD_IS_VIEW_INDEX; + if (!err) + err = create_hardlink_res((MFT_RECORD*)(g_buf + + 11 * g_vol->mft_record_size), extend_ref, m, + MK_LE_MREF(24, 1), 0LL, 0LL, extend_flags, + 0, 0, "$Quota", FILE_NAME_WIN32_AND_DOS); + /* FIXME: This should be IGNORE_CASE */ + if (!err) + err = add_attr_index_root(m, "$Q", 2, CASE_SENSITIVE, AT_UNUSED, + COLLATION_NTOFS_ULONG, g_vol->indx_record_size); + /* FIXME: This should be IGNORE_CASE */ + if (!err) + err = add_attr_index_root(m, "$O", 2, CASE_SENSITIVE, AT_UNUSED, + COLLATION_NTOFS_SID, g_vol->indx_record_size); + if (!err) + err = initialize_quota(m); + if (err < 0) { + ntfs_log_error("Couldn't create $Quota: %s\n", strerror(-err)); + return FALSE; + } + ntfs_log_verbose("Creating $ObjId (mft record 25)\n"); + m = (MFT_RECORD*)(g_buf + 25 * g_vol->mft_record_size); + m->flags |= MFT_RECORD_IS_4; + m->flags |= MFT_RECORD_IS_VIEW_INDEX; + if (!err) + err = create_hardlink_res((MFT_RECORD*)(g_buf + + 11 * g_vol->mft_record_size), extend_ref, + m, MK_LE_MREF(25, 1), 0LL, 0LL, + extend_flags, 0, 0, "$ObjId", + FILE_NAME_WIN32_AND_DOS); + + /* FIXME: This should be IGNORE_CASE */ + if (!err) + err = add_attr_index_root(m, "$O", 2, CASE_SENSITIVE, AT_UNUSED, + COLLATION_NTOFS_ULONGS, + g_vol->indx_record_size); + if (!err && opts.with_uuid) + err = index_obj_id_insert(m, &vol_guid, + MK_LE_MREF(FILE_Volume, FILE_Volume)); + if (err < 0) { + ntfs_log_error("Couldn't create $ObjId: %s\n", + strerror(-err)); + return FALSE; + } + ntfs_log_verbose("Creating $Reparse (mft record 26)\n"); + m = (MFT_RECORD*)(g_buf + 26 * g_vol->mft_record_size); + m->flags |= MFT_RECORD_IS_4; + m->flags |= MFT_RECORD_IS_VIEW_INDEX; + if (!err) + err = create_hardlink_res((MFT_RECORD*)(g_buf + + 11 * g_vol->mft_record_size), + extend_ref, m, MK_LE_MREF(26, 1), + 0LL, 0LL, extend_flags, 0, 0, + "$Reparse", FILE_NAME_WIN32_AND_DOS); + /* FIXME: This should be IGNORE_CASE */ + if (!err) + err = add_attr_index_root(m, "$R", 2, CASE_SENSITIVE, AT_UNUSED, + COLLATION_NTOFS_ULONGS, g_vol->indx_record_size); + if (err < 0) { + ntfs_log_error("Couldn't create $Reparse: %s\n", + strerror(-err)); + return FALSE; + } + return TRUE; +} + +/** + * mkntfs_redirect + */ +static int mkntfs_redirect(struct mkntfs_options *opts2) +{ + int result = 1; + ntfs_attr_search_ctx *ctx = NULL; + long long lw, pos; + ATTR_RECORD *a; + MFT_RECORD *m; + int i, err; + + if (!opts2) { + ntfs_log_error("Internal error: invalid parameters to mkntfs_options.\n"); + goto done; + } + /* Initialize the random number generator with the current time. */ + srandom(le64_to_cpu(mkntfs_time())/10000000); + /* Allocate and initialize ntfs_volume structure g_vol. */ + g_vol = ntfs_volume_alloc(); + if (!g_vol) { + ntfs_log_perror("Could not create volume"); + goto done; + } + /* Create NTFS 3.1 (Windows XP/Vista) volumes. */ + g_vol->major_ver = 3; + g_vol->minor_ver = 1; + /* Transfer some options to the volume. */ + if (opts.label) { + g_vol->vol_name = strdup(opts.label); + if (!g_vol->vol_name) { + ntfs_log_perror("Could not copy volume name"); + goto done; + } + } + if (opts.cluster_size >= 0) + g_vol->cluster_size = opts.cluster_size; + /* Length is in unicode characters. */ + g_vol->upcase_len = 65536; + g_vol->upcase = ntfs_malloc(g_vol->upcase_len * sizeof(ntfschar)); + if (!g_vol->upcase) + goto done; + ntfs_upcase_table_build(g_vol->upcase, + g_vol->upcase_len * sizeof(ntfschar)); + g_vol->attrdef = ntfs_malloc(sizeof(attrdef_ntfs3x_array)); + if (!g_vol->attrdef) { + ntfs_log_perror("Could not create attrdef structure"); + goto done; + } + memcpy(g_vol->attrdef, attrdef_ntfs3x_array, + sizeof(attrdef_ntfs3x_array)); + g_vol->attrdef_len = sizeof(attrdef_ntfs3x_array); + /* Open the partition. */ + if (!mkntfs_open_partition(g_vol)) + goto done; + /* + * Decide on the sector size, cluster size, mft record and index record + * sizes as well as the number of sectors/tracks/heads/size, etc. + */ + if (!mkntfs_override_vol_params(g_vol)) + goto done; + /* Initialize $Bitmap and $MFT/$BITMAP related stuff. */ + if (!mkntfs_initialize_bitmaps()) + goto done; + /* Initialize MFT & set g_logfile_lcn. */ + if (!mkntfs_initialize_rl_mft()) + goto done; + /* Initialize $LogFile. */ + if (!mkntfs_initialize_rl_logfile()) + goto done; + /* Initialize $Boot. */ + if (!mkntfs_initialize_rl_boot()) + goto done; + /* Allocate a buffer large enough to hold the mft. */ + g_buf = ntfs_calloc(g_mft_size); + if (!g_buf) + goto done; + /* Create runlist for $BadClus, $DATA named stream $Bad. */ + if (!mkntfs_initialize_rl_bad()) + goto done; + /* If not quick format, fill the device with 0s. */ + if (!opts.quick_format) { + if (!mkntfs_fill_device_with_zeroes()) + goto done; + } + /* Create NTFS volume structures. */ + if (!mkntfs_create_root_structures()) + goto done; + /* + * - Do not step onto bad blocks!!! + * - If any bad blocks were specified or found, modify $BadClus, + * allocating the bad clusters in $Bitmap. + * - C&w bootsector backup bootsector (backup in last sector of the + * partition). + * - If NTFS 3.0+, c&w $Secure file and $Extend directory with the + * corresponding special files in it, i.e. $ObjId, $Quota, $Reparse, + * and $UsnJrnl. And others? Or not all necessary? + * - RE: Populate $root with the system files (and $Extend directory if + * applicable). Possibly should move this as far to the top as + * possible and update during each subsequent c&w of each system file. + */ + ntfs_log_verbose("Syncing root directory index record.\n"); + if (!mkntfs_sync_index_record(g_index_block, (MFT_RECORD*)(g_buf + 5 * + g_vol->mft_record_size), NTFS_INDEX_I30, 4)) + goto done; + + ntfs_log_verbose("Syncing $Bitmap.\n"); + m = (MFT_RECORD*)(g_buf + 6 * g_vol->mft_record_size); + + ctx = ntfs_attr_get_search_ctx(NULL, m); + if (!ctx) { + ntfs_log_perror("Could not create an attribute search context"); + goto done; + } + + if (mkntfs_attr_lookup(AT_DATA, AT_UNNAMED, 0, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + ntfs_log_error("BUG: $DATA attribute not found.\n"); + goto done; + } + + a = ctx->attr; + if (a->non_resident) { + runlist *rl = ntfs_mapping_pairs_decompress(g_vol, a, NULL); + if (!rl) { + ntfs_log_error("ntfs_mapping_pairs_decompress() failed\n"); + goto done; + } + lw = ntfs_rlwrite(g_vol->dev, rl, (const u8*)NULL, + g_lcn_bitmap_byte_size, NULL, WRITE_BITMAP); + err = errno; + free(rl); + if (lw != g_lcn_bitmap_byte_size) { + ntfs_log_error("ntfs_rlwrite: %s\n", lw == -1 ? + strerror(err) : "unknown error"); + goto done; + } + } else { + /* Error : the bitmap must be created non resident */ + ntfs_log_error("Error : the global bitmap is resident\n"); + goto done; + } + + /* + * No need to sync $MFT/$BITMAP as that has never been modified since + * its creation. + */ + ntfs_log_verbose("Syncing $MFT.\n"); + pos = g_mft_lcn * g_vol->cluster_size; + lw = 1; + for (i = 0; i < g_mft_size / (s32)g_vol->mft_record_size; i++) { + if (!opts.no_action) + lw = ntfs_mst_pwrite(g_vol->dev, pos, 1, g_vol->mft_record_size, g_buf + i * g_vol->mft_record_size); + if (lw != 1) { + ntfs_log_error("ntfs_mst_pwrite: %s\n", lw == -1 ? + strerror(errno) : "unknown error"); + goto done; + } + pos += g_vol->mft_record_size; + } + ntfs_log_verbose("Updating $MFTMirr.\n"); + pos = g_mftmirr_lcn * g_vol->cluster_size; + lw = 1; + for (i = 0; i < g_rl_mftmirr[0].length * g_vol->cluster_size / g_vol->mft_record_size; i++) { + m = (MFT_RECORD*)(g_buf + i * g_vol->mft_record_size); + /* + * Decrement the usn by one, so it becomes the same as the one + * in $MFT once it is mst protected. - This is as we need the + * $MFTMirr to have the exact same byte by byte content as + * $MFT, rather than just equivalent meaning content. + */ + if (ntfs_mft_usn_dec(m)) { + ntfs_log_error("ntfs_mft_usn_dec"); + goto done; + } + if (!opts.no_action) + lw = ntfs_mst_pwrite(g_vol->dev, pos, 1, g_vol->mft_record_size, g_buf + i * g_vol->mft_record_size); + if (lw != 1) { + ntfs_log_error("ntfs_mst_pwrite: %s\n", lw == -1 ? + strerror(errno) : "unknown error"); + goto done; + } + pos += g_vol->mft_record_size; + } + ntfs_log_verbose("Syncing device.\n"); + if (g_vol->dev->d_ops->sync(g_vol->dev)) { + ntfs_log_error("Syncing device. FAILED"); + goto done; + } + ntfs_log_quiet("mkntfs completed successfully. Have a nice day.\n"); + result = 0; +done: + ntfs_attr_put_search_ctx(ctx); + mkntfs_cleanup(); /* Device is unlocked and closed here */ + return result; +} + +/** + * mkntfs_main + */ +int mkntfs_main(const char *devpath, const char *label) +{ + mkntfs_init_options(&opts); + + opts.dev_name = (char*)devpath; + opts.label = (char*)label; + + opts.force = TRUE; + opts.quick_format = TRUE; + + return mkntfs_redirect(&opts); +} diff --git a/src/add-ons/kernel/file_systems/ntfs/utils/sd.c b/src/add-ons/kernel/file_systems/ntfs/utils/sd.c new file mode 100644 index 0000000000..4e3af97801 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ntfs/utils/sd.c @@ -0,0 +1,607 @@ +#include "types.h" +#include "layout.h" +#include "sd.h" + +/** + * init_system_file_sd - + * + * NTFS 3.1 - System files security decriptors + * ===================================================== + * + * Create the security descriptor for system file number @sys_file_no and + * return a pointer to the descriptor. + * + * Note the root directory system file (".") is very different and handled by a + * different function. + * + * The sd is returned in *@sd_val and has length *@sd_val_len. + * + * Do NOT free *@sd_val as it is static memory. This also means that you can + * only use *@sd_val until the next call to this function. + */ +void init_system_file_sd(int sys_file_no, u8 **sd_val, int *sd_val_len) +{ + static u8 sd_array[0x68]; + SECURITY_DESCRIPTOR_RELATIVE *sd; + ACL *acl; + ACCESS_ALLOWED_ACE *aa_ace; + SID *sid; + le32 *sub_authorities; + + if (sys_file_no < 0) { + *sd_val = NULL; + *sd_val_len = 0; + return; + } + *sd_val = sd_array; + sd = (SECURITY_DESCRIPTOR_RELATIVE*)&sd_array; + sd->revision = 1; + sd->alignment = 0; + sd->control = SE_SELF_RELATIVE | SE_DACL_PRESENT; + *sd_val_len = 0x64; + sd->owner = const_cpu_to_le32(0x48); + sd->group = const_cpu_to_le32(0x54); + sd->sacl = const_cpu_to_le32(0); + sd->dacl = const_cpu_to_le32(0x14); + /* + * Now at offset 0x14, as specified in the security descriptor, we have + * the DACL. + */ + acl = (ACL*)((char*)sd + le32_to_cpu(sd->dacl)); + acl->revision = 2; + acl->alignment1 = 0; + acl->size = const_cpu_to_le16(0x34); + acl->ace_count = const_cpu_to_le16(2); + acl->alignment2 = const_cpu_to_le16(0); + /* + * Now at offset 0x1c, just after the DACL's ACL, we have the first + * ACE of the DACL. The type of the ACE is access allowed. + */ + aa_ace = (ACCESS_ALLOWED_ACE*)((char*)acl + sizeof(ACL)); + aa_ace->type = ACCESS_ALLOWED_ACE_TYPE; + aa_ace->flags = 0; + aa_ace->size = const_cpu_to_le16(0x14); + switch (sys_file_no) { + case FILE_AttrDef: + case FILE_Boot: + aa_ace->mask = SYNCHRONIZE | STANDARD_RIGHTS_READ | + FILE_READ_ATTRIBUTES | FILE_READ_EA | FILE_READ_DATA; + break; + default: + aa_ace->mask = SYNCHRONIZE | STANDARD_RIGHTS_WRITE | + FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | + FILE_WRITE_EA | FILE_READ_EA | FILE_APPEND_DATA | + FILE_WRITE_DATA | FILE_READ_DATA; + break; + } + aa_ace->sid.revision = 1; + aa_ace->sid.sub_authority_count = 1; + aa_ace->sid.identifier_authority.value[0] = 0; + aa_ace->sid.identifier_authority.value[1] = 0; + aa_ace->sid.identifier_authority.value[2] = 0; + aa_ace->sid.identifier_authority.value[3] = 0; + aa_ace->sid.identifier_authority.value[4] = 0; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + aa_ace->sid.identifier_authority.value[5] = 5; + aa_ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); + /* + * Now at offset 0x30 within security descriptor, just after the first + * ACE of the DACL. All system files, except the root directory, have + * a second ACE. + */ + /* The second ACE of the DACL. Type is access allowed. */ + aa_ace = (ACCESS_ALLOWED_ACE*)((char*)aa_ace + + le16_to_cpu(aa_ace->size)); + aa_ace->type = ACCESS_ALLOWED_ACE_TYPE; + aa_ace->flags = 0; + aa_ace->size = const_cpu_to_le16(0x18); + /* Only $AttrDef and $Boot behave differently to everything else. */ + switch (sys_file_no) { + case FILE_AttrDef: + case FILE_Boot: + aa_ace->mask = SYNCHRONIZE | STANDARD_RIGHTS_READ | + FILE_READ_ATTRIBUTES | FILE_READ_EA | + FILE_READ_DATA; + break; + default: + aa_ace->mask = SYNCHRONIZE | STANDARD_RIGHTS_READ | + FILE_WRITE_ATTRIBUTES | + FILE_READ_ATTRIBUTES | FILE_WRITE_EA | + FILE_READ_EA | FILE_APPEND_DATA | + FILE_WRITE_DATA | FILE_READ_DATA; + break; + } + aa_ace->sid.revision = 1; + aa_ace->sid.sub_authority_count = 2; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + aa_ace->sid.identifier_authority.value[0] = 0; + aa_ace->sid.identifier_authority.value[1] = 0; + aa_ace->sid.identifier_authority.value[2] = 0; + aa_ace->sid.identifier_authority.value[3] = 0; + aa_ace->sid.identifier_authority.value[4] = 0; + aa_ace->sid.identifier_authority.value[5] = 5; + sub_authorities = aa_ace->sid.sub_authority; + *sub_authorities++ = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + *sub_authorities = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + /* + * Now at offset 0x48 into the security descriptor, as specified in the + * security descriptor, we now have the owner SID. + */ + sid = (SID*)((char*)sd + le32_to_cpu(sd->owner)); + sid->revision = 1; + sid->sub_authority_count = 1; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + sid->identifier_authority.value[0] = 0; + sid->identifier_authority.value[1] = 0; + sid->identifier_authority.value[2] = 0; + sid->identifier_authority.value[3] = 0; + sid->identifier_authority.value[4] = 0; + sid->identifier_authority.value[5] = 5; + sid->sub_authority[0] = const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); + /* + * Now at offset 0x54 into the security descriptor, as specified in the + * security descriptor, we have the group SID. + */ + sid = (SID*)((char*)sd + le32_to_cpu(sd->group)); + sid->revision = 1; + sid->sub_authority_count = 2; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + sid->identifier_authority.value[0] = 0; + sid->identifier_authority.value[1] = 0; + sid->identifier_authority.value[2] = 0; + sid->identifier_authority.value[3] = 0; + sid->identifier_authority.value[4] = 0; + sid->identifier_authority.value[5] = 5; + sub_authorities = sid->sub_authority; + *sub_authorities++ = const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + *sub_authorities = const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); +} + +/** + * init_root_sd - + * + * Creates the security_descriptor for the root folder on ntfs 3.1 as created + * by Windows Vista (when the format is done from the disk management MMC + * snap-in, note this is different from the format done from the disk + * properties in Windows Explorer). + */ +void init_root_sd(u8 **sd_val, int *sd_val_len) +{ + SECURITY_DESCRIPTOR_RELATIVE *sd; + ACL *acl; + ACCESS_ALLOWED_ACE *ace; + SID *sid; + le32 *sub_authorities; + + static char sd_array[0x102c]; + *sd_val_len = 0x102c; + *sd_val = (u8*)&sd_array; + + //security descriptor relative + sd = (SECURITY_DESCRIPTOR_RELATIVE*)sd_array; + sd->revision = SECURITY_DESCRIPTOR_REVISION; + sd->alignment = 0; + sd->control = SE_SELF_RELATIVE | SE_DACL_PRESENT; + sd->owner = const_cpu_to_le32(0x1014); + sd->group = const_cpu_to_le32(0x1020); + sd->sacl = 0; + sd->dacl = const_cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE)); + + //acl + acl = (ACL*)((u8*)sd + sizeof(SECURITY_DESCRIPTOR_RELATIVE)); + acl->revision = ACL_REVISION; + acl->alignment1 = 0; + acl->size = const_cpu_to_le16(0x1000); + acl->ace_count = const_cpu_to_le16(0x08); + acl->alignment2 = 0; + + //ace1 + ace = (ACCESS_ALLOWED_ACE*)((u8*)acl + sizeof(ACL)); + ace->type = ACCESS_ALLOWED_ACE_TYPE; + ace->flags = 0; + ace->size = const_cpu_to_le16(0x18); + ace->mask = STANDARD_RIGHTS_ALL | FILE_WRITE_ATTRIBUTES | + FILE_LIST_DIRECTORY | FILE_WRITE_DATA | + FILE_ADD_SUBDIRECTORY | FILE_READ_EA | FILE_WRITE_EA | + FILE_TRAVERSE | FILE_DELETE_CHILD | + FILE_READ_ATTRIBUTES; + ace->sid.revision = SID_REVISION; + ace->sid.sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + sub_authorities = ace->sid.sub_authority; + *sub_authorities++ = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + *sub_authorities = const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + + //ace2 + ace = (ACCESS_ALLOWED_ACE*)((u8*)ace + le16_to_cpu(ace->size)); + ace->type = ACCESS_ALLOWED_ACE_TYPE; + ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | + INHERIT_ONLY_ACE; + ace->size = const_cpu_to_le16(0x18); + ace->mask = GENERIC_ALL; + ace->sid.revision = SID_REVISION; + ace->sid.sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + sub_authorities = ace->sid.sub_authority; + *sub_authorities++ = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + *sub_authorities = const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + + //ace3 + ace = (ACCESS_ALLOWED_ACE*)((u8*)ace + le16_to_cpu(ace->size)); + ace->type = ACCESS_ALLOWED_ACE_TYPE; + ace->flags = 0; + ace->size = const_cpu_to_le16(0x14); + ace->mask = STANDARD_RIGHTS_ALL | FILE_WRITE_ATTRIBUTES | + FILE_LIST_DIRECTORY | FILE_WRITE_DATA | + FILE_ADD_SUBDIRECTORY | FILE_READ_EA | FILE_WRITE_EA | + FILE_TRAVERSE | FILE_DELETE_CHILD | + FILE_READ_ATTRIBUTES; + ace->sid.revision = SID_REVISION; + ace->sid.sub_authority_count = 0x01; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); + + //ace4 + ace = (ACCESS_ALLOWED_ACE*)((u8*)ace + le16_to_cpu(ace->size)); + ace->type = ACCESS_ALLOWED_ACE_TYPE; + ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | + INHERIT_ONLY_ACE; + ace->size = const_cpu_to_le16(0x14); + ace->mask = GENERIC_ALL; + ace->sid.revision = SID_REVISION; + ace->sid.sub_authority_count = 0x01; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); + + //ace5 + ace = (ACCESS_ALLOWED_ACE*)((char*)ace + le16_to_cpu(ace->size)); + ace->type = ACCESS_ALLOWED_ACE_TYPE; + ace->flags = 0; + ace->size = const_cpu_to_le16(0x14); + ace->mask = SYNCHRONIZE | READ_CONTROL | DELETE | + FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | + FILE_TRAVERSE | FILE_WRITE_EA | FILE_READ_EA | + FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE | + FILE_LIST_DIRECTORY; + ace->sid.revision = SID_REVISION; + ace->sid.sub_authority_count = 0x01; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_AUTHENTICATED_USER_RID); + + //ace6 + ace = (ACCESS_ALLOWED_ACE*)((u8*)ace + le16_to_cpu(ace->size)); + ace->type = ACCESS_ALLOWED_ACE_TYPE; + ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | + INHERIT_ONLY_ACE; + ace->size = const_cpu_to_le16(0x14); + ace->mask = GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | DELETE; + ace->sid.revision = SID_REVISION; + ace->sid.sub_authority_count = 0x01; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_AUTHENTICATED_USER_RID); + + //ace7 + ace = (ACCESS_ALLOWED_ACE*)((u8*)ace + le16_to_cpu(ace->size)); + ace->type = ACCESS_ALLOWED_ACE_TYPE; + ace->flags = 0; + ace->size = const_cpu_to_le16(0x18); + ace->mask = SYNCHRONIZE | READ_CONTROL | FILE_READ_ATTRIBUTES | + FILE_TRAVERSE | FILE_READ_EA | FILE_LIST_DIRECTORY; + ace->sid.revision = SID_REVISION; + ace->sid.sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + sub_authorities = ace->sid.sub_authority; + *sub_authorities++ = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + *sub_authorities = const_cpu_to_le32(DOMAIN_ALIAS_RID_USERS); + + //ace8 + ace = (ACCESS_ALLOWED_ACE*)((u8*)ace + le16_to_cpu(ace->size)); + ace->type = ACCESS_ALLOWED_ACE_TYPE; + ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | + INHERIT_ONLY_ACE; + ace->size = const_cpu_to_le16(0x18); + ace->mask = GENERIC_READ | GENERIC_EXECUTE; + ace->sid.revision = SID_REVISION; + ace->sid.sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + sub_authorities = ace->sid.sub_authority; + *sub_authorities++ = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + *sub_authorities = const_cpu_to_le32(DOMAIN_ALIAS_RID_USERS); + + //owner sid + sid = (SID*)((char*)sd + le32_to_cpu(sd->owner)); + sid->revision = 0x01; + sid->sub_authority_count = 0x01; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + sid->identifier_authority.value[0] = 0; + sid->identifier_authority.value[1] = 0; + sid->identifier_authority.value[2] = 0; + sid->identifier_authority.value[3] = 0; + sid->identifier_authority.value[4] = 0; + sid->identifier_authority.value[5] = 5; + sid->sub_authority[0] = const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); + + //group sid + sid = (SID*)((char*)sd + le32_to_cpu(sd->group)); + sid->revision = 0x01; + sid->sub_authority_count = 0x01; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + sid->identifier_authority.value[0] = 0; + sid->identifier_authority.value[1] = 0; + sid->identifier_authority.value[2] = 0; + sid->identifier_authority.value[3] = 0; + sid->identifier_authority.value[4] = 0; + sid->identifier_authority.value[5] = 5; + sid->sub_authority[0] = const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); +} + +/** + * init_secure_sds - + * + * NTFS 3.1 - System files security decriptors + * =========================================== + * Create the security descriptor entries in $SDS data stream like they + * are in a partition, newly formatted with windows 2003 + */ +void init_secure_sds(char *sd_val) +{ + SECURITY_DESCRIPTOR_HEADER *sds; + SECURITY_DESCRIPTOR_RELATIVE *sd; + ACL *acl; + ACCESS_ALLOWED_ACE *ace; + SID *sid; + +/* + * security descriptor #1 + */ + //header + sds = (SECURITY_DESCRIPTOR_HEADER*)((char*)sd_val); + sds->hash = const_cpu_to_le32(0xF80312F0); + sds->security_id = const_cpu_to_le32(0x0100); + sds->offset = const_cpu_to_le64(0x00); + sds->length = const_cpu_to_le32(0x7C); + //security descriptor relative + sd = (SECURITY_DESCRIPTOR_RELATIVE*)((char*)sds + + sizeof(SECURITY_DESCRIPTOR_HEADER)); + sd->revision = 0x01; + sd->alignment = 0x00; + sd->control = SE_SELF_RELATIVE | SE_DACL_PRESENT; + sd->owner = const_cpu_to_le32(0x48); + sd->group = const_cpu_to_le32(0x58); + sd->sacl = const_cpu_to_le32(0x00); + sd->dacl = const_cpu_to_le32(0x14); + + //acl + acl = (ACL*)((char*)sd + sizeof(SECURITY_DESCRIPTOR_RELATIVE)); + acl->revision = 0x02; + acl->alignment1 = 0x00; + acl->size = const_cpu_to_le16(0x34); + acl->ace_count = const_cpu_to_le16(0x02); + acl->alignment2 = 0x00; + + //ace1 + ace = (ACCESS_ALLOWED_ACE*)((char*)acl + sizeof(ACL)); + ace->type = 0x00; + ace->flags = 0x00; + ace->size = const_cpu_to_le16(0x14); + ace->mask = const_cpu_to_le32(0x120089); + ace->sid.revision = 0x01; + ace->sid.sub_authority_count = 0x01; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); + //ace2 + ace = (ACCESS_ALLOWED_ACE*)((char*)ace + le16_to_cpu(ace->size)); + ace->type = 0x00; + ace->flags = 0x00; + ace->size = const_cpu_to_le16(0x18); + ace->mask = const_cpu_to_le32(0x120089); + ace->sid.revision = 0x01; + ace->sid.sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + ace->sid.sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + + //owner sid + sid = (SID*)((char*)sd + le32_to_cpu(sd->owner)); + sid->revision = 0x01; + sid->sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + sid->identifier_authority.value[0] = 0; + sid->identifier_authority.value[1] = 0; + sid->identifier_authority.value[2] = 0; + sid->identifier_authority.value[3] = 0; + sid->identifier_authority.value[4] = 0; + sid->identifier_authority.value[5] = 5; + sid->sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + sid->sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + //group sid + sid = (SID*)((char*)sd + le32_to_cpu(sd->group)); + sid->revision = 0x01; + sid->sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + sid->identifier_authority.value[0] = 0; + sid->identifier_authority.value[1] = 0; + sid->identifier_authority.value[2] = 0; + sid->identifier_authority.value[3] = 0; + sid->identifier_authority.value[4] = 0; + sid->identifier_authority.value[5] = 5; + sid->sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + sid->sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); +/* + * security descriptor #2 + */ + //header + sds = (SECURITY_DESCRIPTOR_HEADER*)((char*)sd_val + 0x80); + sds->hash = const_cpu_to_le32(0xB32451); + sds->security_id = const_cpu_to_le32(0x0101); + sds->offset = const_cpu_to_le64(0x80); + sds->length = const_cpu_to_le32(0x7C); + + //security descriptor relative + sd = (SECURITY_DESCRIPTOR_RELATIVE*)((char*)sds + + sizeof(SECURITY_DESCRIPTOR_HEADER)); + sd->revision = 0x01; + sd->alignment = 0x00; + sd->control = SE_SELF_RELATIVE | SE_DACL_PRESENT; + sd->owner = const_cpu_to_le32(0x48); + sd->group = const_cpu_to_le32(0x58); + sd->sacl = const_cpu_to_le32(0x00); + sd->dacl = const_cpu_to_le32(0x14); + + //acl + acl = (ACL*)((char*)sd + sizeof(SECURITY_DESCRIPTOR_RELATIVE)); + acl->revision = 0x02; + acl->alignment1 = 0x00; + acl->size = const_cpu_to_le16(0x34); + acl->ace_count = const_cpu_to_le16(0x02); + acl->alignment2 = 0x00; + + //ace1 + ace = (ACCESS_ALLOWED_ACE*)((char*)acl + sizeof(ACL)); + ace->type = 0x00; + ace->flags = 0x00; + ace->size = const_cpu_to_le16(0x14); + ace->mask = const_cpu_to_le32(0x12019F); + ace->sid.revision = 0x01; + ace->sid.sub_authority_count = 0x01; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); + //ace2 + ace = (ACCESS_ALLOWED_ACE*)((char*)ace + le16_to_cpu(ace->size)); + ace->type = 0x00; + ace->flags = 0x00; + ace->size = const_cpu_to_le16(0x18); + ace->mask = const_cpu_to_le32(0x12019F); + ace->sid.revision = 0x01; + ace->sid.sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + ace->sid.sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + + //owner sid + sid = (SID*)((char*)sd + le32_to_cpu(sd->owner)); + sid->revision = 0x01; + sid->sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + sid->identifier_authority.value[0] = 0; + sid->identifier_authority.value[1] = 0; + sid->identifier_authority.value[2] = 0; + sid->identifier_authority.value[3] = 0; + sid->identifier_authority.value[4] = 0; + sid->identifier_authority.value[5] = 5; + sid->sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + sid->sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + + //group sid + sid = (SID*)((char*)sd + le32_to_cpu(sd->group)); + sid->revision = 0x01; + sid->sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + sid->identifier_authority.value[0] = 0; + sid->identifier_authority.value[1] = 0; + sid->identifier_authority.value[2] = 0; + sid->identifier_authority.value[3] = 0; + sid->identifier_authority.value[4] = 0; + sid->identifier_authority.value[5] = 5; + sid->sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + sid->sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + + return; +} diff --git a/src/add-ons/kernel/file_systems/ntfs/utils/sd.h b/src/add-ons/kernel/file_systems/ntfs/utils/sd.h new file mode 100644 index 0000000000..7ad3e6a79c --- /dev/null +++ b/src/add-ons/kernel/file_systems/ntfs/utils/sd.h @@ -0,0 +1,11 @@ +#ifndef _NTFS_SD_H_ +#define _NTFS_SD_H_ + +#include "types.h" + +void init_system_file_sd(int sys_file_no, u8 **sd_val, int *sd_val_len); +void init_root_sd(u8 **sd_val, int *sd_val_len); +void init_secure_sds(char *sd_val); + +#endif /* _NTFS_SD_H_ */ + diff --git a/src/add-ons/kernel/file_systems/ntfs/utils.c b/src/add-ons/kernel/file_systems/ntfs/utils/utils.c similarity index 100% rename from src/add-ons/kernel/file_systems/ntfs/utils.c rename to src/add-ons/kernel/file_systems/ntfs/utils/utils.c diff --git a/src/add-ons/kernel/file_systems/ntfs/utils.h b/src/add-ons/kernel/file_systems/ntfs/utils/utils.h similarity index 100% rename from src/add-ons/kernel/file_systems/ntfs/utils.h rename to src/add-ons/kernel/file_systems/ntfs/utils/utils.h