diff --git a/src/add-ons/kernel/file_systems/ntfs/Jamfile b/src/add-ons/kernel/file_systems/ntfs/Jamfile index 921c9f1dd6..e05dbb6a6b 100644 --- a/src/add-ons/kernel/file_systems/ntfs/Jamfile +++ b/src/add-ons/kernel/file_systems/ntfs/Jamfile @@ -2,8 +2,6 @@ SubDir HAIKU_TOP src add-ons kernel file_systems ntfs ; SubDirHdrs [ FDirName $(SUBDIR) libntfs ] ; -SetSubDirSupportedPlatformsBeOSCompatible ; - SubDirCcFlags -DHAVE_CONFIG_H=1 ; SubDirC++Flags -DHAVE_CONFIG_H=1 ; diff --git a/src/add-ons/kernel/file_systems/ntfs/attributes.c b/src/add-ons/kernel/file_systems/ntfs/attributes.c index 46da4ce40d..c525dcb97b 100644 --- a/src/add-ons/kernel/file_systems/ntfs/attributes.c +++ b/src/add-ons/kernel/file_systems/ntfs/attributes.c @@ -54,18 +54,11 @@ status_t set_mime(vnode *node, const char *filename) } -#ifdef __HAIKU__ status_t fs_open_attrib_dir(fs_volume *_vol, fs_vnode *_node, void **_cookie) { nspace *ns = (nspace *)_vol->private_volume; -#else -int -fs_open_attrib_dir(void *_ns, void *_node, void **_cookie) -{ - nspace *ns = (nspace *)_ns; -#endif - + int result = B_NO_ERROR; ERRPRINT("fs_open_attrdir - ENTER\n"); @@ -88,17 +81,10 @@ exit: return result; } -#ifdef __HAIKU__ status_t fs_close_attrib_dir(fs_volume *_vol, fs_vnode *_node, void *_cookie) { nspace *ns = (nspace *)_vol->private_volume; -#else -int -fs_close_attrib_dir(void *_ns, void *_node, void *_cookie) -{ - nspace *ns = (nspace *)_ns; -#endif ERRPRINT("fs_close_attrdir - ENTER\n"); @@ -113,17 +99,10 @@ fs_close_attrib_dir(void *_ns, void *_node, void *_cookie) return B_NO_ERROR; } -#ifdef __HAIKU__ status_t fs_free_attrib_dir_cookie(fs_volume *_vol, fs_vnode *_node, void *_cookie) { nspace *ns = (nspace *)_vol->private_volume; -#else -int -fs_free_attrib_dir_cookie(void *_ns, void *_node, void *_cookie) -{ - nspace *ns = (nspace *)_ns; -#endif int result = B_NO_ERROR; @@ -149,17 +128,11 @@ exit: return result; } -#ifdef __HAIKU__ status_t fs_rewind_attrib_dir(fs_volume *_vol, fs_vnode *_node, void *_cookie) { nspace *ns = (nspace *)_vol->private_volume; -#else -int -fs_rewind_attrib_dir(void *_ns, void *_node, void *_cookie) -{ - nspace *ns = (nspace *)_ns; -#endif + int result = B_NO_ERROR; LOCK_VOL(ns); @@ -184,19 +157,12 @@ exit: } -#ifdef __HAIKU__ status_t fs_read_attrib_dir(fs_volume *_vol, fs_vnode *_node, void *_cookie, struct dirent *entry, size_t bufsize, uint32 *num) { nspace *ns = (nspace *)_vol->private_volume; vnode *node = (vnode *)_node->private_node; -#else -int -fs_read_attrib_dir(void *_ns, void *_node, void *_cookie, long *num, struct dirent *entry, size_t bufsize) -{ - nspace *ns = (nspace *)_ns; - vnode *node = (vnode *)_node; -#endif + int32 *cookie = (int32 *)_cookie; LOCK_VOL(ns); @@ -223,28 +189,6 @@ fs_read_attrib_dir(void *_ns, void *_node, void *_cookie, long *num, struct dire return B_NO_ERROR; } -#ifndef __HAIKU__ -int -fs_read_attrib_stat(void *_ns, void *_node, const char *name, struct attr_info *buf) -{ - nspace *ns = (nspace *)_ns; - vnode *node = (vnode *)_node; - - if (strcmp(name, "BEOS:TYPE")) - return ENOENT; - - if (node->mime == NULL) { - return ENOENT; - } - - buf->type = MIME_STRING_TYPE; - buf->size = strlen(node->mime) + 1; - - return 0; -} -#endif - -#ifdef __HAIKU__ status_t fs_open_attrib(fs_volume *_vol, fs_vnode *_node, const char *name, int openMode, void **_cookie) { @@ -276,25 +220,22 @@ exit: return result; } -#endif -#ifdef __HAIKU__ + status_t fs_close_attrib(fs_volume *_vol, fs_vnode *_node, void *cookie) { return B_NO_ERROR; } -#endif -#ifdef __HAIKU__ + status_t fs_free_attrib_cookie(fs_volume *_vol, fs_vnode *_node, void *cookie) { return B_NO_ERROR; } -#endif -#ifdef __HAIKU__ + status_t fs_read_attrib_stat(fs_volume *_vol, fs_vnode *_node, void *_cookie,struct stat *stat) { @@ -327,33 +268,24 @@ exit: return B_NO_ERROR; } -#endif -#ifdef __HAIKU__ + status_t fs_read_attrib(fs_volume *_vol, fs_vnode *_node, void *_cookie, off_t pos,void *buffer, size_t *_length) { nspace *ns = (nspace *)_vol->private_volume; vnode *node = (vnode *)_node->private_node; -#else -int -fs_read_attrib(void *_ns, void *_node, const char *name, int type, void *buffer, size_t *_length, off_t pos) -{ - nspace *ns = (nspace *)_ns; - vnode *node = (vnode *)_node; -#endif + int result = B_NO_ERROR; LOCK_VOL(ns); ERRPRINT("fs_read_attr - ENTER\n"); -#ifdef __HAIKU_ if (_cookie != &kBeOSTypeCookie) { result = ENOENT; goto exit; } -#endif if (node->mime == NULL) { result = ENOENT; @@ -379,17 +311,11 @@ exit: } -#ifdef __HAIKU__ status_t fs_write_attrib(fs_volume *_vol, fs_vnode *_node, void *_cookie,off_t pos, const void *buffer, size_t *_length) { nspace *ns = (nspace *)_vol->private_volume; -#else -int -fs_write_attrib(void *_ns, void *_node, const char *name, int type, const void *buffer, size_t *_length, off_t pos) -{ - nspace *ns = (nspace *)_ns; -#endif + int result = B_NO_ERROR; LOCK_VOL(ns); @@ -397,11 +323,10 @@ fs_write_attrib(void *_ns, void *_node, const char *name, int type, const void * ERRPRINT("fs_write_attr - ENTER\n"); *_length = 0; -#ifdef __HAIKU_ + if (_cookie != &kBeOSTypeCookie) { result = ENOSYS; } -#endif ERRPRINT("fs_write_attrib - EXIT, result is %s\n", strerror(result)); 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 6b255a5da1..7d139233b5 100644 --- a/src/add-ons/kernel/file_systems/ntfs/fs_func.c +++ b/src/add-ons/kernel/file_systems/ntfs/fs_func.c @@ -123,7 +123,7 @@ fs_identify_partition(int fd, partition_data *partition, void **_cookie) if(!ioctl(fd,B_GET_PATH_FOR_DEVICE,devpath)) return -1; //try mount - ntVolume = utils_mount_volume(devpath,MS_RDONLY|MS_NOATIME,true); + ntVolume = utils_mount_volume(devpath,MS_RDONLY,true); if(!ntVolume) return -1; @@ -208,8 +208,8 @@ fs_mount(fs_volume *_vol, const char *device, ulong flags, const char *args, ino mnt_flags |= MS_RDONLY; ns->flags |= B_MOUNT_READ_ONLY; } - if (ns->noatime) - mnt_flags |= MS_NOATIME; +// if (ns->noatime) +// mnt_flags |= MS_NOATIME; ns->ntvol=utils_mount_volume(device,mnt_flags,true); if(ns->ntvol!=NULL) @@ -367,7 +367,7 @@ fs_walk(fs_volume *_vol, fs_vnode *_dir, const char *file, ino_t *vnid) result = ENOENT; } else { unicode = ntfs_calloc(MAX_PATH); - len = ntfs_mbstoucs(file, &unicode, MAX_PATH); + len = ntfs_mbstoucs(file, &unicode); if (len < 0) { result = EILSEQ; goto exit; @@ -882,7 +882,7 @@ fs_create(fs_volume *_vol, fs_vnode *_dir, const char *name, int omode, int perm goto exit; } - uname_len = ntfs_mbstoucs(name, &uname, 0); + uname_len = ntfs_mbstoucs(name, &uname); if (uname_len < 0) { result = EINVAL; goto exit; @@ -1285,13 +1285,13 @@ fs_create_symlink(fs_volume *_vol, fs_vnode *_dir, const char *name, const char goto exit; } - uname_len = ntfs_mbstoucs(name, &uname, 0); + uname_len = ntfs_mbstoucs(name, &uname); if (uname_len < 0) { result = EINVAL; goto exit; } - utarget_len = ntfs_mbstoucs(target, &utarget, 0); + utarget_len = ntfs_mbstoucs(target, &utarget); if (utarget_len < 0) { result = EINVAL; goto exit; @@ -1380,7 +1380,7 @@ fs_mkdir(fs_volume *_vol, fs_vnode *_dir, const char *name, int perms) goto exit; } - uname_len = ntfs_mbstoucs(name, &uname, 0); + uname_len = ntfs_mbstoucs(name, &uname); if (uname_len < 0) { result = EINVAL; goto exit; @@ -1475,13 +1475,13 @@ fs_rename(fs_volume *_vol, fs_vnode *_odir, const char *oldname, fs_vnode *_ndir goto exit; //convert names from utf8 to unicode string - unewname_len = ntfs_mbstoucs(newname, &unewname, 0); + unewname_len = ntfs_mbstoucs(newname, &unewname); if (unewname_len < 0) { result = EINVAL; goto exit; } - uoldname_len = ntfs_mbstoucs(oldname, &uoldname, 0); + uoldname_len = ntfs_mbstoucs(oldname, &uoldname); if (uoldname_len < 0) { result = EINVAL; goto exit; @@ -1544,13 +1544,14 @@ fs_rename(fs_volume *_vol, fs_vnode *_odir, const char *oldname, fs_vnode *_ndir goto exit; } - ntfs_delete(oi, odi, uoldname, uoldname_len); - onode->parent_vnid = MREF( ndi->mft_no ); - + notify_entry_moved(ns->id, MREF( odi->mft_no ), oldname, MREF( ndi->mft_no ), newname, onode->vnid ); notify_attribute_changed(ns->id, onode->vnid, "BEOS:TYPE", B_ATTR_CHANGED); + ntfs_delete(oi, odi, uoldname, uoldname_len); + oi = odi = NULL; /* ntfs_delete() always closes ni and dir_ni */ + put_vnode(_vol, onode->vnid ); } else { //renaming @@ -1590,10 +1591,12 @@ fs_rename(fs_volume *_vol, fs_vnode *_odir, const char *oldname, fs_vnode *_ndir goto exit; } - ntfs_delete(oi, odi, uoldname, uoldname_len); notify_entry_moved(ns->id, MREF( odi->mft_no ), oldname, MREF( odi->mft_no ), newname, onode->vnid ); notify_attribute_changed(ns->id, onode->vnid, "BEOS:TYPE", B_ATTR_CHANGED); put_vnode(_vol, onode->vnid ); + + ntfs_delete(oi, odi, uoldname, uoldname_len); + oi = odi = NULL; /* ntfs_delete() always closes ni and dir_ni */ } @@ -1626,7 +1629,7 @@ do_unlink(fs_volume *_vol, vnode *dir, const char *name, bool isdir) int uname_len; status_t result = B_NO_ERROR; - uname_len = ntfs_mbstoucs(name, &uname, 0); + uname_len = ntfs_mbstoucs(name, &uname); if (uname_len < 0) { result = EINVAL; goto exit1; @@ -1675,8 +1678,8 @@ do_unlink(fs_volume *_vol, vnode *dir, const char *name, bool isdir) if(ntfs_delete(ni, bi, uname, uname_len)) result = errno; - else - ni = NULL; + + ni = bi = NULL; node->parent_vnid = dir->vnid; @@ -1760,7 +1763,7 @@ fs_unlink(fs_volume *_vol, fs_vnode *_dir, const char *name) ERRPRINT("fs_unlink - ENTER: name %s\n", name==NULL?"NULL":name); - if( ns == NULL || dir == NULL || name ==NULL) { + if( ns == NULL || dir == NULL || name == NULL) { result = EINVAL; goto exit; } diff --git a/src/add-ons/kernel/file_systems/ntfs/fsproto.h b/src/add-ons/kernel/file_systems/ntfs/fsproto.h deleted file mode 100644 index 1099f2eac3..0000000000 --- a/src/add-ons/kernel/file_systems/ntfs/fsproto.h +++ /dev/null @@ -1,246 +0,0 @@ -#ifndef _FSPROTO_H -#define _FSPROTO_H - -//#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -typedef dev_t nspace_id; - -/* UGLY UGLY UGLY */ -#ifndef _DRIVERS_H -struct selectsync; -typedef struct selectsync selectsync; -#endif - - -/* - * PUBLIC PART OF THE FILE SYSTEM PROTOCOL - */ - -#define WSTAT_MODE 0x0001 -#define WSTAT_UID 0x0002 -#define WSTAT_GID 0x0004 -#define WSTAT_SIZE 0x0008 -#define WSTAT_ATIME 0x0010 -#define WSTAT_MTIME 0x0020 -#define WSTAT_CRTIME 0x0040 - -#define WFSSTAT_NAME 0x0001 - -#define B_ENTRY_CREATED 1 -#define B_ENTRY_REMOVED 2 -#define B_ENTRY_MOVED 3 -#define B_STAT_CHANGED 4 -#define B_ATTR_CHANGED 5 -#define B_DEVICE_MOUNTED 6 -#define B_DEVICE_UNMOUNTED 7 - -#define B_STOP_WATCHING 0x0000 -#define B_WATCH_NAME 0x0001 -#define B_WATCH_STAT 0x0002 -#define B_WATCH_ATTR 0x0004 -#define B_WATCH_DIRECTORY 0x0008 - -#define SELECT_READ 1 -#define SELECT_WRITE 2 -#define SELECT_EXCEPTION 3 - -#define B_CUR_FS_API_VERSION 2 - -struct attr_info; -struct index_info; - -typedef int op_read_vnode(void *ns, ino_t vnid, char r, void **node); -typedef int op_write_vnode(void *ns, void *node, char r); -typedef int op_remove_vnode(void *ns, void *node, char r); -typedef int op_secure_vnode(void *ns, void *node); - -typedef int op_walk(void *ns, void *base, const char *file, char **newpath, - ino_t *vnid); - -typedef int op_access(void *ns, void *node, int mode); - -typedef int op_create(void *ns, void *dir, const char *name, - int omode, int perms, ino_t *vnid, void **cookie); -typedef int op_mkdir(void *ns, void *dir, const char *name, int perms); -typedef int op_symlink(void *ns, void *dir, const char *name, - const char *path); -typedef int op_link(void *ns, void *dir, const char *name, void *node); - -typedef int op_rename(void *ns, void *olddir, const char *oldname, - void *newdir, const char *newname); -typedef int op_unlink(void *ns, void *dir, const char *name); -typedef int op_rmdir(void *ns, void *dir, const char *name); - -typedef int op_readlink(void *ns, void *node, char *buf, size_t *bufsize); - -typedef int op_opendir(void *ns, void *node, void **cookie); -typedef int op_closedir(void *ns, void *node, void *cookie); -typedef int op_rewinddir(void *ns, void *node, void *cookie); -typedef int op_readdir(void *ns, void *node, void *cookie, long *num, - struct dirent *buf, size_t bufsize); - -typedef int op_open(void *ns, void *node, int omode, void **cookie); -typedef int op_close(void *ns, void *node, void *cookie); -typedef int op_free_cookie(void *ns, void *node, void *cookie); -typedef int op_read(void *ns, void *node, void *cookie, off_t pos, void *buf, - size_t *len); -typedef int op_write(void *ns, void *node, void *cookie, off_t pos, - const void *buf, size_t *len); -typedef int op_readv(void *ns, void *node, void *cookie, off_t pos, const iovec *vec, - size_t count, size_t *len); -typedef int op_writev(void *ns, void *node, void *cookie, off_t pos, const iovec *vec, - size_t count, size_t *len); -typedef int op_ioctl(void *ns, void *node, void *cookie, int cmd, void *buf, - size_t len); -typedef int op_setflags(void *ns, void *node, void *cookie, int flags); - -typedef int op_rstat(void *ns, void *node, struct stat *); -typedef int op_wstat(void *ns, void *node, struct stat *, long mask); -typedef int op_fsync(void *ns, void *node); - -typedef int op_select(void *ns, void *node, void *cookie, uint8 event, - uint32 ref, selectsync *sync); -typedef int op_deselect(void *ns, void *node, void *cookie, uint8 event, - selectsync *sync); - -typedef int op_initialize(const char *devname, void *parms, size_t len); -typedef int op_mount(nspace_id nsid, const char *devname, ulong flags, - void *parms, size_t len, void **data, ino_t *vnid); -typedef int op_unmount(void *ns); -typedef int op_sync(void *ns); -typedef int op_rfsstat(void *ns, struct fs_info *); -typedef int op_wfsstat(void *ns, struct fs_info *, long mask); - - -typedef int op_open_attrdir(void *ns, void *node, void **cookie); -typedef int op_close_attrdir(void *ns, void *node, void *cookie); -typedef int op_rewind_attrdir(void *ns, void *node, void *cookie); -typedef int op_read_attrdir(void *ns, void *node, void *cookie, long *num, - struct dirent *buf, size_t bufsize); -typedef int op_remove_attr(void *ns, void *node, const char *name); -typedef int op_rename_attr(void *ns, void *node, const char *oldname, - const char *newname); -typedef int op_stat_attr(void *ns, void *node, const char *name, - struct attr_info *buf); - -typedef int op_write_attr(void *ns, void *node, const char *name, int type, - const void *buf, size_t *len, off_t pos); -typedef int op_read_attr(void *ns, void *node, const char *name, int type, - void *buf, size_t *len, off_t pos); - -typedef int op_open_indexdir(void *ns, void **cookie); -typedef int op_close_indexdir(void *ns, void *cookie); -typedef int op_rewind_indexdir(void *ns, void *cookie); -typedef int op_read_indexdir(void *ns, void *cookie, long *num, - struct dirent *buf, size_t bufsize); -typedef int op_create_index(void *ns, const char *name, int type, int flags); -typedef int op_remove_index(void *ns, const char *name); -typedef int op_rename_index(void *ns, const char *oldname, - const char *newname); -typedef int op_stat_index(void *ns, const char *name, struct index_info *buf); - -typedef int op_open_query(void *ns, const char *query, ulong flags, - port_id port, long token, void **cookie); -typedef int op_close_query(void *ns, void *cookie); -typedef int op_read_query(void *ns, void *cookie, long *num, - struct dirent *buf, size_t bufsize); - -typedef struct vnode_ops { - op_read_vnode (*read_vnode); - op_write_vnode (*write_vnode); - op_remove_vnode (*remove_vnode); - op_secure_vnode (*secure_vnode); - op_walk (*walk); - op_access (*access); - op_create (*create); - op_mkdir (*mkdir); - op_symlink (*symlink); - op_link (*link); - op_rename (*rename); - op_unlink (*unlink); - op_rmdir (*rmdir); - op_readlink (*readlink); - op_opendir (*opendir); - op_closedir (*closedir); - op_free_cookie (*free_dircookie); - op_rewinddir (*rewinddir); - op_readdir (*readdir); - op_open (*open); - op_close (*close); - op_free_cookie (*free_cookie); - op_read (*read); - op_write (*write); - op_readv (*readv); - op_writev (*writev); - op_ioctl (*ioctl); - op_setflags (*setflags); - op_rstat (*rstat); - op_wstat (*wstat); - op_fsync (*fsync); - op_initialize (*initialize); - op_mount (*mount); - op_unmount (*unmount); - op_sync (*sync); - op_rfsstat (*rfsstat); - op_wfsstat (*wfsstat); - op_select (*select); - op_deselect (*deselect); - op_open_indexdir (*open_indexdir); - op_close_indexdir (*close_indexdir); - op_free_cookie (*free_indexdircookie); - op_rewind_indexdir (*rewind_indexdir); - op_read_indexdir (*read_indexdir); - op_create_index (*create_index); - op_remove_index (*remove_index); - op_rename_index (*rename_index); - op_stat_index (*stat_index); - op_open_attrdir (*open_attrdir); - op_close_attrdir (*close_attrdir); - op_free_cookie (*free_attrdircookie); - op_rewind_attrdir (*rewind_attrdir); - op_read_attrdir (*read_attrdir); - op_write_attr (*write_attr); - op_read_attr (*read_attr); - op_remove_attr (*remove_attr); - op_rename_attr (*rename_attr); - op_stat_attr (*stat_attr); - op_open_query (*open_query); - op_close_query (*close_query); - op_free_cookie (*free_querycookie); - op_read_query (*read_query); -} vnode_ops; - -extern int new_path(const char *path, char **copy); -extern void free_path(char *p); - -extern int notify_listener(int op, nspace_id nsid, - ino_t vnida, ino_t vnidb, - ino_t vnidc, const char *name); -extern void notify_select_event(selectsync *sync, uint32 ref); -extern int send_notification(port_id port, long token, - ulong what, long op, nspace_id nsida, - nspace_id nsidb, ino_t vnida, - ino_t vnidb, ino_t vnidc, - const char *name); -extern int get_vnode(nspace_id nsid, ino_t vnid, void **data); -extern int put_vnode(nspace_id nsid, ino_t vnid); -extern int new_vnode(nspace_id nsid, ino_t vnid, void *data); -extern int remove_vnode(nspace_id nsid, ino_t vnid); -extern int unremove_vnode(nspace_id nsid, ino_t vnid); -extern int is_vnode_removed(nspace_id nsid, ino_t vnid); - - -extern _EXPORT vnode_ops fs_entry; -extern _EXPORT int32 api_version; - -#endif diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/Jamfile b/src/add-ons/kernel/file_systems/ntfs/libntfs/Jamfile index 0fd6b1b3ad..7fe9d771b0 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/Jamfile +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/Jamfile @@ -1,7 +1,5 @@ SubDir HAIKU_TOP src add-ons kernel file_systems ntfs libntfs ; -SetSubDirSupportedPlatformsBeOSCompatible ; - SubDirCcFlags -Wall -Wno-multichar -DHAVE_CONFIG_H=1 ; StaticLibrary libntfs.a : @@ -27,7 +25,6 @@ StaticLibrary libntfs.a : runlist.c security.c unistr.c - version.c volume.c ; diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/attrib.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/attrib.c index bf903f9bf0..edd70df6d4 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/attrib.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/attrib.c @@ -3,8 +3,9 @@ * * Copyright (c) 2000-2005 Anton Altaparmakov * Copyright (c) 2002-2005 Richard Russon - * Copyright (c) 2002-2006 Szabolcs Szakacsits - * Copyright (c) 2004-2006 Yura Pakhuchiy + * Copyright (c) 2002-2008 Szabolcs Szakacsits + * Copyright (c) 2004-2007 Yura Pakhuchiy + * Copyright (c) 2007-2008 Jean-Pierre Andre * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -60,6 +61,37 @@ ntfschar AT_UNNAMED[] = { const_cpu_to_le16('\0') }; +static int NAttrFlag(ntfs_attr *na, FILE_ATTR_FLAGS flag) +{ + if (na->type == AT_DATA && na->name == AT_UNNAMED) + return (na->ni->flags & flag); + return 0; +} + +static void NAttrSetFlag(ntfs_attr *na, FILE_ATTR_FLAGS flag) +{ + if (na->type == AT_DATA && na->name == AT_UNNAMED) + na->ni->flags |= flag; + else + ntfs_log_trace("Denied setting flag %d for not unnamed data " + "attribute\n", flag); +} + +static void NAttrClearFlag(ntfs_attr *na, FILE_ATTR_FLAGS flag) +{ + if (na->type == AT_DATA && na->name == AT_UNNAMED) + na->ni->flags &= ~flag; +} + +#define GenNAttrIno(func_name, flag) \ +int NAttr##func_name(ntfs_attr *na) { return NAttrFlag (na, flag); } \ +void NAttrSet##func_name(ntfs_attr *na) { NAttrSetFlag (na, flag); } \ +void NAttrClear##func_name(ntfs_attr *na){ NAttrClearFlag(na, flag); } + +GenNAttrIno(Compressed, FILE_ATTR_COMPRESSED) +GenNAttrIno(Encrypted, FILE_ATTR_ENCRYPTED) +GenNAttrIno(Sparse, FILE_ATTR_SPARSE_FILE) + /** * ntfs_get_attribute_value_length - Find the length of an attribute * @a: @@ -343,39 +375,53 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name, u32 name_len) { ntfs_attr_search_ctx *ctx; - ntfs_attr *na; + ntfs_attr *na = NULL; + ntfschar *newname = NULL; ATTR_RECORD *a; - int err; BOOL cs; - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", - (unsigned long long)ni->mft_no, type); + ntfs_log_enter("Entering for inode %lld, attr 0x%x.\n", + (unsigned long long)ni->mft_no, type); + if (!ni || !ni->vol || !ni->mrec) { errno = EINVAL; - return NULL; + goto out; } - na = calloc(sizeof(ntfs_attr), 1); + na = ntfs_calloc(sizeof(ntfs_attr)); if (!na) - return NULL; + goto out; if (name && name != AT_UNNAMED && name != NTFS_INDEX_I30) { name = ntfs_ucsndup(name, name_len); - if (!name) { - free(na); - return NULL; - } + if (!name) + goto err_out; + newname = name; } ctx = ntfs_attr_get_search_ctx(ni, NULL); - if (!ctx) { - err = errno; + if (!ctx) goto err_out; - } - if (ntfs_attr_lookup(type, name, name_len, 0, 0, NULL, 0, ctx)) { - err = errno; + + if (ntfs_attr_lookup(type, name, name_len, 0, 0, NULL, 0, ctx)) goto put_err_out; - } a = ctx->attr; + + if (!name) { + if (a->name_length) { + name = ntfs_ucsndup((ntfschar*)((u8*)a + le16_to_cpu( + a->name_offset)), a->name_length); + if (!name) + goto put_err_out; + newname = name; + name_len = a->name_length; + } else { + name = AT_UNNAMED; + name_len = 0; + } + } + + __ntfs_attr_init(na, ni, type, name, name_len); + /* * Wipe the flags in case they are not zero for an attribute list * attribute. Windows does not complain about invalid flags and chkdsk @@ -383,23 +429,28 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, */ if (type == AT_ATTRIBUTE_LIST) a->flags = 0; + cs = a->flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE); - if (!name) { - if (a->name_length) { - name = ntfs_ucsndup((ntfschar*)((u8*)a + le16_to_cpu( - a->name_offset)), a->name_length); - if (!name) { - err = errno; - goto put_err_out; - } - name_len = a->name_length; - } else { - name = AT_UNNAMED; - name_len = 0; - } + + if (na->type == AT_DATA && na->name == AT_UNNAMED && + ((!(a->flags & ATTR_IS_COMPRESSED) != !NAttrCompressed(na)) || + (!(a->flags & ATTR_IS_SPARSE) != !NAttrSparse(na)) || + (!(a->flags & ATTR_IS_ENCRYPTED) != !NAttrEncrypted(na)))) { + errno = EIO; + ntfs_log_perror("Inode %lld has corrupt attribute flags " + "(0x%x <> 0x%x)",(unsigned long long)ni->mft_no, + a->flags, na->ni->flags); + goto put_err_out; } - __ntfs_attr_init(na, ni, type, name, name_len); + if (a->non_resident) { + if ((a->flags & ATTR_IS_COMPRESSED) && !a->compression_unit) { + errno = EIO; + ntfs_log_perror("Compressed inode %lld attr 0x%x has " + "no compression unit", + (unsigned long long)ni->mft_no, type); + goto put_err_out; + } ntfs_attr_init(na, TRUE, a->flags & ATTR_IS_COMPRESSED, a->flags & ATTR_IS_ENCRYPTED, a->flags & ATTR_IS_SPARSE, @@ -416,13 +467,17 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, cs ? (l + 7) & ~7 : 0, 0); } ntfs_attr_put_search_ctx(ctx); +out: + ntfs_log_leave("\n"); return na; + put_err_out: ntfs_attr_put_search_ctx(ctx); err_out: + free(newname); free(na); - errno = err; - return NULL; + na = NULL; + goto out; } /** @@ -449,7 +504,7 @@ void ntfs_attr_close(ntfs_attr *na) * @na: ntfs attribute for which to map (part of) a runlist * @vcn: map runlist part containing this vcn * - * Map the part of a runlist containing the @vcn of an the ntfs attribute @na. + * Map the part of a runlist containing the @vcn of the ntfs attribute @na. * * Return 0 on success and -1 on error with errno set to the error code. */ @@ -492,8 +547,8 @@ int ntfs_attr_map_runlist(ntfs_attr *na, VCN vcn) * ntfs_attr_map_whole_runlist - map the whole runlist of an ntfs attribute * @na: ntfs attribute for which to map the runlist * - * Map the whole runlist of an the ntfs attribute @na. For an attribute made - * up of only one attribute extent this is the same as calling + * Map the whole runlist of the ntfs attribute @na. For an attribute made up + * of only one attribute extent this is the same as calling * ntfs_attr_map_runlist(na, 0) but for an attribute with multiple extents this * will map the runlist fragments from each of the extents thus giving access * to the entirety of the disk allocation of an attribute. @@ -506,14 +561,14 @@ int ntfs_attr_map_whole_runlist(ntfs_attr *na) ntfs_attr_search_ctx *ctx; ntfs_volume *vol = na->ni->vol; ATTR_RECORD *a; - int err; + int ret = -1; - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", - (unsigned long long)na->ni->mft_no, na->type); + ntfs_log_enter("Entering for inode %llu, attr 0x%x.\n", + (unsigned long long)na->ni->mft_no, na->type); ctx = ntfs_attr_get_search_ctx(na->ni, NULL); if (!ctx) - return -1; + goto out; /* Map all attribute extents one by one. */ next_vcn = last_vcn = highest_vcn = 0; @@ -584,17 +639,13 @@ int ntfs_attr_map_whole_runlist(ntfs_attr *na) (long long)highest_vcn, (long long)last_vcn); goto err_out; } - err = errno; + if (errno == ENOENT) + ret = 0; +err_out: ntfs_attr_put_search_ctx(ctx); - if (err == ENOENT) - return 0; -out_now: - errno = err; - return -1; -err_out: - err = errno; - ntfs_attr_put_search_ctx(ctx); - goto out_now; +out: + ntfs_log_leave("\n"); + return ret; } /** @@ -727,41 +778,16 @@ map_rl: } /** - * ntfs_attr_pread - read from an attribute specified by an ntfs_attr structure - * @na: ntfs attribute to read from - * @pos: byte position in the attribute to begin reading from - * @count: number of bytes to read - * @b: output data buffer - * - * This function will read @count bytes starting at offset @pos from the ntfs - * attribute @na into the data buffer @b. - * - * On success, return the number of successfully read bytes. If this number is - * lower than @count this means that the read reached end of file or that an - * error was encountered during the read so that the read is partial. 0 means - * end of file or nothing was read (also return 0 when @count is 0). - * - * On error and nothing has been read, return -1 with errno set appropriately - * to the return code of ntfs_pread(), or to EINVAL in case of invalid - * arguments. - */ -s64 ntfs_attr_pread(ntfs_attr *na, const s64 pos, s64 count, void *b) + * ntfs_attr_pread_i - see description at ntfs_attr_pread() + */ +static s64 ntfs_attr_pread_i(ntfs_attr *na, const s64 pos, s64 count, void *b) { s64 br, to_read, ofs, total, total2; ntfs_volume *vol; runlist_element *rl; - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, pos 0x%llx, count " - "0x%llx.\n", (unsigned long long)na->ni->mft_no, - na->type, (long long)pos, (long long)count); - if (!na || !na->ni || !na->ni->vol || !b || pos < 0 || count < 0) { - errno = EINVAL; - return -1; - } - /* - * If this is a compressed attribute it needs special treatment, but - * only if it is non-resident. - */ + /* Sanity checking arguments is done in ntfs_attr_pread(). */ + if (NAttrCompressed(na) && NAttrNonResident(na)) return ntfs_compressed_attr_pread(na, pos, count, b); /* @@ -773,10 +799,7 @@ s64 ntfs_attr_pread(ntfs_attr *na, const s64 pos, s64 count, void *b) return -1; } vol = na->ni->vol; - /* Update access time if needed. */ - if (na->type == AT_DATA || na->type == AT_INDEX_ROOT || - na->type == AT_INDEX_ALLOCATION) - ntfs_inode_update_atime(na->ni); + if (!count) return 0; /* Truncate reads beyond end of attribute. */ @@ -804,6 +827,7 @@ res_err_out: le32_to_cpu(ctx->attr->value_length) > (char*)ctx->mrec + vol->mft_record_size) { errno = EIO; + ntfs_log_perror("%s: Sanity check failed", __FUNCTION__); goto res_err_out; } memcpy(b, val + pos, count); @@ -829,8 +853,10 @@ res_err_out: * However, we already truncated the read to the data_size, * so getting this here is an error. */ - if (errno == ENOENT) + if (errno == ENOENT) { errno = EIO; + ntfs_log_perror("%s: Failed to find VCN #1", __FUNCTION__); + } return -1; } /* @@ -843,18 +869,28 @@ res_err_out: if (rl->lcn == LCN_RL_NOT_MAPPED) { rl = ntfs_attr_find_vcn(na, rl->vcn); if (!rl) { - if (errno == ENOENT) + if (errno == ENOENT) { errno = EIO; + ntfs_log_perror("%s: Failed to find VCN #2", + __FUNCTION__); + } goto rl_err_out; } /* Needed for case when runs merged. */ ofs = pos + total - (rl->vcn << vol->cluster_size_bits); } - if (!rl->length) + if (!rl->length) { + errno = EIO; + ntfs_log_perror("%s: Zero run length", __FUNCTION__); goto rl_err_out; + } if (rl->lcn < (LCN)0) { - if (rl->lcn != (LCN)LCN_HOLE) + if (rl->lcn != (LCN)LCN_HOLE) { + ntfs_log_perror("%s: Bad run (%lld)", + __FUNCTION__, + (long long)rl->lcn); goto rl_err_out; + } /* It is a hole, just zero the matching @b range. */ to_read = min(count, (rl->length << vol->cluster_size_bits) - ofs); @@ -869,8 +905,9 @@ res_err_out: to_read = min(count, (rl->length << vol->cluster_size_bits) - ofs); retry: - ntfs_log_trace("Reading 0x%llx bytes from vcn 0x%llx, lcn 0x%llx, " - "ofs 0x%llx.\n", to_read, rl->vcn, rl->lcn, ofs); + ntfs_log_trace("Reading %lld bytes from vcn %lld, lcn %lld, ofs" + " %lld.\n", (long long)to_read, (long long)rl->vcn, + (long long )rl->lcn, (long long)ofs); br = ntfs_pread(vol->dev, (rl->lcn << vol->cluster_size_bits) + ofs, to_read, b); /* If everything ok, update progress counters and continue. */ @@ -878,8 +915,9 @@ retry: total += br; count -= br; b = (u8*)b + br; - continue; } + if (br == to_read) + continue; /* If the syscall was interrupted, try again. */ if (br == (s64)-1 && errno == EINTR) goto retry; @@ -887,6 +925,7 @@ retry: return total; if (!br) errno = EIO; + ntfs_log_perror("%s: ntfs_pread failed", __FUNCTION__); return -1; } /* Finally, return the number of bytes read. */ @@ -898,6 +937,47 @@ rl_err_out: return -1; } +/** + * ntfs_attr_pread - read from an attribute specified by an ntfs_attr structure + * @na: ntfs attribute to read from + * @pos: byte position in the attribute to begin reading from + * @count: number of bytes to read + * @b: output data buffer + * + * This function will read @count bytes starting at offset @pos from the ntfs + * attribute @na into the data buffer @b. + * + * On success, return the number of successfully read bytes. If this number is + * lower than @count this means that the read reached end of file or that an + * error was encountered during the read so that the read is partial. 0 means + * end of file or nothing was read (also return 0 when @count is 0). + * + * On error and nothing has been read, return -1 with errno set appropriately + * to the return code of ntfs_pread(), or to EINVAL in case of invalid + * arguments. + */ +s64 ntfs_attr_pread(ntfs_attr *na, const s64 pos, s64 count, void *b) +{ + s64 ret; + + if (!na || !na->ni || !na->ni->vol || !b || pos < 0 || count < 0) { + errno = EINVAL; + ntfs_log_perror("%s: na=%p b=%p pos=%lld count=%lld", + __FUNCTION__, na, b, (long long)pos, + (long long)count); + return -1; + } + + ntfs_log_enter("Entering for inode %lld attr 0x%x pos %lld count " + "%lld\n", (unsigned long long)na->ni->mft_no, + na->type, (long long)pos, (long long)count); + + ret = ntfs_attr_pread_i(na, pos, count, b); + + ntfs_log_leave("\n"); + return ret; +} + static int ntfs_attr_fill_zero(ntfs_attr *na, s64 pos, s64 count) { char *buf; @@ -949,7 +1029,8 @@ static int ntfs_attr_fill_hole(ntfs_attr *na, s64 count, s64 *ofs, from_vcn = (*rl)->vcn + (*ofs >> vol->cluster_size_bits); ntfs_log_trace("count: %lld, cur_vcn: %lld, from: %lld, to: %lld, ofs: " - "%lld\n", count, cur_vcn, from_vcn, to_write, *ofs); + "%lld\n", (long long)count, (long long)cur_vcn, + (long long)from_vcn, (long long)to_write, (long long)*ofs); /* Map whole runlist to be able update mapping pairs later. */ if (ntfs_attr_map_whole_runlist(na)) @@ -1081,17 +1162,20 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) ntfs_volume *vol; ntfs_attr_search_ctx *ctx = NULL; runlist_element *rl; + s64 hole_end; int eo; struct { unsigned int undo_initialized_size : 1; unsigned int undo_data_size : 1; } need_to = { 0, 0 }; - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, pos 0x%llx, count " - "0x%llx.\n", na->ni->mft_no, na->type, (long long)pos, - (long long)count); + ntfs_log_enter("Entering for inode %lld, attr 0x%x, pos 0x%llx, count " + "0x%llx.\n", (long long)na->ni->mft_no, na->type, + (long long)pos, (long long)count); + if (!na || !na->ni || !na->ni->vol || !b || pos < 0 || count < 0) { errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); goto errno_set; } vol = na->ni->vol; @@ -1111,10 +1195,7 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) errno = EOPNOTSUPP; goto errno_set; } - /* Update access and change times if needed. */ - if (na->type == AT_DATA || na->type == AT_INDEX_ROOT || - na->type == AT_INDEX_ALLOCATION) - ntfs_inode_update_time(na->ni); + if (!count) goto out; /* If the write reaches beyond the end, extend the attribute. */ @@ -1135,13 +1216,16 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) if (!ctx) goto err_out; if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0, - 0, NULL, 0, ctx)) + 0, NULL, 0, ctx)) { + ntfs_log_perror("%s: lookup failed", __FUNCTION__); goto err_out; + } val = (char*)ctx->attr + le16_to_cpu(ctx->attr->value_offset); if (val < (char*)ctx->attr || val + le32_to_cpu(ctx->attr->value_length) > (char*)ctx->mrec + vol->mft_record_size) { errno = EIO; + ntfs_log_perror("%s: Sanity check failed", __FUNCTION__); goto err_out; } memcpy(val + pos, b, count); @@ -1154,6 +1238,7 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) * it is unlikely to fail writing it, so is ok to just * return error here... (AIA) */ + ntfs_log_perror("%s: failed to write mft record", __FUNCTION__); goto err_out; } ntfs_attr_put_search_ctx(ctx); @@ -1211,8 +1296,10 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) * However, we already extended the size of the attribute, * so getting this here must be an error of some kind. */ - if (errno == ENOENT) + if (errno == ENOENT) { errno = EIO; + ntfs_log_perror("%s: Failed to find VCN #1", __FUNCTION__); + } goto err_out; } /* @@ -1221,12 +1308,15 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) * length. */ ofs = pos - (rl->vcn << vol->cluster_size_bits); - for (; count; rl++, ofs = 0) { + for (hole_end = 0; count; rl++, ofs = 0, hole_end = 0) { if (rl->lcn == LCN_RL_NOT_MAPPED) { rl = ntfs_attr_find_vcn(na, rl->vcn); if (!rl) { - if (errno == ENOENT) + if (errno == ENOENT) { errno = EIO; + ntfs_log_perror("%s: Failed to find VCN" + " #2", __FUNCTION__); + } goto rl_err_out; } /* Needed for case when runs merged. */ @@ -1234,12 +1324,18 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) } if (!rl->length) { errno = EIO; + ntfs_log_perror("%s: Zero run length", __FUNCTION__); goto rl_err_out; } if (rl->lcn < (LCN)0) { + + hole_end = rl->vcn + rl->length; if (rl->lcn != (LCN)LCN_HOLE) { errno = EIO; + ntfs_log_perror("%s: Unexpected LCN (%lld)", + __FUNCTION__, + (long long)rl->lcn); goto rl_err_out; } @@ -1250,20 +1346,53 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) to_write = min(count, (rl->length << vol->cluster_size_bits) - ofs); retry: ntfs_log_trace("Writing %lld bytes to vcn %lld, lcn %lld, ofs " - "%lld.\n", to_write, rl->vcn, rl->lcn, ofs); - if (!NVolReadOnly(vol)) - written = ntfs_pwrite(vol->dev, (rl->lcn << - vol->cluster_size_bits) + ofs, - to_write, b); - else + "%lld.\n", (long long)to_write, (long long)rl->vcn, + (long long)rl->lcn, (long long)ofs); + if (!NVolReadOnly(vol)) { + + s64 wpos = (rl->lcn << vol->cluster_size_bits) + ofs; + s64 wend = (rl->vcn << vol->cluster_size_bits) + ofs + to_write; + u32 bsize = vol->cluster_size; + /* Byte size needed to zero fill a cluster */ + s64 rounding = ((wend + bsize - 1) & ~(s64)(bsize - 1)) - wend; + /** + * Zero fill to cluster boundary if we're writing at the + * end of the attribute or into an ex-sparse cluster. + * This will cause the kernel not to seek and read disk + * blocks during write(2) to fill the end of the buffer + * which increases write speed by 2-10 fold typically. + */ + if (rounding && ((wend == na->initialized_size) || + (wend < (hole_end << vol->cluster_size_bits)))){ + + char *cb; + + rounding += to_write; + + cb = ntfs_malloc(rounding); + if (!cb) + goto err_out; + + memcpy(cb, b, to_write); + memset(cb + to_write, 0, rounding - to_write); + + written = ntfs_pwrite(vol->dev, wpos, rounding, cb); + if (written == rounding) + written = to_write; + + free(cb); + } else + written = ntfs_pwrite(vol->dev, wpos, to_write, b); + } else written = to_write; /* If everything ok, update progress counters and continue. */ if (written > 0) { total += written; count -= written; b = (const u8*)b + written; - continue; } + if (written == to_write) + continue; /* If the syscall was interrupted, try again. */ if (written == (s64)-1 && errno == EINTR) goto retry; @@ -1276,8 +1405,16 @@ done: ntfs_attr_put_search_ctx(ctx); /* Update mapping pairs if needed. */ if (update_from != -1) - ntfs_attr_update_mapping_pairs(na, 0 /*update_from*/); + if (ntfs_attr_update_mapping_pairs(na, 0 /*update_from*/)) { + /* + * FIXME: trying to recover by goto rl_err_out; + * could cause driver hang by infinite looping. + */ + total = -1; + goto out; + } out: + ntfs_log_leave("\n"); return total; rl_err_out: eo = errno; @@ -1289,7 +1426,6 @@ rl_err_out: * TODO: Need to try to change initialized_size. If it * succeeds goto done, otherwise goto err_out. (AIA) */ - errno = EOPNOTSUPP; goto err_out; } goto done; @@ -1384,6 +1520,7 @@ s64 ntfs_attr_mst_pread(ntfs_attr *na, const s64 pos, const s64 bk_cnt, (long long)pos); if (bk_cnt < 0 || bk_size % NTFS_BLOCK_SIZE) { errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); return -1; } br = ntfs_attr_pread(na, pos, bk_cnt * bk_size, dst); @@ -1449,6 +1586,7 @@ s64 ntfs_attr_mst_pwrite(ntfs_attr *na, const s64 pos, s64 bk_cnt, ((u8*)src + i * bk_size), bk_size); if (err < 0) { /* Abort write at this position. */ + ntfs_log_perror("%s #1", __FUNCTION__); if (!i) return err; bk_cnt = i; @@ -1457,6 +1595,10 @@ s64 ntfs_attr_mst_pwrite(ntfs_attr *na, const s64 pos, s64 bk_cnt, } /* Write the prepared data. */ written = ntfs_attr_pwrite(na, pos, bk_cnt * bk_size, src); + if (written <= 0) { + ntfs_log_perror("%s: written=%lld", __FUNCTION__, + (long long)written); + } /* Quickly deprotect the data again. */ for (i = 0; i < bk_cnt; ++i) ntfs_mst_post_write_fixup((NTFS_RECORD*)((u8*)src + i * @@ -1555,6 +1697,7 @@ static int ntfs_attr_find(const ATTR_TYPES type, const ntfschar *name, } else { if (name && name != AT_UNNAMED) { errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); return -1; } vol = NULL; @@ -1666,8 +1809,9 @@ static int ntfs_attr_find(const ATTR_TYPES type, const ntfschar *name, } } } - ntfs_log_debug("ntfs_attr_find(): File is corrupt. Run chkdsk.\n"); errno = EIO; + ntfs_log_perror("%s: Corrupt inode (%lld)", __FUNCTION__, + ctx->ntfs_ino ? (long long)ctx->ntfs_ino->mft_no : -1); return -1; } @@ -1782,7 +1926,7 @@ static int ntfs_external_attr_find(ATTR_TYPES type, const ntfschar *name, ni = ctx->ntfs_ino; base_ni = ctx->base_ntfs_ino; - ntfs_log_trace("Entering for inode 0x%llx, attribute type 0x%x.\n", + ntfs_log_trace("Entering for inode %lld, attribute type 0x%x.\n", (unsigned long long)ni->mft_no, type); if (!base_ni) { /* First call happens with the base mft record. */ @@ -1835,6 +1979,7 @@ find_attr_list_attr: /* Check for bogus calls. */ if (name || name_len || val || val_len || lowest_vcn) { errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); return -1; } @@ -1865,9 +2010,9 @@ find_attr_list_attr: if (errno != ENOENT) return rc; - /* Not found?!? Absurd! Must be a bug... )-: */ - ntfs_log_error("Extant attribute list wasn't found\n"); - errno = EINVAL; + /* Not found?!? Absurd! */ + errno = EIO; + ntfs_log_error("Attribute list wasn't found"); return -1; } } @@ -1968,7 +2113,7 @@ is_enumeration: if (MSEQNO_LE(al_entry->mft_reference) != le16_to_cpu( ni->mrec->sequence_number)) { - ntfs_log_debug("Found stale mft reference in " + ntfs_log_error("Found stale mft reference in " "attribute list!\n"); break; } @@ -1982,10 +2127,8 @@ is_enumeration: /* We want an extent record. */ ni = ntfs_extent_inode_open(base_ni, al_entry->mft_reference); - if (!ni) { - ntfs_log_perror("Failed to map extent inode"); + if (!ni) break; - } ctx->ntfs_ino = ni; ctx->mrec = ni->mrec; } @@ -2054,8 +2197,8 @@ do_next_attr: ctx->mrec = ctx->base_mrec; ctx->attr = ctx->base_attr; } - ntfs_log_debug("Inode is corrupt.\n"); errno = EIO; + ntfs_log_perror("Inode is corrupt (%lld)", (long long)base_ni->mft_no); return -1; not_found: /* @@ -2181,22 +2324,60 @@ int ntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name, { ntfs_volume *vol; ntfs_inode *base_ni; + int ret = -1; + ntfs_log_enter("Entering for attribute type 0x%x\n", type); + if (!ctx || !ctx->mrec || !ctx->attr || (name && name != AT_UNNAMED && (!ctx->ntfs_ino || !(vol = ctx->ntfs_ino->vol) || !vol->upcase || !vol->upcase_len))) { errno = EINVAL; - return -1; + ntfs_log_perror("%s", __FUNCTION__); + goto out; } + 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 ntfs_attr_find(type, name, name_len, ic, val, val_len, - ctx); - return ntfs_external_attr_find(type, name, name_len, ic, lowest_vcn, - val, val_len, ctx); + ret = ntfs_attr_find(type, name, name_len, ic, val, val_len, ctx); + else + ret = ntfs_external_attr_find(type, name, name_len, ic, + lowest_vcn, val, val_len, ctx); +out: + ntfs_log_leave("\n"); + return ret; +} + +/** + * ntfs_attr_position - find given or next attribute type in an ntfs inode + * @type: attribute type to start lookup + * @ctx: search context with mft record and attribute to search from + * + * Find an attribute type in an ntfs inode or the next attribute which is not + * the AT_END attribute. Please see more details at ntfs_attr_lookup. + * + * Return 0 if the search was successful and -1 if not, with errno set to the + * error code. + * + * The following error codes are defined: + * EINVAL Invalid arguments. + * EIO I/O error or corrupt data structures found. + * ENOMEM Not enough memory to allocate necessary buffers. + * ENOSPC No attribute was found after 'type', only AT_END. + */ +int ntfs_attr_position(const ATTR_TYPES type, ntfs_attr_search_ctx *ctx) +{ + if (ntfs_attr_lookup(type, NULL, 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { + if (errno != ENOENT) + return -1; + if (ctx->attr->type == AT_END) { + errno = ENOSPC; + return -1; + } + } + return 0; } /** @@ -2313,6 +2494,7 @@ ATTR_DEF *ntfs_attr_find_in_attrdef(const ntfs_volume *vol, if (!vol || !vol->attrdef || !type) { errno = EINVAL; + ntfs_log_perror("%s: type=%d", __FUNCTION__, type); return NULL; } for (ad = vol->attrdef; (u8*)ad - (u8*)vol->attrdef < @@ -2326,8 +2508,8 @@ ATTR_DEF *ntfs_attr_find_in_attrdef(const ntfs_volume *vol, /* We have gone too far already. No point in continuing. */ break; } - /* Attribute not found?!? */ errno = ENOENT; + ntfs_log_perror("%s: type=%d", __FUNCTION__, type); return NULL; } @@ -2350,31 +2532,38 @@ int ntfs_attr_size_bounds_check(const ntfs_volume *vol, const ATTR_TYPES type, const s64 size) { ATTR_DEF *ad; + s64 min_size, max_size; if (size < 0) { errno = EINVAL; + ntfs_log_perror("%s: size=%lld", __FUNCTION__, + (long long)size); return -1; } /* - * $ATTRIBUTE_LIST should be not greater than 0x40000, but this is not - * listed in the AttrDef. + * $ATTRIBUTE_LIST shouldn't be greater than 0x40000, otherwise + * Windows would crash. This is not listed in the AttrDef. */ if (type == AT_ATTRIBUTE_LIST && size > 0x40000) { errno = ERANGE; + ntfs_log_perror("Too large attrlist (%lld)", (long long)size); return -1; } ad = ntfs_attr_find_in_attrdef(vol, type); if (!ad) return -1; - /* We found the attribute. - Do the bounds check. */ - if ((sle64_to_cpu(ad->min_size) && size < - sle64_to_cpu(ad->min_size)) || - ((sle64_to_cpu(ad->max_size) > 0) && size > - sle64_to_cpu(ad->max_size))) { - /* @size is out of range! */ + + min_size = sle64_to_cpu(ad->min_size); + max_size = sle64_to_cpu(ad->max_size); + + if ((min_size && (size < min_size)) || + ((max_size > 0) && (size > max_size))) { errno = ERANGE; + ntfs_log_perror("Attr type %d size check failed (min,size,max=" + "%lld,%lld,%lld)", type, (long long)min_size, + (long long)size, (long long)max_size); return -1; } return 0; @@ -2473,9 +2662,9 @@ int ntfs_make_room_for_attr(MFT_RECORD *m, u8 *pos, u32 size) size = (size + 7) & ~7; /* Rigorous consistency checks. */ - if (!m || !pos || pos < (u8*)m || pos + size > - (u8*)m + le32_to_cpu(m->bytes_allocated)) { + if (!m || !pos || pos < (u8*)m) { errno = EINVAL; + ntfs_log_perror("%s: pos=%p m=%p", __FUNCTION__, pos, m); return -1; } /* The -8 is for the attribute terminator. */ @@ -2489,7 +2678,8 @@ int ntfs_make_room_for_attr(MFT_RECORD *m, u8 *pos, u32 size) biu = le32_to_cpu(m->bytes_in_use); /* Do we have enough space? */ - if (biu + size > le32_to_cpu(m->bytes_allocated)) { + if (biu + size > le32_to_cpu(m->bytes_allocated) || + pos + size > (u8*)m + le32_to_cpu(m->bytes_allocated)) { errno = ENOSPC; ntfs_log_trace("No enough space in the MFT record\n"); return -1; @@ -2614,6 +2804,10 @@ int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, goto put_err_out; } } + if (type == AT_DATA && name == AT_UNNAMED) { + ni->data_size = size; + ni->allocated_size = (size + 7) & ~7; + } ntfs_inode_mark_dirty(ni); ntfs_attr_put_search_ctx(ctx); return offset; @@ -2664,9 +2858,9 @@ int ntfs_non_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, if (ntfs_attr_can_be_non_resident(ni->vol, type)) { if (errno == EPERM) - ntfs_log_trace("Attribute can't be non resident.\n"); + ntfs_log_perror("Attribute can't be non resident"); else - ntfs_log_trace("ntfs_attr_can_be_non_resident failed.\n"); + ntfs_log_perror("ntfs_attr_can_be_non_resident failed"); return -1; } @@ -2682,10 +2876,11 @@ int ntfs_non_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, if (!ntfs_attr_find(type, name, name_len, CASE_SENSITIVE, NULL, 0, ctx)) { err = EEXIST; - ntfs_log_trace("Attribute already present.\n"); + ntfs_log_perror("Attribute 0x%x already present", type); goto put_err_out; } if (errno != ENOENT) { + ntfs_log_perror("ntfs_attr_find failed"); err = EIO; goto put_err_out; } @@ -2700,7 +2895,7 @@ int ntfs_non_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, sizeof(a->compressed_size) : 0); if (ntfs_make_room_for_attr(ctx->mrec, (u8*) ctx->attr, length)) { err = errno; - ntfs_log_trace("Failed to make room for attribute.\n"); + ntfs_log_perror("Failed to make room for attribute"); goto put_err_out; } @@ -2738,9 +2933,8 @@ int ntfs_non_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, if (type != AT_ATTRIBUTE_LIST && NInoAttrList(base_ni)) { if (ntfs_attrlist_entry_add(ni, a)) { err = errno; + ntfs_log_perror("Failed add attr entry to attrlist"); ntfs_attr_record_resize(m, a, 0); - ntfs_log_trace("Failed add attribute entry to " - "ATTRIBUTE_LIST.\n"); goto put_err_out; } } @@ -2753,8 +2947,7 @@ int ntfs_non_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, ntfs_attr_reinit_search_ctx(ctx); if (ntfs_attr_lookup(type, name, name_len, CASE_SENSITIVE, lowest_vcn, NULL, 0, ctx)) { - ntfs_log_trace("Attribute lookup failed. Probably leaving inconstant " - "metadata.\n"); + ntfs_log_perror("%s: attribute lookup failed", __FUNCTION__); ntfs_attr_put_search_ctx(ctx); return -1; @@ -2835,7 +3028,7 @@ int ntfs_attr_record_rm(ntfs_attr_search_ctx *ctx) NInoAttrListClearDirty(base_ni); } - /* Free MFT record, if it isn't contain attributes. */ + /* Free MFT record, if it doesn't contain attributes. */ if (le32_to_cpu(ctx->mrec->bytes_in_use) - le16_to_cpu(ctx->mrec->attrs_offset) == 8) { if (ntfs_mft_record_free(ni->vol, ni)) { @@ -2931,29 +3124,27 @@ int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type, u32 attr_rec_size; int err, i, offset; BOOL is_resident; + BOOL can_be_non_resident = FALSE; ntfs_inode *attr_ni; ntfs_attr *na; if (!ni || size < 0 || type == AT_ATTRIBUTE_LIST) { - ntfs_log_trace("Invalid arguments passed.\n"); errno = EINVAL; + ntfs_log_perror("%s: ni=%p size=%lld", __FUNCTION__, ni, + (long long)size); return -1; } - ntfs_log_trace("Entering for inode 0x%llx, attr %x, size %lld.\n", - (long long) ni->mft_no, type, size); + ntfs_log_trace("Entering for inode %lld, attr %x, size %lld.\n", + (long long)ni->mft_no, type, (long long)size); if (ni->nr_extents == -1) ni = ni->base_ni; /* Check the attribute type and the size. */ if (ntfs_attr_size_bounds_check(ni->vol, type, size)) { - if (errno == ERANGE) { - ntfs_log_trace("Size bounds check failed. Aborting...\n"); - } else if (errno == ENOENT) { - ntfs_log_trace("Invalid attribute type. Aborting...\n"); + if (errno == ENOENT) errno = EIO; - } return -1; } @@ -2961,22 +3152,23 @@ int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type, if (ntfs_attr_can_be_non_resident(ni->vol, type)) { if (errno != EPERM) { err = errno; - ntfs_log_trace("ntfs_attr_can_be_non_resident failed.\n"); + ntfs_log_perror("ntfs_attr_can_be_non_resident failed"); goto err_out; } /* @val is mandatory. */ if (!val) { - ntfs_log_trace("val is mandatory for always resident " - "attributes.\n"); errno = EINVAL; + ntfs_log_perror("val is mandatory for always resident " + "attributes"); return -1; } if (size > ni->vol->mft_record_size) { - ntfs_log_trace("Attribute is too big.\n"); errno = ERANGE; + ntfs_log_perror("Attribute is too big"); return -1; } - } + } else + can_be_non_resident = TRUE; /* * Determine resident or not will be new attribute. We add 8 to size in @@ -2987,7 +3179,7 @@ int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type, } else { if (errno != EPERM) { err = errno; - ntfs_log_trace("ntfs_attr_can_be_resident failed.\n"); + ntfs_log_perror("ntfs_attr_can_be_resident failed"); goto err_out; } is_resident = FALSE; @@ -3014,7 +3206,7 @@ int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type, /* Try to add to extent inodes. */ if (ntfs_inode_attach_all_extents(ni)) { err = errno; - ntfs_log_trace("Failed to attach all extents to inode.\n"); + ntfs_log_perror("Failed to attach all extents to inode"); goto err_out; } for (i = 0; i < ni->nr_extents; i++) { @@ -3030,7 +3222,7 @@ int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type, /* Add attribute list not present, add it and retry. */ if (ntfs_inode_add_attrlist(ni)) { err = errno; - ntfs_log_trace("Failed to add attribute list.\n"); + ntfs_log_perror("Failed to add attribute list"); goto err_out; } return ntfs_attr_add(ni, type, name, name_len, val, size); @@ -3039,7 +3231,7 @@ int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type, attr_ni = ntfs_mft_record_alloc(ni->vol, ni); if (!attr_ni) { err = errno; - ntfs_log_trace("Failed to allocate extent record.\n"); + ntfs_log_perror("Failed to allocate extent record"); goto err_out; } @@ -3049,19 +3241,22 @@ add_attr_record: offset = ntfs_resident_attr_record_add(attr_ni, type, name, name_len, val, size, 0); if (offset < 0) { + if (errno == ENOSPC && can_be_non_resident) + goto add_non_resident; err = errno; - ntfs_log_trace("Failed to add resident attribute.\n"); + ntfs_log_perror("Failed to add resident attribute"); goto free_err_out; } return 0; } +add_non_resident: /* Add non resident attribute. */ offset = ntfs_non_resident_attr_record_add(attr_ni, type, name, name_len, 0, 8, 0); if (offset < 0) { err = errno; - ntfs_log_trace("Failed to add non resident attribute.\n"); + ntfs_log_perror("Failed to add non resident attribute"); goto free_err_out; } @@ -3073,17 +3268,16 @@ add_attr_record: na = ntfs_attr_open(ni, type, name, name_len); if (!na) { err = errno; - ntfs_log_trace("Failed to open just added attribute.\n"); + ntfs_log_perror("Failed to open just added attribute"); goto rm_attr_err_out; } /* Resize and set attribute value. */ if (ntfs_attr_truncate(na, size) || (val && (ntfs_attr_pwrite(na, 0, size, val) != size))) { err = errno; - ntfs_log_trace("Failed to initialize just added attribute.\n"); + ntfs_log_perror("Failed to initialize just added attribute"); if (ntfs_attr_rm(na)) - ntfs_log_trace("Failed to remove just added attribute. " - "Probably leaving inconstant metadata.\n"); + ntfs_log_perror("Failed to remove just added attribute"); ntfs_attr_close(na); goto err_out; } @@ -3093,18 +3287,14 @@ add_attr_record: rm_attr_err_out: /* Remove just added attribute. */ if (ntfs_attr_record_resize(attr_ni->mrec, - (ATTR_RECORD*)((u8*)attr_ni->mrec + offset), 0)) { - ntfs_log_trace("Failed to remove just added attribute.\n"); - } + (ATTR_RECORD*)((u8*)attr_ni->mrec + offset), 0)) + ntfs_log_perror("Failed to remove just added attribute #2"); free_err_out: - /* Free MFT record, if it isn't contain attributes. */ + /* Free MFT record, if it doesn't contain attributes. */ if (le32_to_cpu(attr_ni->mrec->bytes_in_use) - - le32_to_cpu(attr_ni->mrec->attrs_offset) == 8) { - if (ntfs_mft_record_free(attr_ni->vol, attr_ni)) { - ntfs_log_trace("Failed to free MFT record. Leaving " - "inconstant metadata.\n"); - } - } + le16_to_cpu(attr_ni->mrec->attrs_offset) == 8) + if (ntfs_mft_record_free(attr_ni->vol, attr_ni)) + ntfs_log_perror("Failed to free MFT record"); err_out: errno = err; return -1; @@ -3157,6 +3347,7 @@ int ntfs_attr_rm(ntfs_attr *na) } ntfs_attr_reinit_search_ctx(ctx); } + ntfs_attr_put_search_ctx(ctx); if (errno != ENOENT) { ntfs_log_trace("Attribute lookup failed. Probably leaving inconstant " "metadata.\n"); @@ -3210,6 +3401,14 @@ int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size) "(%u > %u)\n", new_muse, alloc_size); return -1; } + + if (a->type == AT_INDEX_ROOT && new_size > attr_size && + new_muse + 120 > alloc_size && old_size + 120 <= alloc_size) { + errno = ENOSPC; + ntfs_log_trace("Too big INDEX_ROOT (%u > %u)\n", + new_muse, alloc_size); + return STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT; + } /* Move attributes following @a to their new location. */ memmove((u8 *)a + new_size, (u8 *)a + attr_size, @@ -3242,12 +3441,14 @@ int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size) int ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a, const u32 new_size) { + int ret; + ntfs_log_trace("Entering for new size %u.\n", (unsigned)new_size); /* Resize the resident part of the attribute record. */ - if (ntfs_attr_record_resize(m, a, (le16_to_cpu(a->value_offset) + - new_size + 7) & ~7) < 0) - return -1; + if ((ret = ntfs_attr_record_resize(m, a, (le16_to_cpu(a->value_offset) + + new_size + 7) & ~7)) < 0) + return ret; /* * If we made the attribute value bigger, clear the area between the * old size and @new_size. @@ -3302,10 +3503,9 @@ int ntfs_attr_record_move_to(ntfs_attr_search_ctx *ctx, ntfs_inode *ni) /* Find place in MFT record where attribute will be moved. */ a = ctx->attr; nctx = ntfs_attr_get_search_ctx(ni, NULL); - if (!nctx) { - ntfs_log_trace("Couldn't obtain search context.\n"); + if (!nctx) return -1; - } + /* * Use ntfs_attr_find instead of ntfs_attr_lookup to find place for * attribute in @ni->mrec, not any extent inode in case if @ni is base @@ -3374,14 +3574,15 @@ int ntfs_attr_record_move_away(ntfs_attr_search_ctx *ctx, int extra) int i; if (!ctx || !ctx->attr || !ctx->ntfs_ino || extra < 0) { - ntfs_log_trace("Invalid arguments passed.\n"); errno = EINVAL; + ntfs_log_perror("%s: ctx=%p ctx->attr=%p extra=%d", __FUNCTION__, + ctx, ctx ? ctx->attr : NULL, extra); return -1; } - ntfs_log_trace("Entering for attr 0x%x, inode 0x%llx.\n", + ntfs_log_trace("Entering for attr 0x%x, inode %llu\n", (unsigned) le32_to_cpu(ctx->attr->type), - (long long) ctx->ntfs_ino->mft_no); + (unsigned long long)ctx->ntfs_ino->mft_no); if (ctx->ntfs_ino->nr_extents == -1) base_ni = ctx->base_ntfs_ino; @@ -3389,14 +3590,15 @@ int ntfs_attr_record_move_away(ntfs_attr_search_ctx *ctx, int extra) base_ni = ctx->ntfs_ino; if (!NInoAttrList(base_ni)) { - ntfs_log_trace("Inode should contain attribute list to use this " - "function.\n"); errno = EINVAL; + ntfs_log_perror("Inode %llu has no attrlist", + (unsigned long long)base_ni->mft_no); return -1; } if (ntfs_inode_attach_all_extents(ctx->ntfs_ino)) { - ntfs_log_trace("Couldn't attach extent inode.\n"); + ntfs_log_perror("Couldn't attach extents, inode=%llu", + (unsigned long long)base_ni->mft_no); return -1; } @@ -3428,11 +3630,11 @@ int ntfs_attr_record_move_away(ntfs_attr_search_ctx *ctx, int extra) */ ni = ntfs_mft_record_alloc(base_ni->vol, base_ni); if (!ni) { - ntfs_log_trace("Couldn't allocate new MFT record.\n"); + ntfs_log_perror("Couldn't allocate MFT record"); return -1; } if (ntfs_attr_record_move_to(ctx, ni)) { - ntfs_log_trace("Couldn't move attribute to new MFT record.\n"); + ntfs_log_perror("Couldn't move attribute to MFT record"); return -1; } return 0; @@ -3488,12 +3690,8 @@ static int ntfs_attr_make_non_resident(ntfs_attr *na, /* Start by allocating clusters to hold the attribute value. */ rl = ntfs_cluster_alloc(vol, 0, new_allocated_size >> vol->cluster_size_bits, -1, DATA_ZONE); - if (!rl) { - if (errno != ENOSPC) - ntfs_log_trace("Eeek! Failed to allocate " - "cluster(s). Aborting...\n"); + if (!rl) return -1; - } } else rl = NULL; /* @@ -3606,6 +3804,9 @@ cluster_free_err_out: return -1; } + +static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize); + /** * ntfs_resident_attr_resize - resize a resident, open ntfs attribute * @na: resident ntfs attribute to resize @@ -3622,7 +3823,7 @@ cluster_free_err_out: * ERANGE - @newsize is not valid for the attribute type of @na. * ENOSPC - There is no enough space in base mft to resize $ATTRIBUTE_LIST. */ -static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize) +static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize) { ntfs_attr_search_ctx *ctx; ntfs_volume *vol; @@ -3640,6 +3841,7 @@ static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize) if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0, 0, NULL, 0, ctx)) { err = errno; + ntfs_log_perror("ntfs_attr_lookup failed"); goto put_err_out; } vol = na->ni->vol; @@ -3649,11 +3851,9 @@ static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize) */ if (ntfs_attr_size_bounds_check(vol, na->type, newsize) < 0) { err = errno; - if (err == ERANGE) { - ntfs_log_trace("Eeek! Size bounds check failed. " - "Aborting...\n"); - } else if (err == ENOENT) + if (err == ENOENT) err = EIO; + ntfs_log_perror("%s: bounds check failed", __FUNCTION__); goto put_err_out; } /* @@ -3663,8 +3863,8 @@ static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize) */ if (newsize < vol->mft_record_size) { /* Perform the resize of the attribute record. */ - if (!ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr, - newsize)) { + if (!(ret = ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr, + newsize))) { /* Update attribute size everywhere. */ na->data_size = na->initialized_size = newsize; na->allocated_size = (newsize + 7) & ~7; @@ -3677,6 +3877,11 @@ static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize) } goto resize_done; } + /* Prefer AT_INDEX_ALLOCATION instead of AT_ATTRIBUTE_LIST */ + if (ret == STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT) { + err = errno; + goto put_err_out; + } } /* There is not enough space in the mft record to perform the resize. */ @@ -3688,8 +3893,7 @@ static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize) return ntfs_attr_truncate(na, newsize); } else if (errno != ENOSPC && errno != EPERM) { err = errno; - ntfs_log_trace("Eeek! Failed to make attribute non-resident. " - "Aborting...\n"); + ntfs_log_perror("Failed to make attribute non-resident"); goto put_err_out; } @@ -3716,7 +3920,7 @@ static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize) le16_to_cpu(a->name_offset)), a->name_length); if (!tna) { err = errno; - ntfs_log_trace("Couldn't open attribute.\n"); + ntfs_log_perror("Couldn't open attribute"); goto put_err_out; } if (ntfs_attr_make_non_resident(tna, ctx)) { @@ -3731,17 +3935,19 @@ static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize) /* Check whether error occurred. */ if (errno != ENOENT) { err = errno; - ntfs_log_trace("Attribute lookup failed.\n"); + ntfs_log_perror("%s: Attribute lookup failed 1", __FUNCTION__); goto put_err_out; } - - /* We can't move out attribute list, thus move out others. */ - if (na->type == AT_ATTRIBUTE_LIST) { + + /* + * The standard information and attribute list attributes can't be + * moved out from the base MFT record, so try to move out others. + */ + if (na->type==AT_STANDARD_INFORMATION || na->type==AT_ATTRIBUTE_LIST) { ntfs_attr_put_search_ctx(ctx); if (ntfs_inode_free_space(na->ni, offsetof(ATTR_RECORD, non_resident_end) + 8)) { - ntfs_log_trace("Couldn't free space in the MFT record to " - "make attribute list non resident.\n"); + ntfs_log_perror("Could not free space in MFT record"); return -1; } return ntfs_resident_attr_resize(na, newsize); @@ -3756,7 +3962,7 @@ static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize) ntfs_attr_init_search_ctx(ctx, na->ni, NULL); if (ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE, 0, NULL, 0, ctx)) { - ntfs_log_trace("Attribute lookup failed.\n"); + ntfs_log_perror("%s: Attribute lookup failed 2", __FUNCTION__); err = errno; goto put_err_out; } @@ -3789,13 +3995,13 @@ static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize) ni = ntfs_mft_record_alloc(vol, ni); if (!ni) { err = errno; - ntfs_log_trace("Couldn't allocate new MFT record.\n"); + ntfs_log_perror("Couldn't allocate new MFT record"); goto put_err_out; } /* Move attribute to it. */ if (ntfs_attr_record_move_to(ctx, ni)) { err = errno; - ntfs_log_trace("Couldn't move attribute to new MFT record.\n"); + ntfs_log_perror("Couldn't move attribute to new MFT record"); goto put_err_out; } /* Update ntfs attribute. */ @@ -3812,7 +4018,6 @@ resize_done: * written out later. */ ntfs_inode_mark_dirty(ctx->ntfs_ino); - /* Done! */ ntfs_attr_put_search_ctx(ctx); return 0; put_err_out: @@ -3821,6 +4026,16 @@ put_err_out: return ret; } +static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize) +{ + int ret; + + ntfs_log_enter("Entering\n"); + ret = ntfs_resident_attr_resize_i(na, newsize); + ntfs_log_leave("\n"); + return ret; +} + /** * ntfs_attr_make_resident - convert a non-resident to a resident attribute * @na: open ntfs attribute to make resident @@ -3988,7 +4203,412 @@ static int ntfs_attr_make_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx) return 0; } +/* + * If we are in the first extent, then set/clean sparse bit, + * update allocated and compressed size. + */ +static int ntfs_attr_update_meta(ATTR_RECORD *a, ntfs_attr *na, MFT_RECORD *m, + ntfs_attr_search_ctx *ctx) +{ + int sparse, ret = 0; + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x\n", + (unsigned long long)na->ni->mft_no, na->type); + + if (a->lowest_vcn) + goto out; + + a->allocated_size = cpu_to_sle64(na->allocated_size); + + /* Update sparse bit. */ + sparse = ntfs_rl_sparse(na->rl); + if (sparse == -1) { + errno = EIO; + goto error; + } + + /* Attribute become sparse. */ + if (sparse && !(a->flags & (ATTR_IS_SPARSE | ATTR_IS_COMPRESSED))) { + /* + * Move attribute to another mft record, if attribute is too + * small to add compressed_size field to it and we have no + * free space in the current mft record. + */ + if ((le32_to_cpu(a->length) - + le16_to_cpu(a->mapping_pairs_offset) == 8) + && !(le32_to_cpu(m->bytes_allocated) - + le32_to_cpu(m->bytes_in_use))) { + + if (!NInoAttrList(na->ni)) { + ntfs_attr_put_search_ctx(ctx); + if (ntfs_inode_add_attrlist(na->ni)) + goto leave; + goto retry; + } + if (ntfs_attr_record_move_away(ctx, 8)) { + ntfs_log_perror("Failed to move attribute"); + goto error; + } + ntfs_attr_put_search_ctx(ctx); + goto retry; + } + if (!(le32_to_cpu(a->length) - le16_to_cpu( + a->mapping_pairs_offset))) { + errno = EIO; + ntfs_log_perror("Mapping pairs space is 0"); + goto error; + } + + NAttrSetSparse(na); + a->flags |= ATTR_IS_SPARSE; + a->compression_unit = 4; /* Windows set it so, even if attribute + is not actually compressed. */ + + memmove((u8*)a + le16_to_cpu(a->name_offset) + 8, + (u8*)a + le16_to_cpu(a->name_offset), + a->name_length * sizeof(ntfschar)); + + a->name_offset = cpu_to_le16(le16_to_cpu(a->name_offset) + 8); + + a->mapping_pairs_offset = + cpu_to_le16(le16_to_cpu(a->mapping_pairs_offset) + 8); + } + + /* Attribute no longer sparse. */ + if (!sparse && (a->flags & ATTR_IS_SPARSE) && + !(a->flags & ATTR_IS_COMPRESSED)) { + + NAttrClearSparse(na); + a->flags &= ~ATTR_IS_SPARSE; + a->compression_unit = 0; + + memmove((u8*)a + le16_to_cpu(a->name_offset) - 8, + (u8*)a + le16_to_cpu(a->name_offset), + a->name_length * sizeof(ntfschar)); + + if (le16_to_cpu(a->name_offset) >= 8) + a->name_offset = cpu_to_le16(le16_to_cpu(a->name_offset) - 8); + + a->mapping_pairs_offset = + cpu_to_le16(le16_to_cpu(a->mapping_pairs_offset) - 8); + } + + /* Update compressed size if required. */ + if (sparse) { + s64 new_compr_size; + + new_compr_size = ntfs_rl_get_compressed_size(na->ni->vol, na->rl); + if (new_compr_size == -1) + goto error; + + na->compressed_size = new_compr_size; + a->compressed_size = cpu_to_sle64(new_compr_size); + } + /* + * Set FILE_NAME dirty flag, to update sparse bit and + * allocated size in the index. + */ + if (na->type == AT_DATA && na->name == AT_UNNAMED) { + if (sparse) + na->ni->allocated_size = na->compressed_size; + else + na->ni->allocated_size = na->allocated_size; + NInoFileNameSetDirty(na->ni); + } +out: + return ret; +leave: ret = -1; goto out; /* return -1 */ +retry: ret = -2; goto out; +error: ret = -3; goto out; +} + #define NTFS_VCN_DELETE_MARK -2 +/** + * ntfs_attr_update_mapping_pairs_i - see ntfs_attr_update_mapping_pairs + */ +static int ntfs_attr_update_mapping_pairs_i(ntfs_attr *na, VCN from_vcn) +{ + ntfs_attr_search_ctx *ctx; + ntfs_inode *ni, *base_ni; + MFT_RECORD *m; + ATTR_RECORD *a; + VCN stop_vcn; + int err, mp_size, cur_max_mp_size, exp_max_mp_size, ret = -1; + BOOL finished_build; +retry: + if (!na || !na->rl || from_vcn) { + errno = EINVAL; + ntfs_log_perror("%s: na=%p", __FUNCTION__, na); + return -1; + } + + ntfs_log_trace("Entering for inode %llu, attr 0x%x\n", + (unsigned long long)na->ni->mft_no, na->type); + + if (!NAttrNonResident(na)) { + errno = EINVAL; + ntfs_log_perror("%s: resident attribute", __FUNCTION__); + return -1; + } + + if (na->ni->nr_extents == -1) + base_ni = na->ni->base_ni; + else + base_ni = na->ni; + + ctx = ntfs_attr_get_search_ctx(base_ni, NULL); + if (!ctx) + return -1; + + /* Fill attribute records with new mapping pairs. */ + stop_vcn = 0; + finished_build = FALSE; + while (!ntfs_attr_lookup(na->type, na->name, na->name_len, + CASE_SENSITIVE, from_vcn, NULL, 0, ctx)) { + a = ctx->attr; + m = ctx->mrec; + /* + * If runlist is updating not from the beginning, then set + * @stop_vcn properly, i.e. to the lowest vcn of record that + * contain @from_vcn. Also we do not need @from_vcn anymore, + * set it to 0 to make ntfs_attr_lookup enumerate attributes. + */ + if (from_vcn) { + LCN first_lcn; + + stop_vcn = sle64_to_cpu(a->lowest_vcn); + from_vcn = 0; + /* + * Check whether the first run we need to update is + * the last run in runlist, if so, then deallocate + * all attrubute extents starting this one. + */ + first_lcn = ntfs_rl_vcn_to_lcn(na->rl, stop_vcn); + if (first_lcn == LCN_EINVAL) { + errno = EIO; + ntfs_log_perror("Bad runlist"); + goto put_err_out; + } + if (first_lcn == LCN_ENOENT || + first_lcn == LCN_RL_NOT_MAPPED) + finished_build = TRUE; + } + + /* + * Check whether we finished mapping pairs build, if so mark + * extent as need to delete (by setting highest vcn to + * NTFS_VCN_DELETE_MARK (-2), we shall check it later and + * delete extent) and continue search. + */ + if (finished_build) { + ntfs_log_trace("Mark attr 0x%x for delete in inode " + "%lld.\n", (unsigned)le32_to_cpu(a->type), + (long long)ctx->ntfs_ino->mft_no); + a->highest_vcn = cpu_to_sle64(NTFS_VCN_DELETE_MARK); + ntfs_inode_mark_dirty(ctx->ntfs_ino); + continue; + } + + switch (ntfs_attr_update_meta(a, na, m, ctx)) { + case -1: return -1; + case -2: goto retry; + case -3: goto put_err_out; + } + + /* Get the size for the rest of mapping pairs array. */ + mp_size = ntfs_get_size_for_mapping_pairs(na->ni->vol, na->rl, + stop_vcn); + if (mp_size <= 0) { + ntfs_log_perror("%s: get MP size failed", __FUNCTION__); + goto put_err_out; + } + /* + * Determine maximum possible length of mapping pairs, + * if we shall *not* expand space for mapping pairs. + */ + cur_max_mp_size = le32_to_cpu(a->length) - + le16_to_cpu(a->mapping_pairs_offset); + /* + * Determine maximum possible length of mapping pairs in the + * current mft record, if we shall expand space for mapping + * pairs. + */ + exp_max_mp_size = le32_to_cpu(m->bytes_allocated) - + le32_to_cpu(m->bytes_in_use) + cur_max_mp_size; + /* Test mapping pairs for fitting in the current mft record. */ + if (mp_size > exp_max_mp_size) { + /* + * Mapping pairs of $ATTRIBUTE_LIST attribute must fit + * in the base mft record. Try to move out other + * attributes and try again. + */ + if (na->type == AT_ATTRIBUTE_LIST) { + ntfs_attr_put_search_ctx(ctx); + if (ntfs_inode_free_space(na->ni, mp_size - + cur_max_mp_size)) { + ntfs_log_perror("Attribute list is too " + "big. Defragment the " + "volume\n"); + return -1; + } + goto retry; + } + + /* Add attribute list if it isn't present, and retry. */ + if (!NInoAttrList(base_ni)) { + ntfs_attr_put_search_ctx(ctx); + if (ntfs_inode_add_attrlist(base_ni)) { + ntfs_log_perror("Can not add attrlist"); + return -1; + } + goto retry; + } + + /* + * Set mapping pairs size to maximum possible for this + * mft record. We shall write the rest of mapping pairs + * to another MFT records. + */ + mp_size = exp_max_mp_size; + } + + /* Change space for mapping pairs if we need it. */ + if (((mp_size + 7) & ~7) != cur_max_mp_size) { + if (ntfs_attr_record_resize(m, a, + le16_to_cpu(a->mapping_pairs_offset) + + mp_size)) { + errno = EIO; + ntfs_log_perror("Failed to resize attribute"); + goto put_err_out; + } + } + + /* Update lowest vcn. */ + a->lowest_vcn = cpu_to_sle64(stop_vcn); + ntfs_inode_mark_dirty(ctx->ntfs_ino); + if ((ctx->ntfs_ino->nr_extents == -1 || + NInoAttrList(ctx->ntfs_ino)) && + ctx->attr->type != AT_ATTRIBUTE_LIST) { + ctx->al_entry->lowest_vcn = cpu_to_sle64(stop_vcn); + ntfs_attrlist_mark_dirty(ctx->ntfs_ino); + } + + /* + * Generate the new mapping pairs array directly into the + * correct destination, i.e. the attribute record itself. + */ + if (!ntfs_mapping_pairs_build(na->ni->vol, (u8*)a + le16_to_cpu( + a->mapping_pairs_offset), mp_size, na->rl, + stop_vcn, &stop_vcn)) + finished_build = TRUE; + if (!finished_build && errno != ENOSPC) { + ntfs_log_perror("Failed to build mapping pairs"); + goto put_err_out; + } + a->highest_vcn = cpu_to_sle64(stop_vcn - 1); + } + /* Check whether error occurred. */ + if (errno != ENOENT) { + ntfs_log_perror("%s: Attribute lookup failed", __FUNCTION__); + goto put_err_out; + } + + /* Deallocate not used attribute extents and return with success. */ + if (finished_build) { + ntfs_attr_reinit_search_ctx(ctx); + ntfs_log_trace("Deallocate marked extents.\n"); + while (!ntfs_attr_lookup(na->type, na->name, na->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + if (sle64_to_cpu(ctx->attr->highest_vcn) != + NTFS_VCN_DELETE_MARK) + continue; + /* Remove unused attribute record. */ + if (ntfs_attr_record_rm(ctx)) { + ntfs_log_perror("Could not remove unused attr"); + goto put_err_out; + } + ntfs_attr_reinit_search_ctx(ctx); + } + if (errno != ENOENT) { + ntfs_log_perror("%s: Attr lookup failed", __FUNCTION__); + goto put_err_out; + } + ntfs_log_trace("Deallocate done.\n"); + ntfs_attr_put_search_ctx(ctx); + goto ok; + } + ntfs_attr_put_search_ctx(ctx); + ctx = NULL; + + /* Allocate new MFT records for the rest of mapping pairs. */ + while (1) { + /* Calculate size of rest mapping pairs. */ + mp_size = ntfs_get_size_for_mapping_pairs(na->ni->vol, + na->rl, stop_vcn); + if (mp_size <= 0) { + ntfs_log_perror("%s: get mp size failed", __FUNCTION__); + goto put_err_out; + } + /* Allocate new mft record. */ + ni = ntfs_mft_record_alloc(na->ni->vol, base_ni); + if (!ni) { + ntfs_log_perror("Could not allocate new MFT record"); + goto put_err_out; + } + m = ni->mrec; + /* + * If mapping size exceed available space, set them to + * possible maximum. + */ + cur_max_mp_size = le32_to_cpu(m->bytes_allocated) - + le32_to_cpu(m->bytes_in_use) - + (offsetof(ATTR_RECORD, compressed_size) + + ((NAttrCompressed(na) || NAttrSparse(na)) ? + sizeof(a->compressed_size) : 0)) - + ((sizeof(ntfschar) * na->name_len + 7) & ~7); + if (mp_size > cur_max_mp_size) + mp_size = cur_max_mp_size; + /* Add attribute extent to new record. */ + err = ntfs_non_resident_attr_record_add(ni, na->type, + na->name, na->name_len, stop_vcn, mp_size, 0); + if (err == -1) { + err = errno; + ntfs_log_perror("Could not add attribute extent"); + if (ntfs_mft_record_free(na->ni->vol, ni)) + ntfs_log_perror("Could not free MFT record"); + errno = err; + goto put_err_out; + } + a = (ATTR_RECORD*)((u8*)m + err); + + err = ntfs_mapping_pairs_build(na->ni->vol, (u8*)a + + le16_to_cpu(a->mapping_pairs_offset), mp_size, na->rl, + stop_vcn, &stop_vcn); + if (err < 0 && errno != ENOSPC) { + err = errno; + ntfs_log_perror("Failed to build MP"); + if (ntfs_mft_record_free(na->ni->vol, ni)) + ntfs_log_perror("Couldn't free MFT record"); + errno = err; + goto put_err_out; + } + a->highest_vcn = cpu_to_sle64(stop_vcn - 1); + ntfs_inode_mark_dirty(ni); + /* All mapping pairs has been written. */ + if (!err) + break; + } +ok: + ret = 0; +out: + return ret; +put_err_out: + if (ctx) + ntfs_attr_put_search_ctx(ctx); + goto out; +} +#undef NTFS_VCN_DELETE_MARK + /** * ntfs_attr_update_mapping_pairs - update mapping pairs for ntfs attribute * @na: non-resident ntfs open attribute for which we need update @@ -4016,428 +4636,13 @@ static int ntfs_attr_make_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx) */ int ntfs_attr_update_mapping_pairs(ntfs_attr *na, VCN from_vcn) { - ntfs_attr_search_ctx *ctx; - ntfs_inode *ni, *base_ni; - MFT_RECORD *m; - ATTR_RECORD *a; - VCN stop_vcn; - int err, mp_size, cur_max_mp_size, exp_max_mp_size, ret = -1; - BOOL finished_build; - -retry: - if (!na || !na->rl || from_vcn) { - ntfs_log_trace("Invalid parameters passed.\n"); - errno = EINVAL; - return -1; - } - - if (!NAttrNonResident(na)) { - ntfs_log_trace("Attribute should be non resident.\n"); - errno = EINVAL; - return -1; - } - - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (unsigned long - long)na->ni->mft_no, na->type); - - if (na->ni->nr_extents == -1) - base_ni = na->ni->base_ni; - else - base_ni = na->ni; - - ctx = ntfs_attr_get_search_ctx(base_ni, NULL); - if (!ctx) { - ntfs_log_trace("Couldn't get search context.\n"); - return -1; - } - - /* Fill attribute records with new mapping pairs. */ - stop_vcn = 0; - finished_build = FALSE; - while (!ntfs_attr_lookup(na->type, na->name, na->name_len, - CASE_SENSITIVE, from_vcn, NULL, 0, ctx)) { - a = ctx->attr; - m = ctx->mrec; - /* - * If runlist is updating not from the beginning, then set - * @stop_vcn properly, i.e. to the lowest vcn of record that - * contain @from_vcn. Also we do not need @from_vcn anymore, - * set it to 0 to make ntfs_attr_lookup enumerate attributes. - */ - if (from_vcn) { - LCN first_lcn; - - stop_vcn = sle64_to_cpu(a->lowest_vcn); - from_vcn = 0; - /* - * Check whether the first run we need to update is - * the last run in runlist, if so, then deallocate - * all attrubute extents starting this one. - */ - first_lcn = ntfs_rl_vcn_to_lcn(na->rl, stop_vcn); - if (first_lcn == LCN_EINVAL) { - ntfs_log_trace("BUG! Incorrect runlist.\n"); - err = EIO; - goto put_err_out; - } - if (first_lcn == LCN_ENOENT || - first_lcn == LCN_RL_NOT_MAPPED) - finished_build = TRUE; - } - - /* - * Check whether we finished mapping pairs build, if so mark - * extent as need to delete (by setting highest vcn to - * NTFS_VCN_DELETE_MARK (-2), we shall check it later and - * delete extent) and continue search. - */ - if (finished_build) { - ntfs_log_trace("Mark attr 0x%x for delete in inode " - "0x%llx.\n", (unsigned)le32_to_cpu( - a->type), ctx->ntfs_ino->mft_no); - a->highest_vcn = cpu_to_sle64(NTFS_VCN_DELETE_MARK); - ntfs_inode_mark_dirty(ctx->ntfs_ino); - continue; - } - - /* - * If we in the first extent, then set/clean sparse bit, - * update allocated and compressed size. - */ - if (!a->lowest_vcn) { - int sparse; - - /* Update allocated size. */ - a->allocated_size = cpu_to_sle64(na->allocated_size); - /* Update sparse bit. */ - sparse = ntfs_rl_sparse(na->rl); - if (sparse == -1) { - ntfs_log_trace("Bad runlist.\n"); - err = EIO; - goto put_err_out; - } - /* Attribute become sparse. */ - if (sparse && !(a->flags & (ATTR_IS_SPARSE | - ATTR_IS_COMPRESSED))) { - /* - * We need to move attribute to another mft - * record, if attribute is to small to add - * compressed_size field to it and we have no - * free space in the current mft record. - */ - if ((le32_to_cpu(a->length) - le16_to_cpu( - a->mapping_pairs_offset) - == 8) && !(le32_to_cpu( - m->bytes_allocated) - - le32_to_cpu(m->bytes_in_use))) { - if (!NInoAttrList(na->ni)) { - ntfs_attr_put_search_ctx(ctx); - if (ntfs_inode_add_attrlist( - na->ni)) - return -1; - goto retry; - } - if (ntfs_attr_record_move_away(ctx, - 8)) { - ntfs_log_trace("Failed to move " - "attribute to another " - "extent. Aborting..\n"); - err = errno; - goto put_err_out; - } - ntfs_attr_put_search_ctx(ctx); - goto retry; - } - if (!(le32_to_cpu(a->length) - le16_to_cpu( - a->mapping_pairs_offset))) { - ntfs_log_trace("Size of the space " - "allocated for mapping " - "pairs should not be 0." - " Aborting ...\n"); - err = EIO; - goto put_err_out; - } - NAttrSetSparse(na); - a->flags |= ATTR_IS_SPARSE; - a->compression_unit = 4; /* Windows set it so, - even if attribute - is not actually - compressed. */ - memmove((u8*)a + le16_to_cpu(a->name_offset) + - 8, (u8*)a + le16_to_cpu(a->name_offset), - a->name_length * sizeof(ntfschar)); - a->name_offset = cpu_to_le16(le16_to_cpu( - a->name_offset) + 8); - a->mapping_pairs_offset = - cpu_to_le16(le16_to_cpu( - a->mapping_pairs_offset) + 8); - } - /* Attribute no longer sparse. */ - if (!sparse && (a->flags & ATTR_IS_SPARSE) && - !(a->flags & ATTR_IS_COMPRESSED)) { - NAttrClearSparse(na); - a->flags &= ~ATTR_IS_SPARSE; - a->compression_unit = 0; - memmove((u8*)a + le16_to_cpu(a->name_offset) - - 8, (u8*)a + le16_to_cpu(a->name_offset), - a->name_length * sizeof(ntfschar)); - if (le16_to_cpu(a->name_offset) >= 8) - a->name_offset = cpu_to_le16( - le16_to_cpu(a->name_offset) - 8); - a->mapping_pairs_offset = - cpu_to_le16(le16_to_cpu( - a->mapping_pairs_offset) - 8); - } - /* Update compressed size if required. */ - if (sparse) { - s64 new_compr_size; - - new_compr_size = ntfs_rl_get_compressed_size( - na->ni->vol, na->rl); - if (new_compr_size == -1) { - err = errno; - ntfs_log_trace("BUG! Leaving inconstant" - " metadata.\n"); - goto put_err_out; - } - na->compressed_size = new_compr_size; - a->compressed_size = cpu_to_sle64( - new_compr_size); - } - /* - * Set FILE_NAME dirty flag, to update sparse bit and - * allocated size in the index. - */ - if (na->type == AT_DATA && na->name == AT_UNNAMED) { - if (sparse) - na->ni->allocated_size = - na->compressed_size; - else - na->ni->allocated_size = - na->allocated_size; - NInoFileNameSetDirty(na->ni); - } - } - /* Get the size for the rest of mapping pairs array. */ - mp_size = ntfs_get_size_for_mapping_pairs(na->ni->vol, na->rl, - stop_vcn); - if (mp_size <= 0) { - err = errno; - ntfs_log_trace("Get size for mapping pairs failed.\n"); - goto put_err_out; - } - /* - * Determine maximum possible length of mapping pairs, - * if we shall *not* expand space for mapping pairs. - */ - cur_max_mp_size = le32_to_cpu(a->length) - - le16_to_cpu(a->mapping_pairs_offset); - /* - * Determine maximum possible length of mapping pairs in the - * current mft record, if we shall expand space for mapping - * pairs. - */ - exp_max_mp_size = le32_to_cpu(m->bytes_allocated) - - le32_to_cpu(m->bytes_in_use) + cur_max_mp_size; - /* Test mapping pairs for fitting in the current mft record. */ - if (mp_size > exp_max_mp_size) { - /* - * Mapping pairs of $ATTRIBUTE_LIST attribute must fit - * in the base mft record. Try to move out other - * attributes and try again. - */ - if (na->type == AT_ATTRIBUTE_LIST) { - ntfs_attr_put_search_ctx(ctx); - if (ntfs_inode_free_space(na->ni, mp_size - - cur_max_mp_size)) { - if (errno != ENOSPC) - return -1; - ntfs_log_error("Attribute list mapping " - "pairs size to big, " - "can't fit them in the " - "base MFT record. " - "Defragment volume and " - "try once again.\n"); - errno = ENOSPC; - return -1; - } - goto retry; - } - - /* Add attribute list if it isn't present, and retry. */ - if (!NInoAttrList(base_ni)) { - ntfs_attr_put_search_ctx(ctx); - if (ntfs_inode_add_attrlist(base_ni)) { - ntfs_log_trace("Couldn't add attribute " - "list.\n"); - return -1; - } - goto retry; - } - - /* - * Set mapping pairs size to maximum possible for this - * mft record. We shall write the rest of mapping pairs - * to another MFT records. - */ - mp_size = exp_max_mp_size; - } - - /* Change space for mapping pairs if we need it. */ - if (((mp_size + 7) & ~7) != cur_max_mp_size) { - if (ntfs_attr_record_resize(m, a, - le16_to_cpu(a->mapping_pairs_offset) + - mp_size)) { - ntfs_log_error("BUG! Ran out of space in mft " - "record. Please run chkdsk and " - "if that doesn't find any " - "errors please report you saw " - "this message to %s.\n", - NTFS_DEV_LIST); - err = EIO; - goto put_err_out; - } - } - - /* Update lowest vcn. */ - a->lowest_vcn = cpu_to_sle64(stop_vcn); - ntfs_inode_mark_dirty(ctx->ntfs_ino); - if ((ctx->ntfs_ino->nr_extents == -1 || - NInoAttrList(ctx->ntfs_ino)) && - ctx->attr->type != AT_ATTRIBUTE_LIST) { - ctx->al_entry->lowest_vcn = cpu_to_sle64(stop_vcn); - ntfs_attrlist_mark_dirty(ctx->ntfs_ino); - } - - /* - * Generate the new mapping pairs array directly into the - * correct destination, i.e. the attribute record itself. - */ - if (!ntfs_mapping_pairs_build(na->ni->vol, (u8*)a + le16_to_cpu( - a->mapping_pairs_offset), mp_size, na->rl, - stop_vcn, &stop_vcn)) - finished_build = TRUE; - if (!finished_build && errno != ENOSPC) { - err = errno; - ntfs_log_error("BUG! Mapping pairs build failed. " - "Please run chkdsk and if that doesn't " - "find any errors please report you saw " - "this message to %s.\n", NTFS_DEV_LIST); - goto put_err_out; - } - a->highest_vcn = cpu_to_sle64(stop_vcn - 1); - } - /* Check whether error occurred. */ - if (errno != ENOENT) { - err = errno; - ntfs_log_trace("Attribute lookup failed.\n"); - goto put_err_out; - } - - /* Deallocate not used attribute extents and return with success. */ - if (finished_build) { - ntfs_attr_reinit_search_ctx(ctx); - ntfs_log_trace("Deallocate marked extents.\n"); - while (!ntfs_attr_lookup(na->type, na->name, na->name_len, - CASE_SENSITIVE, 0, NULL, 0, ctx)) { - if (sle64_to_cpu(ctx->attr->highest_vcn) != - NTFS_VCN_DELETE_MARK) - continue; - /* Remove unused attribute record. */ - if (ntfs_attr_record_rm(ctx)) { - err = errno; - ntfs_log_trace("Couldn't remove unused " - "attribute record.\n"); - goto put_err_out; - } - ntfs_attr_reinit_search_ctx(ctx); - } - if (errno != ENOENT) { - err = errno; - ntfs_log_trace("Attribute lookup failed.\n"); - goto put_err_out; - } - ntfs_log_trace("Deallocate done.\n"); - ntfs_attr_put_search_ctx(ctx); - goto ok; - } - ntfs_attr_put_search_ctx(ctx); - ctx = NULL; - - /* Allocate new MFT records for the rest of mapping pairs. */ - while (1) { - /* Calculate size of rest mapping pairs. */ - mp_size = ntfs_get_size_for_mapping_pairs(na->ni->vol, - na->rl, stop_vcn); - if (mp_size <= 0) { - err = errno; - ntfs_log_trace("Get size for mapping pairs failed.\n"); - goto put_err_out; - } - /* Allocate new mft record. */ - ni = ntfs_mft_record_alloc(na->ni->vol, base_ni); - if (!ni) { - err = errno; - ntfs_log_trace("Couldn't allocate new MFT record.\n"); - goto put_err_out; - } - m = ni->mrec; - /* - * If mapping size exceed available space, set them to - * possible maximum. - */ - cur_max_mp_size = le32_to_cpu(m->bytes_allocated) - - le32_to_cpu(m->bytes_in_use) - - (offsetof(ATTR_RECORD, compressed_size) + - ((NAttrCompressed(na) || NAttrSparse(na)) ? - sizeof(a->compressed_size) : 0)) - - ((sizeof(ntfschar) * na->name_len + 7) & ~7); - if (mp_size > cur_max_mp_size) - mp_size = cur_max_mp_size; - /* Add attribute extent to new record. */ - err = ntfs_non_resident_attr_record_add(ni, na->type, - na->name, na->name_len, stop_vcn, mp_size, 0); - if (err == -1) { - err = errno; - ntfs_log_trace("Couldn't add attribute extent into the " - "MFT record.\n"); - if (ntfs_mft_record_free(na->ni->vol, ni)) { - ntfs_log_trace("Couldn't free MFT record.\n"); - } - goto put_err_out; - } - a = (ATTR_RECORD*)((u8*)m + err); - - err = ntfs_mapping_pairs_build(na->ni->vol, (u8*)a + - le16_to_cpu(a->mapping_pairs_offset), mp_size, na->rl, - stop_vcn, &stop_vcn); - if (err < 0 && errno != ENOSPC) { - err = errno; - ntfs_log_error("BUG! Mapping pairs build failed. " - "Please run chkdsk and if that doesn't " - "find any errors please report you saw " - "this message to %s.\n", NTFS_DEV_LIST); - if (ntfs_mft_record_free(na->ni->vol, ni)) - ntfs_log_trace("Couldn't free MFT record.\n"); - goto put_err_out; - } - a->highest_vcn = cpu_to_sle64(stop_vcn - 1); - ntfs_inode_mark_dirty(ni); - /* All mapping pairs has been written. */ - if (!err) - break; - } -ok: - ret = 0; -out: + int ret; + + ntfs_log_enter("Entering\n"); + ret = ntfs_attr_update_mapping_pairs_i(na, from_vcn); + ntfs_log_leave("\n"); return ret; -put_err_out: - if (ctx) - ntfs_attr_put_search_ctx(ctx); - errno = err; - goto out; } -#undef NTFS_VCN_DELETE_MARK /** * ntfs_non_resident_attr_shrink - shrink a non-resident, open ntfs attribute @@ -4524,10 +4729,9 @@ static int ntfs_non_resident_attr_shrink(ntfs_attr *na, const s64 newsize) /* Get the first attribute record. */ ctx = ntfs_attr_get_search_ctx(na->ni, NULL); - if (!ctx) { - ntfs_log_trace("Couldn't get attribute search context.\n"); + if (!ctx) return -1; - } + if (ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE, 0, NULL, 0, ctx)) { err = errno; @@ -4586,7 +4790,7 @@ put_err_out: * ERANGE - @newsize is not valid for the attribute type of @na. * ENOSPC - There is no enough space in base mft to resize $ATTRIBUTE_LIST. */ -static int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize) +static int ntfs_non_resident_attr_expand_i(ntfs_attr *na, const s64 newsize) { LCN lcn_seek_from; VCN first_free_vcn; @@ -4596,7 +4800,7 @@ static int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize) s64 org_alloc_size; int err; - ntfs_log_trace("Inode 0x%llx, attr 0x%x, new size %lld old size %lld\n", + ntfs_log_trace("Inode %lld, attr 0x%x, new size %lld old size %lld\n", (unsigned long long)na->ni->mft_no, na->type, (long long)newsize, (long long)na->data_size); @@ -4607,11 +4811,9 @@ static int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize) * against @newsize and fail if @newsize is too big. */ if (ntfs_attr_size_bounds_check(vol, na->type, newsize) < 0) { - if (errno == ERANGE) { - ntfs_log_trace("Eeek! Size bounds check failed. " - "Aborting...\n"); - } else if (errno == ENOENT) + if (errno == ENOENT) errno = EIO; + ntfs_log_perror("%s: bounds check failed", __FUNCTION__); return -1; } @@ -4626,8 +4828,7 @@ static int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize) */ if ((na->allocated_size >> vol->cluster_size_bits) < first_free_vcn) { if (ntfs_attr_map_whole_runlist(na)) { - ntfs_log_trace("Eeek! ntfs_attr_map_whole_runlist " - "failed.\n"); + ntfs_log_perror("ntfs_attr_map_whole_runlist failed"); return -1; } @@ -4680,7 +4881,11 @@ static int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize) vol->cluster_size_bits), lcn_seek_from, DATA_ZONE); if (!rl) { - ntfs_log_trace("Eeek! Cluster allocation failed.\n"); + ntfs_log_perror("Cluster allocation failed " + "(%lld)", + (long long)first_free_vcn - + ((long long)na->allocated_size >> + vol->cluster_size_bits)); return -1; } } @@ -4690,7 +4895,7 @@ static int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize) if (!rln) { /* Failed, free just allocated clusters. */ err = errno; - ntfs_log_trace("Eeek! Run list merge failed.\n"); + ntfs_log_perror("Run list merge failed"); ntfs_cluster_free_from_rl(vol, rl); free(rl); errno = err; @@ -4704,7 +4909,7 @@ static int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize) if (ntfs_attr_update_mapping_pairs(na, 0 /*na->allocated_size >> vol->cluster_size_bits*/)) { err = errno; - ntfs_log_trace("Eeek! Mapping pairs update failed.\n"); + ntfs_log_perror("Mapping pairs update failed"); goto rollback; } } @@ -4712,7 +4917,6 @@ static int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize) ctx = ntfs_attr_get_search_ctx(na->ni, NULL); if (!ctx) { err = errno; - ntfs_log_trace("Failed to get search context.\n"); if (na->allocated_size == org_alloc_size) { errno = err; return -1; @@ -4723,7 +4927,7 @@ static int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize) if (ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE, 0, NULL, 0, ctx)) { err = errno; - ntfs_log_trace("Lookup of first attribute extent failed.\n"); + ntfs_log_perror("Lookup of first attribute extent failed"); if (err == ENOENT) err = EIO; if (na->allocated_size != org_alloc_size) { @@ -4750,8 +4954,8 @@ rollback: /* Free allocated clusters. */ if (ntfs_cluster_free(vol, na, org_alloc_size >> vol->cluster_size_bits, -1) < 0) { - ntfs_log_trace("Eeek! Leaking clusters. Run chkdsk!\n"); err = EIO; + ntfs_log_perror("Leaking clusters"); } /* Now, truncate the runlist itself. */ if (ntfs_rl_truncate(&na->rl, org_alloc_size >> @@ -4762,15 +4966,14 @@ rollback: */ free(na->rl); na->rl = NULL; - ntfs_log_trace("Couldn't truncate runlist. Rollback failed.\n"); + ntfs_log_perror("Couldn't truncate runlist. Rollback failed"); } else { /* Prepare to mapping pairs update. */ - na->allocated_size = org_alloc_size << vol->cluster_size_bits; + na->allocated_size = org_alloc_size; /* Restore mapping pairs. */ if (ntfs_attr_update_mapping_pairs(na, 0 /*na->allocated_size >> vol->cluster_size_bits*/)) { - ntfs_log_trace("Failed to restore old mapping pairs. " - "Rollback failed.\n"); + ntfs_log_perror("Failed to restore old mapping pairs"); } } errno = err; @@ -4781,6 +4984,17 @@ put_err_out: return -1; } + +static int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize) +{ + int ret; + + ntfs_log_enter("Entering\n"); + ret = ntfs_non_resident_attr_expand_i(na, newsize); + ntfs_log_leave("\n"); + return ret; +} + /** * ntfs_attr_truncate - resize an ntfs attribute * @na: open ntfs attribute to resize @@ -4803,7 +5017,7 @@ put_err_out: */ int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize) { - int ret; + int ret = STATUS_ERROR; if (!na || newsize < 0 || (na->ni->mft_no == FILE_MFT && na->type == AT_DATA)) { @@ -4812,12 +5026,14 @@ int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize) return STATUS_ERROR; } - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, size %lld\n", - (unsigned long long)na->ni->mft_no, na->type, newsize); + ntfs_log_enter("Entering for inode %lld, attr 0x%x, size %lld\n", + (unsigned long long)na->ni->mft_no, na->type, + (long long)newsize); if (na->data_size == newsize) { ntfs_log_trace("Size is already ok\n"); - return STATUS_OK; + ret = STATUS_OK; + goto out; } /* * Encrypted attributes are not supported. We return access denied, @@ -4826,7 +5042,7 @@ int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize) if (NAttrEncrypted(na)) { errno = EACCES; ntfs_log_perror("Failed to truncate encrypted attribute"); - return STATUS_ERROR; + goto out; } /* * TODO: Implement making handling of compressed attributes. @@ -4834,7 +5050,7 @@ int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize) if (NAttrCompressed(na)) { errno = EOPNOTSUPP; ntfs_log_perror("Failed to truncate compressed attribute"); - return STATUS_ERROR; + goto out; } if (NAttrNonResident(na)) { if (newsize > na->data_size) @@ -4843,12 +5059,8 @@ int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize) ret = ntfs_non_resident_attr_shrink(na, newsize); } else ret = ntfs_resident_attr_resize(na, newsize); - /* Update access and change times if needed. */ - if (na->type == AT_DATA || na->type == AT_INDEX_ROOT || - na->type == AT_INDEX_ALLOCATION) - ntfs_inode_update_time(na->ni); - - ntfs_log_trace("Return status %d\n", ret); +out: + ntfs_log_leave("Return status %d\n", ret); return ret; } @@ -4878,12 +5090,12 @@ void *ntfs_attr_readall(ntfs_inode *ni, const ATTR_TYPES type, void *data, *ret = NULL; s64 size; - ntfs_log_trace("Entering\n"); + ntfs_log_enter("Entering\n"); na = ntfs_attr_open(ni, type, name, name_len); if (!na) { ntfs_log_perror("ntfs_attr_open failed"); - return NULL; + goto err_exit; } data = ntfs_malloc(na->data_size); if (!data) @@ -4900,6 +5112,8 @@ void *ntfs_attr_readall(ntfs_inode *ni, const ATTR_TYPES type, *data_size = size; out: ntfs_attr_close(na); +err_exit: + ntfs_log_leave("\n"); return ret; } @@ -4955,3 +5169,63 @@ int ntfs_attr_remove(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name, return ret; } +/* Below macros are 32-bit ready. */ +#define BCX(x) ((x) - (((x) >> 1) & 0x77777777) - \ + (((x) >> 2) & 0x33333333) - \ + (((x) >> 3) & 0x11111111)) +#define BITCOUNT(x) (((BCX(x) + (BCX(x) >> 4)) & 0x0F0F0F0F) % 255) + +static u8 *ntfs_init_lut256(void) +{ + int i; + u8 *lut; + + lut = ntfs_malloc(256); + if (lut) + for(i = 0; i < 256; i++) + *(lut + i) = 8 - BITCOUNT(i); + return lut; +} + +s64 ntfs_attr_get_free_bits(ntfs_attr *na) +{ + u8 *buf, *lut; + s64 br = 0; + s64 total = 0; + s64 nr_free = 0; + + lut = ntfs_init_lut256(); + if (!lut) + return -1; + + buf = ntfs_malloc(65536); + if (!buf) + goto out; + + while (1) { + u32 *p; + br = ntfs_attr_pread(na, total, 65536, buf); + if (br <= 0) + break; + total += br; + p = (u32 *)buf + br / 4 - 1; + for (; (u8 *)p >= buf; p--) { + nr_free += lut[ *p & 255] + + lut[(*p >> 8) & 255] + + lut[(*p >> 16) & 255] + + lut[(*p >> 24) ]; + } + switch (br % 4) { + case 3: nr_free += lut[*(buf + br - 3)]; + case 2: nr_free += lut[*(buf + br - 2)]; + case 1: nr_free += lut[*(buf + br - 1)]; + } + } + free(buf); +out: + free(lut); + if (!total || br < 0) + return -1; + return nr_free; +} + diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/attrib.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/attrib.h index 9e4dd31b5d..360962392e 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/attrib.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/attrib.h @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2004 Anton Altaparmakov * Copyright (c) 2004-2005 Yura Pakhuchiy - * Copyright (c) 2006 Szabolcs Szakacsits + * Copyright (c) 2006-2007 Szabolcs Szakacsits * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -92,6 +92,8 @@ extern int ntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name, const VCN lowest_vcn, const u8 *val, const u32 val_len, ntfs_attr_search_ctx *ctx); +extern int ntfs_attr_position(const ATTR_TYPES type, ntfs_attr_search_ctx *ctx); + extern ATTR_DEF *ntfs_attr_find_in_attrdef(const ntfs_volume *vol, const ATTR_TYPES type); @@ -185,7 +187,8 @@ struct _ntfs_attr { }; /** - * enum ntfs_attr_state_bits - bits for the state field in the ntfs_attr structure + * enum ntfs_attr_state_bits - bits for the state field in the ntfs_attr + * structure */ typedef enum { NA_Initialized, /* 1: structure is initialized. */ @@ -204,30 +207,15 @@ typedef enum { #define NAttrSetNonResident(na) set_nattr_flag(na, NonResident) #define NAttrClearNonResident(na) clear_nattr_flag(na, NonResident) -#define GenNAttrIno(func_name,flag) \ -static inline int NAttr##func_name(ntfs_attr *na) \ -{ \ - if (na->type == AT_DATA && na->name == AT_UNNAMED) \ - return (na->ni->flags & FILE_ATTR_##flag); \ - return 0; \ -} \ -static inline void NAttrSet##func_name(ntfs_attr *na) \ -{ \ - if (na->type == AT_DATA && na->name == AT_UNNAMED) \ - na->ni->flags |= FILE_ATTR_##flag; \ - else \ - ntfs_log_trace("BUG! Should be called only for "\ - "unnamed data attribute.\n"); \ -} \ -static inline void NAttrClear##func_name(ntfs_attr *na) \ -{ \ - if (na->type == AT_DATA && na->name == AT_UNNAMED) \ - na->ni->flags &= ~FILE_ATTR_##flag; \ -} +#define GenNAttrIno(func_name, flag) \ +extern int NAttr##func_name(ntfs_attr *na); \ +extern void NAttrSet##func_name(ntfs_attr *na); \ +extern void NAttrClear##func_name(ntfs_attr *na); -GenNAttrIno(Compressed, COMPRESSED) -GenNAttrIno(Encrypted, ENCRYPTED) -GenNAttrIno(Sparse, SPARSE_FILE) +GenNAttrIno(Compressed, FILE_ATTR_COMPRESSED) +GenNAttrIno(Encrypted, FILE_ATTR_ENCRYPTED) +GenNAttrIno(Sparse, FILE_ATTR_SPARSE_FILE) +#undef GenNAttrIno /** * union attr_val - Union of all known attribute values @@ -318,7 +306,6 @@ extern int ntfs_attr_update_mapping_pairs(ntfs_attr *na, VCN from_vcn); extern int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize); -// FIXME / TODO: Above here the file is cleaned up. (AIA) /** * get_attribute_value_length - return the length of the value of an attribute * @a: pointer to a buffer containing the attribute record @@ -355,6 +342,7 @@ extern int ntfs_attr_exist(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name, u32 name_len); extern int ntfs_attr_remove(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name, u32 name_len); +extern s64 ntfs_attr_get_free_bits(ntfs_attr *na); #endif /* defined _NTFS_ATTRIB_H */ diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/attrlist.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/attrlist.c index 33cb3e271e..9c62f31614 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/attrlist.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/attrlist.c @@ -145,7 +145,6 @@ int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr) ctx = ntfs_attr_get_search_ctx(ni, NULL); if (!ctx) { err = errno; - ntfs_log_trace("Failed to obtain attribute search context.\n"); goto err_out; } if (!ntfs_attr_lookup(attr->type, (attr->name_length) ? (ntfschar*) diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/bitmap.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/bitmap.c index 097386c229..65162a2946 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/bitmap.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/bitmap.c @@ -3,7 +3,7 @@ * * Copyright (c) 2002-2006 Anton Altaparmakov * Copyright (c) 2004-2005 Richard Russon - * Copyright (c) 2004-2006 Szabolcs Szakacsits + * Copyright (c) 2004-2008 Szabolcs Szakacsits * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -117,10 +117,12 @@ static int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit, { s64 bufsize, br; u8 *buf, *lastbyte_buf; - int bit, firstbyte, lastbyte, lastbyte_pos, tmp, err; + int bit, firstbyte, lastbyte, lastbyte_pos, tmp, ret = -1; if (!na || start_bit < 0 || count < 0) { errno = EINVAL; + ntfs_log_perror("%s: Invalid argument (%p, %lld, %lld)", + __FUNCTION__, na, (long long)start_bit, (long long)count); return -1; } @@ -147,9 +149,9 @@ static int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit, /* read it in... */ br = ntfs_attr_pread(na, start_bit >> 3, 1, buf); if (br != 1) { - free(buf); - errno = EIO; - return -1; + if (br >= 0) + errno = EIO; + goto free_err_out; } /* and set or clear the appropriate bits in it. */ while ((bit & 7) && count--) { @@ -172,9 +174,9 @@ static int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit, lastbyte_pos = ((count + 7) >> 3) + firstbyte; if (!lastbyte_pos) { // FIXME: Eeek! BUG! - ntfs_log_trace("Eeek! lastbyte is zero. Leaving " + ntfs_log_error("Lastbyte is zero. Leaving " "inconsistent metadata.\n"); - err = EIO; + errno = EIO; goto free_err_out; } /* and it is in the currently loaded bitmap window... */ @@ -186,10 +188,11 @@ static int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit, 3, 1, lastbyte_buf); if (br != 1) { // FIXME: Eeek! We need rollback! (AIA) - ntfs_log_trace("Eeek! Read of last byte " - "failed. Leaving " - "inconsistent metadata.\n"); - err = EIO; + if (br >= 0) + errno = EIO; + ntfs_log_perror("Reading of last byte " + "failed (%lld). Leaving inconsistent " + "metadata", (long long)br); goto free_err_out; } /* and set/clear the appropriate bits in it. */ @@ -211,9 +214,11 @@ static int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit, br = ntfs_attr_pwrite(na, tmp, bufsize, buf); if (br != bufsize) { // FIXME: Eeek! We need rollback! (AIA) - ntfs_log_trace("Eeek! Failed to write buffer to bitmap. " - "Leaving inconsistent metadata.\n"); - err = EIO; + if (br >= 0) + errno = EIO; + ntfs_log_perror("Failed to write buffer to bitmap " + "(%lld != %lld). Leaving inconsistent metadata", + (long long)br, (long long)bufsize); goto free_err_out; } @@ -234,22 +239,19 @@ static int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit, if (lastbyte && count != 0) { // FIXME: Eeek! BUG! - ntfs_log_trace("Eeek! Last buffer but count is not zero (= " - "%lli). Leaving inconsistent metadata.\n", - (long long)count); - err = EIO; + ntfs_log_error("Last buffer but count is not zero " + "(%lld). Leaving inconsistent metadata.\n", + (long long)count); + errno = EIO; goto free_err_out; } } while (count > 0); - - /* Done! */ - free(buf); - return 0; - + + ret = 0; + free_err_out: free(buf); - errno = err; - return -1; + return ret; } /** @@ -265,7 +267,13 @@ free_err_out: */ int ntfs_bitmap_set_run(ntfs_attr *na, s64 start_bit, s64 count) { - return ntfs_bitmap_set_bits_in_run(na, start_bit, count, 1); + int ret; + + ntfs_log_enter("Set from bit %lld, count %lld\n", + (long long)start_bit, (long long)count); + ret = ntfs_bitmap_set_bits_in_run(na, start_bit, count, 1); + ntfs_log_leave("\n"); + return ret; } /** @@ -281,9 +289,12 @@ int ntfs_bitmap_set_run(ntfs_attr *na, s64 start_bit, s64 count) */ int ntfs_bitmap_clear_run(ntfs_attr *na, s64 start_bit, s64 count) { - ntfs_log_trace("Dealloc from bit 0x%llx, count 0x%llx.\n", + int ret; + + ntfs_log_enter("Clear from bit %lld, count %lld\n", (long long)start_bit, (long long)count); - - return ntfs_bitmap_set_bits_in_run(na, start_bit, count, 0); + ret = ntfs_bitmap_set_bits_in_run(na, start_bit, count, 0); + ntfs_log_leave("\n"); + return ret; } diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/bitmap.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/bitmap.h index 56d1f64e1e..10b5f6c5b4 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/bitmap.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/bitmap.h @@ -70,5 +70,27 @@ static __inline__ int ntfs_bitmap_clear_bit(ntfs_attr *na, s64 bit) return ntfs_bitmap_clear_run(na, bit, 1); } +/* + * rol32 - rotate a 32-bit value left + * + * @word: value to rotate + * @shift: bits to roll + */ +static __inline__ u32 ntfs_rol32(u32 word, unsigned int shift) +{ + return (word << shift) | (word >> (32 - shift)); +} + +/* + * ror32 - rotate a 32-bit value right + * + * @word: value to rotate + * @shift: bits to roll + */ +static __inline__ u32 ntfs_ror32(u32 word, unsigned int shift) +{ + return (word >> shift) | (word << (32 - shift)); +} + #endif /* defined _NTFS_BITMAP_H */ diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/bootsect.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/bootsect.c index f13a11cdfd..e9bea3703b 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/bootsect.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/bootsect.c @@ -2,7 +2,7 @@ * bootsect.c - Boot sector handling code. Originated from the Linux-NTFS project. * * Copyright (c) 2000-2006 Anton Altaparmakov - * Copyright (c) 2003-2006 Szabolcs Szakacsits + * Copyright (c) 2003-2008 Szabolcs Szakacsits * Copyright (c) 2005 Yura Pakhuchiy * * This program/include file is free software; you can redistribute it and/or @@ -151,6 +151,13 @@ not_ntfs: return ret; } +static const char *last_sector_error = +"HINTS: Either the volume is a RAID/LDM but it wasn't setup yet,\n" +" or it was not setup correctly (e.g. by not using mdadm --build ...),\n" +" or a wrong device is tried to be mounted,\n" +" or the partition table is corrupt (partition is smaller than NTFS),\n" +" or the NTFS boot sector is corrupt (NTFS size is not valid).\n"; + /** * ntfs_boot_sector_parse - setup an ntfs volume from an ntfs boot sector * @vol: ntfs_volume to setup @@ -188,7 +195,7 @@ int ntfs_boot_sector_parse(ntfs_volume *vol, const NTFS_BOOT_SECTOR *bs) } sectors = sle64_to_cpu(bs->number_of_sectors); - ntfs_log_debug("NumberOfSectors = %lld\n", sectors); + ntfs_log_debug("NumberOfSectors = %lld\n", (long long)sectors); if (!sectors) { ntfs_log_error("Volume size is set to zero.\n"); return -1; @@ -198,9 +205,7 @@ int ntfs_boot_sector_parse(ntfs_volume *vol, const NTFS_BOOT_SECTOR *bs) SEEK_SET) == -1) { ntfs_log_perror("Failed to read last sector (%lld)", (long long)sectors); - ntfs_log_error("Perhaps the volume is a RAID/LDM but it wasn't " - "setup yet, or the\nwrong device was used, " - "or the partition table is incorrect.\n" ); + ntfs_log_error("%s", last_sector_error); return -1; } @@ -208,8 +213,8 @@ int ntfs_boot_sector_parse(ntfs_volume *vol, const NTFS_BOOT_SECTOR *bs) vol->mft_lcn = sle64_to_cpu(bs->mft_lcn); vol->mftmirr_lcn = sle64_to_cpu(bs->mftmirr_lcn); - ntfs_log_debug("MFT LCN = 0x%llx\n", vol->mft_lcn); - ntfs_log_debug("MFTMirr LCN = 0x%llx\n", vol->mftmirr_lcn); + ntfs_log_debug("MFT LCN = %lld\n", (long long)vol->mft_lcn); + ntfs_log_debug("MFTMirr LCN = %lld\n", (long long)vol->mftmirr_lcn); if (vol->mft_lcn > vol->nr_clusters || vol->mftmirr_lcn > vol->nr_clusters) { ntfs_log_error("$MFT LCN (%lld) or $MFTMirr LCN (%lld) is " diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/compat.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/compat.c index 1c31623e02..81f95a673c 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/compat.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/compat.c @@ -20,16 +20,12 @@ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#if defined(WINDOWS) || defined(__BEOS__) || defined (__HAIKU__) - #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "compat.h" -/* TODO: Add check for FFS in the configure script... (AIA) */ - #ifndef HAVE_FFS /** * ffs - Find the first set bit in an int @@ -69,5 +65,186 @@ int ffs(int x) } #endif /* HAVE_FFS */ -#endif /* defined(WINDOWS) || defined(__BEOS__) || defined (__HAIKU__) */ +#ifndef HAVE_DAEMON +/* ************************************************************ + * From: src.opensolaris.org + * src/lib/libresolv2/common/bsd/daemon.c + */ +/* + * Copyright (c) 1997-2000 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char sccsid[] = "@(#)daemon.c 8.1 (Berkeley) 6/4/93"; +static const char rcsid[] = "$Id: compat.c,v 1.2 2008/07/17 15:01:48 szaka Exp $"; +#endif /* LIBC_SCCS and not lint */ + +/* + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + +int daemon(int nochdir, int noclose) { + int fd; + + switch (fork()) { + case -1: + return (-1); + case 0: + break; + default: + _exit(0); + } + + if (setsid() == -1) + return (-1); + + if (!nochdir) + (void)chdir("/"); + + if (!noclose && (fd = open("/dev/null", O_RDWR, 0)) != -1) { + (void)dup2(fd, 0); + (void)dup2(fd, 1); + (void)dup2(fd, 2); + if (fd > 2) + (void)close (fd); + } + return (0); +} +/* + * End: src/lib/libresolv2/common/bsd/daemon.c + *************************************************************/ +#endif /* HAVE_DAEMON */ + +#ifndef HAVE_STRSEP +/* ************************************************************ + * From: src.opensolaris.org + * src/lib/libresolv2/common/bsd/strsep.c + */ +/* + * Copyright (c) 1997, by Sun Microsystems, Inc. + * All rights reserved. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char sccsid[] = "strsep.c 8.1 (Berkeley) 6/4/93"; +static const char rcsid[] = "$Id: compat.c,v 1.2 2008/07/17 15:01:48 szaka Exp $"; +#endif /* LIBC_SCCS and not lint */ + +/* + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_STDIO_H +#include +#endif + +/* + * Get next token from string *stringp, where tokens are possibly-empty + * strings separated by characters from delim. + * + * Writes NULs into the string at *stringp to end tokens. + * delim need not remain constant from call to call. + * On return, *stringp points past the last NUL written (if there might + * be further tokens), or is NULL (if there are definitely no more tokens). + * + * If *stringp is NULL, strsep returns NULL. + */ +char *strsep(char **stringp, const char *delim) { + char *s; + const char *spanp; + int c, sc; + char *tok; + + if ((s = *stringp) == NULL) + return (NULL); + for (tok = s;;) { + c = *s++; + spanp = delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) + s = NULL; + else + s[-1] = 0; + *stringp = s; + return (tok); + } + } while (sc != 0); + } + /* NOTREACHED */ +} + +/* + * End: src/lib/libresolv2/common/bsd/strsep.c + *************************************************************/ +#endif /* HAVE_STRSEP */ diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/compat.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/compat.h index 74d555cf01..04767b0536 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/compat.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/compat.h @@ -3,6 +3,7 @@ * * Copyright (c) 2002 Richard Russon * Copyright (c) 2002-2004 Anton Altaparmakov + * Copyright (c) 2008-2009 Szabolcs Szakacsits * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -26,14 +27,28 @@ #ifdef HAVE_CONFIG_H #include "config.h" #endif +#ifdef HAVE_SYS_PARAM_H +#include +#endif -#ifdef WINDOWS +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif #ifndef HAVE_FFS -#define HAVE_FFS extern int ffs(int i); #endif /* HAVE_FFS */ +#ifndef HAVE_DAEMON +extern int daemon(int nochdir, int noclose); +#endif /* HAVE_DAEMON */ + +#ifndef HAVE_STRSEP +extern char *strsep(char **stringp, const char *delim); +#endif /* HAVE_STRSEP */ + +#ifdef WINDOWS + #define HAVE_STDIO_H /* mimic config.h */ #define HAVE_STDARG_H @@ -44,9 +59,11 @@ extern int ffs(int i); #else /* !defined WINDOWS */ +#ifndef __HAIKU__ #ifndef O_BINARY #define O_BINARY 0 /* unix is binary by default */ #endif +#endif #endif /* defined WINDOWS */ diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/compress.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/compress.c index a6be8275ad..09f874bbc3 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/compress.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/compress.c @@ -100,8 +100,8 @@ static int ntfs_decompress(u8 *dest, const u32 dest_size, ntfs_log_trace("Entering, cb_size = 0x%x.\n", (unsigned)cb_size); do_next_sb: - ntfs_log_debug("Beginning sub-block at offset = 0x%x in the cb.\n", - cb - cb_start); + ntfs_log_debug("Beginning sub-block at offset = %d in the cb.\n", + (int)(cb - cb_start)); /* * Have we reached the end of the compression block or the end of the * decompressed data? The latter can happen for example if the current diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/debug.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/debug.c index d154fb0ee8..f193483309 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/debug.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/debug.c @@ -63,9 +63,15 @@ void ntfs_debug_runlist_dump(const runlist_element *rl) if (idx > -LCN_EINVAL - 1) idx = 4; - ntfs_log_debug("%-16llx %s %-16llx%s\n", rl[i].vcn, lcn_str[idx], rl[i].length, rl[i].length ? "" : " (runlist end)"); + ntfs_log_debug("%-16lld %s %-16lld%s\n", + (long long)rl[i].vcn, lcn_str[idx], + (long long)rl[i].length, + rl[i].length ? "" : " (runlist end)"); } else - ntfs_log_debug("%-16llx %-16llx %-16llx%s\n", rl[i].vcn, rl[i].lcn, rl[i].length, rl[i].length ? "" : " (runlist end)"); + ntfs_log_debug("%-16lld %-16lld %-16lld%s\n", + (long long)rl[i].vcn, (long long)rl[i].lcn, + (long long)rl[i].length, + rl[i].length ? "" : " (runlist end)"); } while (rl[i++].length); } diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/device.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/device.c index cf4969c385..c77d8f9576 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/device.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/device.c @@ -178,7 +178,7 @@ s64 ntfs_pread(struct ntfs_device *dev, const s64 pos, s64 count, void *b) s64 br, total; struct ntfs_device_operations *dops; - ntfs_log_trace("Entering for pos 0x%llx, count 0x%llx.\n", pos, count); + ntfs_log_trace("pos %lld, count %lld\n",(long long)pos,(long long)count); if (!b || count < 0 || pos < 0) { errno = EINVAL; @@ -229,7 +229,8 @@ s64 ntfs_pwrite(struct ntfs_device *dev, const s64 pos, s64 count, s64 written, total, ret = -1; struct ntfs_device_operations *dops; - ntfs_log_trace("Entering for pos 0x%llx, count 0x%llx.\n", pos, count); + ntfs_log_trace("pos %lld, count %lld\n",(long long)pos,(long long)count); + if (!b || count < 0 || pos < 0) { errno = EINVAL; goto out; diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/dir.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/dir.c index eb00e3810d..ebc0803b84 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/dir.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/dir.c @@ -3,8 +3,8 @@ * * Copyright (c) 2002-2005 Anton Altaparmakov * Copyright (c) 2004-2005 Richard Russon - * Copyright (c) 2004-2006 Szabolcs Szakacsits - * Copyright (c) 2005-2006 Yura Pakhuchiy + * Copyright (c) 2004-2008 Szabolcs Szakacsits + * Copyright (c) 2005-2007 Yura Pakhuchiy * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -134,7 +134,7 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, const ntfschar *uname, if (ntfs_attr_lookup(AT_INDEX_ROOT, NTFS_INDEX_I30, 4, CASE_SENSITIVE, 0, NULL, 0, ctx)) { ntfs_log_perror("Index root attribute missing in directory inode " - "0x%llx", (unsigned long long)dir_ni->mft_no); + "%lld", (unsigned long long)dir_ni->mft_no); goto put_err_out; } /* Get to the index root value. */ @@ -143,7 +143,7 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, const ntfschar *uname, index_block_size = le32_to_cpu(ir->index_block_size); if (index_block_size < NTFS_BLOCK_SIZE || index_block_size & (index_block_size - 1)) { - ntfs_log_debug("Index block size %u is invalid.\n", + ntfs_log_error("Index block size %u is invalid.\n", (unsigned)index_block_size); goto put_err_out; } @@ -160,14 +160,23 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, const ntfschar *uname, if ((u8*)ie < (u8*)ctx->mrec || (u8*)ie + sizeof(INDEX_ENTRY_HEADER) > index_end || (u8*)ie + le16_to_cpu(ie->key_length) > - index_end) + index_end) { + ntfs_log_error("Index entry out of bounds in inode %lld" + "\n", (unsigned long long)dir_ni->mft_no); goto put_err_out; + } /* * The last entry cannot contain a name. It can however contain * a pointer to a child node in the B+tree so we just break out. */ if (ie->ie_flags & INDEX_ENTRY_END) break; + + if (!le16_to_cpu(ie->length)) { + ntfs_log_error("Zero length index entry in inode %lld" + "\n", (unsigned long long)dir_ni->mft_no); + goto put_err_out; + } /* * Not a perfect match, need to do full blown collation so we * know which way in the B+tree we have to go. @@ -226,8 +235,7 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, const ntfschar *uname, /* Open the index allocation attribute. */ ia_na = ntfs_attr_open(dir_ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4); if (!ia_na) { - ntfs_log_perror("Failed to open index allocation attribute. Directory " - "inode 0x%llx is corrupt or driver bug", + ntfs_log_perror("Failed to open index allocation (inode %lld)", (unsigned long long)dir_ni->mft_no); goto put_err_out; } @@ -265,7 +273,7 @@ descend_into_child_node: } if (sle64_to_cpu(ia->index_block_vcn) != vcn) { - ntfs_log_debug("Actual VCN (0x%llx) of index buffer is different " + ntfs_log_error("Actual VCN (0x%llx) of index buffer is different " "from expected VCN (0x%llx).\n", (long long)sle64_to_cpu(ia->index_block_vcn), (long long)vcn); @@ -273,7 +281,7 @@ descend_into_child_node: goto close_err_out; } if (le32_to_cpu(ia->index.allocated_size) + 0x18 != index_block_size) { - ntfs_log_debug("Index buffer (VCN 0x%llx) of directory inode 0x%llx " + ntfs_log_error("Index buffer (VCN 0x%llx) of directory inode 0x%llx " "has a size (%u) differing from the directory " "specified size (%u).\n", (long long)vcn, (unsigned long long)dir_ni->mft_no, @@ -284,7 +292,7 @@ descend_into_child_node: } index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length); if (index_end > (u8*)ia + index_block_size) { - ntfs_log_debug("Size of index buffer (VCN 0x%llx) of directory inode " + ntfs_log_error("Size of index buffer (VCN 0x%llx) of directory inode " "0x%llx exceeds maximum size.\n", (long long)vcn, (unsigned long long)dir_ni->mft_no); errno = EIO; @@ -305,9 +313,9 @@ descend_into_child_node: sizeof(INDEX_ENTRY_HEADER) > index_end || (u8*)ie + le16_to_cpu(ie->key_length) > index_end) { - ntfs_log_debug("Index entry out of bounds in directory " - "inode 0x%llx.\n", - (unsigned long long)dir_ni->mft_no); + ntfs_log_error("Index entry out of bounds in directory " + "inode %lld.\n", + (unsigned long long)dir_ni->mft_no); errno = EIO; goto close_err_out; } @@ -317,6 +325,13 @@ descend_into_child_node: */ if (ie->ie_flags & INDEX_ENTRY_END) break; + + if (!le16_to_cpu(ie->length)) { + errno = EIO; + ntfs_log_error("Zero length index entry in inode %lld" + "\n", (unsigned long long)dir_ni->mft_no); + goto close_err_out; + } /* * Not a perfect match, need to do full blown collation so we * know which way in the B+tree we have to go. @@ -361,8 +376,8 @@ descend_into_child_node: */ if (ie->ie_flags & INDEX_ENTRY_NODE) { if ((ia->index.ih_flags & NODE_MASK) == LEAF_NODE) { - ntfs_log_debug("Index entry with child node found in a leaf " - "node in directory inode 0x%llx.\n", + ntfs_log_error("Index entry with child node found in a leaf " + "node in directory inode %lld.\n", (unsigned long long)dir_ni->mft_no); errno = EIO; goto close_err_out; @@ -371,8 +386,8 @@ descend_into_child_node: vcn = sle64_to_cpup((u8*)ie + le16_to_cpu(ie->length) - 8); if (vcn >= 0) goto descend_into_child_node; - ntfs_log_debug("Negative child node vcn in directory inode " - "0x%llx.\n", (unsigned long long)dir_ni->mft_no); + ntfs_log_error("Negative child node vcn in directory inode " + "0x%llx.\n", (unsigned long long)dir_ni->mft_no); errno = EIO; goto close_err_out; } @@ -446,10 +461,9 @@ ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent, } } - unicode = ntfs_calloc(MAX_PATH); ascii = strdup(pathname); - if (!unicode || !ascii) { - ntfs_log_debug("Out of memory.\n"); + if (!ascii) { + ntfs_log_error("Out of memory.\n"); err = ENOMEM; goto close; } @@ -466,9 +480,10 @@ ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent, q++; } - len = ntfs_mbstoucs(p, &unicode, MAX_PATH); + len = ntfs_mbstoucs(p, &unicode); if (len < 0) { - ntfs_log_debug("Couldn't convert name to Unicode: %s.\n", p); + ntfs_log_perror("Could not convert filename to Unicode:" + " '%s'", p); err = errno; goto close; } else if (len > NTFS_MAX_NAME_LEN) { @@ -499,6 +514,9 @@ ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent, goto close; } + free(unicode); + unicode = NULL; + p = q; while (p && *p && *p == PATH_SEP) p++; @@ -578,6 +596,8 @@ static int ntfs_filldir(ntfs_inode *dir_ni, s64 *pos, u8 ivcn_bits, return 0; if (ie->key.file_name.file_attributes & FILE_ATTR_I30_INDEX_PRESENT) dt_type = NTFS_DT_DIR; + else if (fn->file_attributes & FILE_ATTR_SYSTEM) + dt_type = NTFS_DT_UNKNOWN; else dt_type = NTFS_DT_REG; return filldir(dirent, fn->file_name, fn->file_name_length, @@ -621,21 +641,21 @@ static MFT_REF ntfs_mft_get_parent_ref(ntfs_inode *ni) if (!ctx) return ERR_MREF(-1); if (ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { - ntfs_log_debug("No file name found in inode 0x%llx. Corrupt " - "inode.\n", (unsigned long long)ni->mft_no); + ntfs_log_error("No file name found in inode %lld\n", + (unsigned long long)ni->mft_no); goto err_out; } if (ctx->attr->non_resident) { - ntfs_log_debug("File name attribute must be resident. Corrupt inode " - "0x%llx.\n", (unsigned long long)ni->mft_no); + ntfs_log_error("File name attribute must be resident (inode " + "%lld)\n", (unsigned long long)ni->mft_no); goto io_err_out; } fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + le16_to_cpu(ctx->attr->value_offset)); if ((u8*)fn + le32_to_cpu(ctx->attr->value_length) > (u8*)ctx->attr + le32_to_cpu(ctx->attr->length)) { - ntfs_log_debug("Corrupt file name attribute in inode 0x%llx.\n", - (unsigned long long)ni->mft_no); + ntfs_log_error("Corrupt file name attribute in inode %lld.\n", + (unsigned long long)ni->mft_no); goto io_err_out; } mref = le64_to_cpu(fn->parent_directory); @@ -695,7 +715,7 @@ int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, vol = dir_ni->vol; - ntfs_log_trace("Entering for inode 0x%llx, *pos 0x%llx.\n", + ntfs_log_trace("Entering for inode %lld, *pos 0x%llx.\n", (unsigned long long)dir_ni->mft_no, (long long)*pos); /* Open the index allocation attribute. */ @@ -703,7 +723,7 @@ int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, if (!ia_na) { if (errno != ENOENT) { ntfs_log_perror("Failed to open index allocation attribute. " - "Directory inode 0x%llx is corrupt or bug", + "Directory inode %lld is corrupt or bug", (unsigned long long)dir_ni->mft_no); return -1; } @@ -724,7 +744,7 @@ int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, le16_to_cpu(dir_ni->mrec->sequence_number)), NTFS_DT_DIR); if (rc) - goto done; + goto err_out; ++*pos; } if (*pos == 1) { @@ -739,7 +759,7 @@ int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, rc = filldir(dirent, dotdot, 2, FILE_NAME_POSIX, *pos, parent_mref, NTFS_DT_DIR); if (rc) - goto done; + goto err_out; ++*pos; } @@ -752,8 +772,8 @@ int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, /* Find the index root attribute in the mft record. */ if (ntfs_attr_lookup(AT_INDEX_ROOT, NTFS_INDEX_I30, 4, CASE_SENSITIVE, 0, NULL, 0, ctx)) { - ntfs_log_debug("Index root attribute missing in directory inode " - "0x%llx.\n", (unsigned long long)dir_ni->mft_no); + ntfs_log_perror("Index root attribute missing in directory inode " + "%lld", (unsigned long long)dir_ni->mft_no); goto dir_err_out; } /* Get to the index root value. */ @@ -764,7 +784,7 @@ int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, index_block_size = le32_to_cpu(ir->index_block_size); if (index_block_size < NTFS_BLOCK_SIZE || index_block_size & (index_block_size - 1)) { - ntfs_log_debug("Index block size %u is invalid.\n", + ntfs_log_error("Index block size %u is invalid.\n", (unsigned)index_block_size); goto dir_err_out; } @@ -794,7 +814,7 @@ int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, * or signals an error (both covered by the rc test). */ for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) { - ntfs_log_debug("In index root, offset 0x%x.\n", (u8*)ie - (u8*)ir); + ntfs_log_debug("In index root, offset %d.\n", (int)((u8*)ie - (u8*)ir)); /* Bounds checks. */ if ((u8*)ie < (u8*)ctx->mrec || (u8*)ie + sizeof(INDEX_ENTRY_HEADER) > index_end || @@ -804,6 +824,10 @@ int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, /* The last entry cannot contain a name. */ if (ie->ie_flags & INDEX_ENTRY_END) break; + + if (!le16_to_cpu(ie->length)) + goto dir_err_out; + /* Skip index root entry if continuing previous readdir. */ if (ir_pos > (u8*)ie - (u8*)ir) continue; @@ -816,7 +840,7 @@ int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, if (rc) { ntfs_attr_put_search_ctx(ctx); ctx = NULL; - goto done; + goto err_out; } } ntfs_attr_put_search_ctx(ctx); @@ -850,7 +874,7 @@ skip_index_root: bmp_pos = ia_pos >> index_block_size_bits; if (bmp_pos >> 3 >= bmp_na->data_size) { - ntfs_log_debug("Current index position exceeds index bitmap " + ntfs_log_error("Current index position exceeds index bitmap " "size.\n"); goto dir_err_out; } @@ -881,6 +905,7 @@ find_next_index_buffer: if (bmp_buf_pos >> 3 < bmp_buf_size) continue; /* Read next chunk from the index bitmap. */ + bmp_buf_pos = 0; if ((bmp_pos >> 3) + bmp_buf_size > bmp_na->data_size) bmp_buf_size = bmp_na->data_size - (bmp_pos >> 3); br = ntfs_attr_pread(bmp_na, bmp_pos >> 3, bmp_buf_size, bmp); @@ -907,7 +932,7 @@ find_next_index_buffer: ia_start = ia_pos & ~(s64)(index_block_size - 1); if (sle64_to_cpu(ia->index_block_vcn) != ia_start >> index_vcn_size_bits) { - ntfs_log_debug("Actual VCN (0x%llx) of index buffer is different " + ntfs_log_error("Actual VCN (0x%llx) of index buffer is different " "from expected VCN (0x%llx) in inode 0x%llx.\n", (long long)sle64_to_cpu(ia->index_block_vcn), (long long)ia_start >> index_vcn_size_bits, @@ -915,7 +940,7 @@ find_next_index_buffer: goto dir_err_out; } if (le32_to_cpu(ia->index.allocated_size) + 0x18 != index_block_size) { - ntfs_log_debug("Index buffer (VCN 0x%llx) of directory inode 0x%llx " + ntfs_log_error("Index buffer (VCN 0x%llx) of directory inode %lld " "has a size (%u) differing from the directory " "specified size (%u).\n", (long long)ia_start >> index_vcn_size_bits, @@ -926,8 +951,8 @@ find_next_index_buffer: } index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length); if (index_end > (u8*)ia + index_block_size) { - ntfs_log_debug("Size of index buffer (VCN 0x%llx) of directory inode " - "0x%llx exceeds maximum size.\n", + ntfs_log_error("Size of index buffer (VCN 0x%llx) of directory inode " + "%lld exceeds maximum size.\n", (long long)ia_start >> index_vcn_size_bits, (unsigned long long)dir_ni->mft_no); goto dir_err_out; @@ -948,13 +973,17 @@ find_next_index_buffer: sizeof(INDEX_ENTRY_HEADER) > index_end || (u8*)ie + le16_to_cpu(ie->key_length) > index_end) { - ntfs_log_debug("Index entry out of bounds in directory inode " - "0x%llx.\n", (unsigned long long)dir_ni->mft_no); + ntfs_log_error("Index entry out of bounds in directory inode " + "%lld.\n", (unsigned long long)dir_ni->mft_no); goto dir_err_out; } /* The last entry cannot contain a name. */ if (ie->ie_flags & INDEX_ENTRY_END) break; + + if (!le16_to_cpu(ie->length)) + goto dir_err_out; + /* Skip index entry if continuing previous readdir. */ if (ia_pos - ia_start > (u8*)ie - (u8*)ia) continue; @@ -965,7 +994,7 @@ find_next_index_buffer: rc = ntfs_filldir(dir_ni, pos, index_vcn_size_bits, INDEX_TYPE_ALLOCATION, ia, ie, dirent, filldir); if (rc) - goto done; + goto err_out; } goto find_next_index_buffer; EOD: @@ -978,13 +1007,7 @@ done: ntfs_attr_close(bmp_na); if (ia_na) ntfs_attr_close(ia_na); -#ifdef DEBUG - if (!rc) - ntfs_log_debug("EOD, *pos 0x%llx, returning 0.\n", (long long)*pos); - else - ntfs_log_debug("filldir returned %i, *pos 0x%llx, returning 0.\n", - rc, (long long)*pos); -#endif + ntfs_log_debug("EOD, *pos 0x%llx, returning 0.\n", (long long)*pos); return 0; dir_err_out: errno = EIO; @@ -1037,7 +1060,7 @@ err_out: */ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, ntfschar *name, u8 name_len, dev_t type, dev_t dev, - ntfschar *target, u8 target_len) + ntfschar *target, int target_len) { ntfs_inode *ni; int rollback_data = 0, rollback_sd = 0; @@ -1206,6 +1229,8 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, fn->last_data_change_time = utc2ntfs(ni->last_data_change_time); fn->last_mft_change_time = utc2ntfs(ni->last_mft_change_time); fn->last_access_time = utc2ntfs(ni->last_access_time); + fn->data_size = cpu_to_sle64(ni->data_size); + fn->allocated_size = cpu_to_sle64(ni->allocated_size); memcpy(fn->file_name, name, name_len * sizeof(ntfschar)); /* Add FILE_NAME attribute to inode. */ if (ntfs_attr_add(ni, AT_FILE_NAME, AT_UNNAMED, 0, (u8*)fn, fn_len)) { @@ -1284,10 +1309,11 @@ ntfs_inode *ntfs_create_device(ntfs_inode *dir_ni, ntfschar *name, u8 name_len, } ntfs_inode *ntfs_create_symlink(ntfs_inode *dir_ni, ntfschar *name, u8 name_len, - ntfschar *target, u8 target_len) + ntfschar *target, int target_len) { if (!target || !target_len) { - ntfs_log_error("Invalid arguments.\n"); + ntfs_log_error("%s: Invalid argument (%p, %d)\n", __FUNCTION__, + target, target_len); return NULL; } return __ntfs_create(dir_ni, name, name_len, S_IFLNK, 0, @@ -1312,7 +1338,7 @@ int ntfs_check_empty_dir(ntfs_inode *ni) /* Non-empty directory? */ if ((na->data_size != sizeof(INDEX_ROOT) + sizeof(INDEX_ENTRY_HEADER))){ /* Both ENOTEMPTY and EEXIST are ok. We use the more common. */ - errno = EEXIST; + errno = ENOTEMPTY; ntfs_log_debug("Directory is not empty\n"); ret = -1; } @@ -1327,7 +1353,7 @@ static int ntfs_check_unlinkable_dir(ntfs_inode *ni, FILE_NAME_ATTR *fn) int ret; ret = ntfs_check_empty_dir(ni); - if (!ret || errno != EEXIST) + if (!ret || errno != ENOTEMPTY) return ret; /* * Directory is non-empty, so we can unlink only if there is more than @@ -1335,7 +1361,7 @@ static int ntfs_check_unlinkable_dir(ntfs_inode *ni, FILE_NAME_ATTR *fn) */ if ((link_count == 1) || (link_count == 2 && fn->file_name_type == FILE_NAME_DOS)) { - errno = EEXIST; + errno = ENOTEMPTY; ntfs_log_debug("Non-empty directory without hard links\n"); goto no_hardlink; } @@ -1360,7 +1386,6 @@ no_hardlink: int ntfs_delete(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, u8 name_len) { ntfs_attr_search_ctx *actx = NULL; - ntfs_index_context *ictx = NULL; FILE_NAME_ATTR *fn = NULL; BOOL looking_for_dos_name = FALSE, looking_for_win32_name = FALSE; BOOL case_sensitive_match = TRUE; @@ -1417,8 +1442,9 @@ search: /* Ignore hard links from other directories */ if (dir_ni->mft_no != MREF_LE(fn->parent_directory)) { ntfs_log_debug("MFT record numbers don't match " - "(%llu != %llu)\n", dir_ni->mft_no, - MREF_LE(fn->parent_directory)); + "(%llu != %llu)\n", + (long long unsigned)dir_ni->mft_no, + (long long unsigned)MREF_LE(fn->parent_directory)); continue; } @@ -1455,19 +1481,7 @@ search: if (ntfs_check_unlinkable_dir(ni, fn) < 0) goto err_out; - ictx = ntfs_index_ctx_get(dir_ni, NTFS_INDEX_I30, 4); - if (!ictx) - goto err_out; - if (ntfs_index_lookup(fn, le32_to_cpu(actx->attr->value_length), ictx)) - goto err_out; - - if (((FILE_NAME_ATTR*)ictx->data)->file_attributes & - FILE_ATTR_REPARSE_POINT) { - errno = EOPNOTSUPP; - goto err_out; - } - - if (ntfs_index_rm(ictx)) + if (ntfs_index_remove(dir_ni, fn, le32_to_cpu(actx->attr->value_length))) goto err_out; if (ntfs_attr_record_rm(actx)) @@ -1489,8 +1503,10 @@ search: * case there are no reference to this inode left, so we should free all * non-resident attributes and mark all MFT record as not in use. */ - if (ni->mrec->link_count) - goto out; + if (ni->mrec->link_count) { + ntfs_inode_update_times(ni, NTFS_UPDATE_CTIME); + goto ok; + } ntfs_attr_reinit_search_ctx(actx); while (!ntfs_attrs_walk(actx)) { if (actx->attr->non_resident) { @@ -1531,12 +1547,14 @@ search: "Leaving inconsistent metadata.\n"); } ni = NULL; +ok: + ntfs_inode_update_times(dir_ni, NTFS_UPDATE_MCTIME); out: if (actx) ntfs_attr_put_search_ctx(actx); - if (ictx) - ntfs_index_ctx_put(ictx); - if (ni && ntfs_inode_close(ni) && !err) + if (ntfs_inode_close(dir_ni) && !err) + err = errno; + if (ntfs_inode_close(ni) && !err) err = errno; if (err) { errno = err; @@ -1614,22 +1632,11 @@ int ntfs_link(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, u8 name_len) } /* Add FILE_NAME attribute to inode. */ if (ntfs_attr_add(ni, AT_FILE_NAME, AT_UNNAMED, 0, (u8*)fn, fn_len)) { - ntfs_index_context *ictx; - - err = errno; ntfs_log_error("Failed to add FILE_NAME attribute.\n"); + err = errno; /* Try to remove just added attribute from index. */ - ictx = ntfs_index_ctx_get(dir_ni, NTFS_INDEX_I30, 4); - if (!ictx) + if (ntfs_index_remove(dir_ni, fn, fn_len)) goto rollback_failed; - if (ntfs_index_lookup(fn, fn_len, ictx)) { - ntfs_index_ctx_put(ictx); - goto rollback_failed; - } - if (ntfs_index_rm(ictx)) { - ntfs_index_ctx_put(ictx); - goto rollback_failed; - } goto err_out; } /* Increment hard links count. */ diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/dir.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/dir.h index 2d81cff0d2..2000eefff3 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/dir.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/dir.h @@ -1,9 +1,10 @@ /* * dir.h - Exports for directory handling. Originated from the Linux-NTFS project. * - * Copyright (c) 2002 Anton Altaparmakov + * Copyright (c) 2002 Anton Altaparmakov * Copyright (c) 2005-2006 Yura Pakhuchiy * Copyright (c) 2004-2005 Richard Russon + * Copyright (c) 2005-2008 Szabolcs Szakacsits * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -28,17 +29,10 @@ #define PATH_SEP '/' +#ifdef __HAIKU__ #ifndef MAX_PATH #define MAX_PATH 1024 #endif - -/* - * We do not have S_IFSOCK under BeOS/Haiku - */ -#if defined(__BEOS__) || defined(__HAIKU__) -#ifndef S_IFSOCK -#define S_IFSOCK 0140000 -#endif #endif /* @@ -82,7 +76,7 @@ extern ntfs_inode *ntfs_create(ntfs_inode *dir_ni, ntfschar *name, u8 name_len, extern ntfs_inode *ntfs_create_device(ntfs_inode *dir_ni, ntfschar *name, u8 name_len, dev_t type, dev_t dev); extern ntfs_inode *ntfs_create_symlink(ntfs_inode *dir_ni, - ntfschar *name, u8 name_len, ntfschar *target, u8 target_len); + ntfschar *name, u8 name_len, ntfschar *target, int target_len); extern int ntfs_check_empty_dir(ntfs_inode *ni); extern int ntfs_delete(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, u8 name_len); diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/endians.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/endians.h index 48c11ff4b8..397f1c20b9 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/endians.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/endians.h @@ -48,9 +48,6 @@ #ifdef HAVE_SYS_BYTEORDER_H #include #endif -#ifdef HAVE_BYTEORDER_H -#include -#endif #ifdef HAVE_SYS_PARAM_H #include #endif diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/index.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/index.c index 506a6a5265..acd6394c53 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/index.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/index.c @@ -4,7 +4,7 @@ * Copyright (c) 2004-2005 Anton Altaparmakov * Copyright (c) 2004-2005 Richard Russon * Copyright (c) 2005-2006 Yura Pakhuchiy - * Copyright (c) 2005-2006 Szabolcs Szakacsits + * Copyright (c) 2005-2008 Szabolcs Szakacsits * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -78,18 +78,17 @@ static VCN ntfs_ib_pos_to_vcn(ntfs_index_context *icx, s64 pos) return pos >> icx->vcn_size_bits; } -static int ntfs_ib_write(ntfs_index_context *icx, VCN vcn, void *buf) +static int ntfs_ib_write(ntfs_index_context *icx, INDEX_BLOCK *ib) { - s64 ret; + s64 ret, vcn = sle64_to_cpu(ib->index_block_vcn); - ntfs_log_trace("vcn: %lld\n", vcn); + ntfs_log_trace("vcn: %lld\n", (long long)vcn); ret = ntfs_attr_mst_pwrite(icx->ia_na, ntfs_ib_vcn_to_pos(icx, vcn), - 1, icx->block_size, buf); + 1, icx->block_size, ib); if (ret != 1) { - ntfs_log_perror("Failed to write index block %lld of inode " - "%llu", (long long)vcn, - (unsigned long long)icx->ni->mft_no); + ntfs_log_perror("Failed to write index block %lld, inode %llu", + (long long)vcn, (unsigned long long)icx->ni->mft_no); return STATUS_ERROR; } @@ -98,7 +97,7 @@ static int ntfs_ib_write(ntfs_index_context *icx, VCN vcn, void *buf) static int ntfs_icx_ib_write(ntfs_index_context *icx) { - if (ntfs_ib_write(icx, icx->ib_vcn, icx->ib)) + if (ntfs_ib_write(icx, icx->ib)) return STATUS_ERROR; icx->ib_dirty = FALSE; @@ -148,18 +147,14 @@ static void ntfs_index_ctx_free(ntfs_index_context *icx) if (icx->actx) ntfs_attr_put_search_ctx(icx->actx); - if (icx->is_in_root) { - if (icx->ia_na) - ntfs_attr_close(icx->ia_na); - return; - } - - if (icx->ib_dirty) { - /* FIXME: Error handling!!! */ - ntfs_ib_write(icx, icx->ib_vcn, icx->ib); + if (!icx->is_in_root) { + if (icx->ib_dirty) { + /* FIXME: Error handling!!! */ + ntfs_ib_write(icx, icx->ib); + } + free(icx->ib); } - free(icx->ib); ntfs_attr_close(icx->ia_na); } @@ -225,7 +220,7 @@ static u8 *ntfs_ie_get_end(INDEX_HEADER *ih) static int ntfs_ie_end(INDEX_ENTRY *ie) { - return ie->ie_flags & INDEX_ENTRY_END; + return ie->ie_flags & INDEX_ENTRY_END || !ie->length; } /** @@ -306,11 +301,13 @@ static int ntfs_ih_numof_entries(INDEX_HEADER *ih) { int n; INDEX_ENTRY *ie; + u8 *end; ntfs_log_trace("Entering\n"); + end = ntfs_ie_get_end(ih); ie = ntfs_ie_get_first(ih); - for (n = 0; !ntfs_ie_end(ie); n++) + for (n = 0; !ntfs_ie_end(ie) && (u8 *)ie < end; n++) ie = ntfs_ie_get_next(ie); return n; } @@ -434,10 +431,8 @@ static INDEX_ROOT *ntfs_ir_lookup(ntfs_inode *ni, ntfschar *name, ntfs_log_trace("Entering\n"); *ctx = ntfs_attr_get_search_ctx(ni, NULL); - if (!*ctx) { - ntfs_log_perror("Failed to get $INDEX_ROOT search context"); + if (!*ctx) return NULL; - } if (ntfs_attr_lookup(AT_INDEX_ROOT, name, name_len, CASE_SENSITIVE, 0, NULL, 0, *ctx)) { @@ -454,8 +449,21 @@ static INDEX_ROOT *ntfs_ir_lookup(ntfs_inode *ni, ntfschar *name, ir = (INDEX_ROOT *)((char *)a + le16_to_cpu(a->value_offset)); err_out: - if (!ir) + if (!ir) { ntfs_attr_put_search_ctx(*ctx); + *ctx = NULL; + } + return ir; +} + +static INDEX_ROOT *ntfs_ir_lookup2(ntfs_inode *ni, ntfschar *name, u32 len) +{ + ntfs_attr_search_ctx *ctx; + INDEX_ROOT *ir; + + ir = ntfs_ir_lookup(ni, name, len, &ctx); + if (ir) + ntfs_attr_put_search_ctx(ctx); return ir; } @@ -577,7 +585,7 @@ static int ntfs_ib_read(ntfs_index_context *icx, VCN vcn, INDEX_BLOCK *dst) { s64 pos, ret; - ntfs_log_trace("vcn: %lld\n", vcn); + ntfs_log_trace("vcn: %lld\n", (long long)vcn); pos = ntfs_ib_vcn_to_pos(icx, vcn); @@ -651,15 +659,13 @@ static int ntfs_icx_parent_dec(ntfs_index_context *icx) * the call to ntfs_index_ctx_put() to ensure that the changes are written * to disk. */ -int ntfs_index_lookup(const void *key, const int key_len, - ntfs_index_context *icx) +int ntfs_index_lookup(const void *key, const int key_len, ntfs_index_context *icx) { VCN old_vcn, vcn; ntfs_inode *ni = icx->ni; INDEX_ROOT *ir; INDEX_ENTRY *ie; INDEX_BLOCK *ib = NULL; - ntfs_attr_search_ctx *actx; int ret, err = 0; ntfs_log_trace("Entering\n"); @@ -670,7 +676,7 @@ int ntfs_index_lookup(const void *key, const int key_len, return -1; } - ir = ntfs_ir_lookup(ni, icx->name, icx->name_len, &actx); + ir = ntfs_ir_lookup(ni, icx->name, icx->name_len, &icx->actx); if (!ir) { if (errno == ENOENT) errno = EIO; @@ -682,7 +688,7 @@ int ntfs_index_lookup(const void *key, const int key_len, errno = EINVAL; ntfs_log_perror("Index block size (%d) is smaller than the " "sector size (%d)", icx->block_size, NTFS_BLOCK_SIZE); - return -1; + goto err_out; } if (ni->vol->cluster_size <= icx->block_size) @@ -709,7 +715,6 @@ int ntfs_index_lookup(const void *key, const int key_len, goto err_out; } - icx->actx = actx; icx->ir = ir; if (ret != STATUS_KEEP_SEARCHING) { @@ -741,7 +746,7 @@ descend_into_child_node: } old_vcn = vcn; - ntfs_log_debug("Descend into node with VCN %lld.\n", vcn); + ntfs_log_debug("Descend into node with VCN %lld\n", (long long)vcn); if (ntfs_ib_read(icx, vcn, ib)) goto err_out; @@ -755,7 +760,7 @@ descend_into_child_node: /* STATUS_OK or STATUS_NOT_FOUND */ icx->is_in_root = FALSE; icx->ib = ib; - icx->parent_vcn[icx->pindex] = icx->ib_vcn = vcn; + icx->parent_vcn[icx->pindex] = vcn; goto done; } @@ -768,20 +773,15 @@ descend_into_child_node: goto descend_into_child_node; err_out: - if (icx->ia_na) - ntfs_attr_close(icx->ia_na); free(ib); if (!err) err = EIO; - if (actx) - ntfs_attr_put_search_ctx(actx); errno = err; return -1; done: icx->entry = ie; icx->data = (u8 *)ie + offsetof(INDEX_ENTRY, key); icx->data_len = le16_to_cpu(ie->key_length); - icx->max_depth = icx->pindex; ntfs_log_trace("Done.\n"); if (err) { errno = err; @@ -797,7 +797,7 @@ static INDEX_BLOCK *ntfs_ib_alloc(VCN ib_vcn, u32 ib_size, INDEX_BLOCK *ib; int ih_size = sizeof(INDEX_HEADER); - ntfs_log_trace("Entering ib_vcn = %lld ib_size = %u\n", ib_vcn, ib_size); + ntfs_log_trace("ib_vcn: %lld ib_size: %u\n", (long long)ib_vcn, ib_size); ib = ntfs_calloc(ib_size); if (!ib) @@ -893,7 +893,7 @@ static int ntfs_ibm_modify(ntfs_index_context *icx, VCN vcn, int set) ntfs_attr *na; int ret = STATUS_ERROR; - ntfs_log_trace("%s vcn: %lld\n", set ? "set" : "clear", vcn); + ntfs_log_trace("%s vcn: %lld\n", set ? "set" : "clear", (long long)vcn); na = ntfs_attr_open(icx->ni, AT_BITMAP, icx->name, icx->name_len); if (!na) { @@ -970,7 +970,7 @@ static VCN ntfs_ibm_get_free(ntfs_index_context *icx) vcn = ntfs_ibm_pos_to_vcn(icx, size * 8); out: - ntfs_log_trace("allocated vcn: %lld\n", vcn); + ntfs_log_trace("allocated vcn: %lld\n", (long long)vcn); if (ntfs_ibm_set(icx, vcn)) vcn = (VCN)-1; @@ -1052,13 +1052,13 @@ static int ntfs_ib_copy_tail(ntfs_index_context *icx, INDEX_BLOCK *src, dst->index.index_length = cpu_to_le32(tail_size + le32_to_cpu(dst->index.entries_offset)); - ret = ntfs_ib_write(icx, new_vcn, dst); + ret = ntfs_ib_write(icx, dst); free(dst); return ret; } -static int ntfs_ib_cut_tail(ntfs_index_context *icx, INDEX_BLOCK *src, +static int ntfs_ib_cut_tail(ntfs_index_context *icx, INDEX_BLOCK *ib, INDEX_ENTRY *ie) { char *ies_start, *ies_end; @@ -1066,8 +1066,8 @@ static int ntfs_ib_cut_tail(ntfs_index_context *icx, INDEX_BLOCK *src, ntfs_log_trace("Entering\n"); - ies_start = (char *)ntfs_ie_get_first(&src->index); - ies_end = (char *)ntfs_ie_get_end(&src->index); + ies_start = (char *)ntfs_ie_get_first(&ib->index); + ies_end = (char *)ntfs_ie_get_end(&ib->index); ie_last = ntfs_ie_get_last((INDEX_ENTRY *)ies_start, ies_end); if (ie_last->ie_flags & INDEX_ENTRY_NODE) @@ -1075,10 +1075,10 @@ static int ntfs_ib_cut_tail(ntfs_index_context *icx, INDEX_BLOCK *src, memcpy(ie, ie_last, le16_to_cpu(ie_last->length)); - src->index.index_length = cpu_to_le32(((char *)ie - ies_start) + - le16_to_cpu(ie->length) + le32_to_cpu(src->index.entries_offset)); + ib->index.index_length = cpu_to_le32(((char *)ie - ies_start) + + le16_to_cpu(ie->length) + le32_to_cpu(ib->index.entries_offset)); - if (ntfs_ib_write(icx, icx->parent_vcn[icx->pindex + 1], src)) + if (ntfs_ib_write(icx, ib)) return STATUS_ERROR; return STATUS_OK; @@ -1109,7 +1109,7 @@ static int ntfs_ia_add(ntfs_index_context *icx) static int ntfs_ir_reparent(ntfs_index_context *icx) { - ntfs_attr_search_ctx *ctx; + ntfs_attr_search_ctx *ctx = NULL; INDEX_ROOT *ir; INDEX_ENTRY *ie; INDEX_BLOCK *ib = NULL; @@ -1118,25 +1118,33 @@ static int ntfs_ir_reparent(ntfs_index_context *icx) ntfs_log_trace("Entering\n"); - ir = ntfs_ir_lookup(icx->ni, icx->name, icx->name_len, &ctx); + ir = ntfs_ir_lookup2(icx->ni, icx->name, icx->name_len); if (!ir) - return -1; + goto out; if ((ir->index.ih_flags & NODE_MASK) == SMALL_INDEX) if (ntfs_ia_add(icx)) - goto err_out; + goto out; new_ib_vcn = ntfs_ibm_get_free(icx); if (new_ib_vcn == -1) - goto err_out; + goto out; + ir = ntfs_ir_lookup2(icx->ni, icx->name, icx->name_len); + if (!ir) + goto clear_bmp; + ib = ntfs_ir_to_ib(ir, new_ib_vcn); if (ib == NULL) { ntfs_log_perror("Failed to move index root to index block"); goto clear_bmp; } - if (ntfs_ib_write(icx, new_ib_vcn, ib)) + if (ntfs_ib_write(icx, ib)) + goto clear_bmp; + + ir = ntfs_ir_lookup(icx->ni, icx->name, icx->name_len, &ctx); + if (!ir) goto clear_bmp; ntfs_ir_nill(ir); @@ -1153,8 +1161,8 @@ static int ntfs_ir_reparent(ntfs_index_context *icx) if (ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr, sizeof(INDEX_ROOT) - sizeof(INDEX_HEADER) + le32_to_cpu(ir->index.allocated_size))) - /* FIXME: revert bitmap, index root */ - goto err_out; + /* FIXME: revert index root */ + goto clear_bmp; /* * FIXME: do it earlier if we have enough space in IR (should always), * so in error case we wouldn't lose the IB. @@ -1165,6 +1173,7 @@ static int ntfs_ir_reparent(ntfs_index_context *icx) err_out: free(ib); ntfs_attr_put_search_ctx(ctx); +out: return ret; clear_bmp: ntfs_ibm_clear(icx, new_ib_vcn); @@ -1195,18 +1204,14 @@ static int ntfs_ir_truncate(ntfs_index_context *icx, int data_size) ret = ntfs_attr_truncate(na, data_size + offsetof(INDEX_ROOT, index)); if (ret == STATUS_OK) { - ntfs_attr_search_ctx *ctx; - - icx->ir = ntfs_ir_lookup(icx->ni, icx->name, icx->name_len, &ctx); + icx->ir = ntfs_ir_lookup2(icx->ni, icx->name, icx->name_len); if (!icx->ir) return STATUS_ERROR; icx->ir->index.allocated_size = cpu_to_le32(data_size); - ntfs_attr_put_search_ctx(ctx); - - } else if (errno != ENOSPC) - ntfs_log_trace("Failed to truncate INDEX_ROOT"); + } else if (ret == STATUS_ERROR) + ntfs_log_perror("Failed to truncate INDEX_ROOT"); ntfs_attr_close(na); return ret; @@ -1223,16 +1228,16 @@ static int ntfs_ir_make_space(ntfs_index_context *icx, int data_size) int ret; ntfs_log_trace("Entering\n"); - + ret = ntfs_ir_truncate(icx, data_size); if (ret == STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT) { - + ret = ntfs_ir_reparent(icx); if (ret == STATUS_OK) ret = STATUS_KEEP_SEARCHING; else ntfs_log_perror("Failed to nodify INDEX_ROOT"); - } + } return ret; } @@ -1304,6 +1309,10 @@ static int ntfs_ir_insert_median(ntfs_index_context *icx, INDEX_ENTRY *median, ntfs_log_trace("Entering\n"); + icx->ir = ntfs_ir_lookup2(icx->ni, icx->name, icx->name_len); + if (!icx->ir) + return STATUS_ERROR; + new_size = le32_to_cpu(icx->ir->index.index_length) + le16_to_cpu(median->length); if (!(median->ie_flags & INDEX_ENTRY_NODE)) @@ -1312,6 +1321,10 @@ static int ntfs_ir_insert_median(ntfs_index_context *icx, INDEX_ENTRY *median, ret = ntfs_ir_make_space(icx, new_size); if (ret != STATUS_OK) return ret; + + icx->ir = ntfs_ir_lookup2(icx->ni, icx->name, icx->name_len); + if (!icx->ir) + return STATUS_ERROR; return ntfs_ih_insert(&icx->ir->index, median, new_vcn, ntfs_icx_parent_pos(icx)); @@ -1354,7 +1367,7 @@ static int ntfs_ib_insert(ntfs_index_context *icx, INDEX_ENTRY *ie, VCN new_vcn) if (ntfs_ih_insert(&ib->index, ie, new_vcn, ntfs_icx_parent_pos(icx))) goto err_out; - if (ntfs_ib_write(icx, old_vcn, ib)) + if (ntfs_ib_write(icx, ib)) goto err_out; err = STATUS_OK; @@ -1364,7 +1377,7 @@ err_out: } /** - * ntfs_ib_split - Split index allocation attribute + * ntfs_ib_split - Split an index block * * On success return STATUS_OK or STATUS_KEEP_SEARCHING. * On error return is STATUS_ERROR. @@ -1395,8 +1408,6 @@ static int ntfs_ib_split(ntfs_index_context *icx, INDEX_BLOCK *ib) else ret = ntfs_ib_insert(icx, median, new_vcn); - ntfs_inode_mark_dirty(icx->actx->ntfs_ino); - if (ret != STATUS_OK) { ntfs_ibm_clear(icx, new_vcn); return ret; @@ -1410,14 +1421,16 @@ static int ntfs_ib_split(ntfs_index_context *icx, INDEX_BLOCK *ib) static int ntfs_ie_add(ntfs_index_context *icx, INDEX_ENTRY *ie) { - char *fn; INDEX_HEADER *ih; int allocated_size, new_size; int ret = STATUS_ERROR; +#ifdef DEBUG + char *fn; fn = ntfs_ie_filename_get(ie); ntfs_log_trace("file: '%s'\n", fn); ntfs_attr_name_free(&fn); +#endif while (1) { @@ -1478,7 +1491,7 @@ int ntfs_index_add_filename(ntfs_inode *ni, FILE_NAME_ATTR *fn, MFT_REF mref) { INDEX_ENTRY *ie; ntfs_index_context *icx; - int fn_size, ie_size, ret = -1; + int fn_size, ie_size, err, ret = -1; ntfs_log_trace("Entering\n"); @@ -1506,8 +1519,9 @@ int ntfs_index_add_filename(ntfs_inode *ni, FILE_NAME_ATTR *fn, MFT_REF mref) goto out; ret = ntfs_ie_add(icx, ie); - + err = errno; ntfs_index_ctx_put(icx); + errno = err; out: free(ie); return ret; @@ -1530,7 +1544,7 @@ static int ntfs_ih_takeout(ntfs_index_context *icx, INDEX_HEADER *ih, if (ntfs_icx_parent_vcn(icx) == VCN_INDEX_ROOT_PARENT) ntfs_inode_mark_dirty(icx->actx->ntfs_ino); else - if (ntfs_ib_write(icx, ntfs_icx_parent_vcn(icx), ib)) + if (ntfs_ib_write(icx, ib)) goto out; ntfs_index_ctx_reinit(icx); @@ -1560,9 +1574,6 @@ static void ntfs_ir_leafify(ntfs_index_context *icx, INDEX_HEADER *ih) /* Not fatal error */ ntfs_ir_truncate(icx, le32_to_cpu(ih->index_length)); - - ntfs_inode_mark_dirty(icx->actx->ntfs_ino); - ntfs_index_ctx_reinit(icx); } /** @@ -1640,7 +1651,7 @@ out: static int ntfs_index_rm_node(ntfs_index_context *icx) { - int entry_pos; + int entry_pos, pindex; VCN vcn; INDEX_BLOCK *ib = NULL; INDEX_ENTRY *ie_succ, *ie, *entry = icx->entry; @@ -1662,6 +1673,7 @@ static int ntfs_index_rm_node(ntfs_index_context *icx) ie_succ = ntfs_ie_get_next(icx->entry); entry_pos = icx->parent_pos[icx->pindex]++; + pindex = icx->pindex; descend: vcn = ntfs_ie_get_vcn(ie_succ); if (ntfs_ib_read(icx, vcn, ib)) @@ -1679,9 +1691,8 @@ descend: goto descend; if (ntfs_ih_zero_entry(&ib->index)) { - errno = EOPNOTSUPP; - ntfs_log_perror("Failed to find any entry in an index block. " - "Please run chkdsk."); + errno = EIO; + ntfs_log_perror("Empty index block"); goto out; } @@ -1703,18 +1714,18 @@ descend: new_size = le32_to_cpu(ih->index_length) + delta; if (delta > 0) { if (icx->is_in_root) { - if (ntfs_ir_truncate(icx, new_size)) { - errno = EOPNOTSUPP; - ntfs_log_perror("Denied to truncate INDEX ROOT during entry removal"); + ret = ntfs_ir_make_space(icx, new_size); + if (ret != STATUS_OK) goto out2; - } ih = &icx->ir->index; entry = ntfs_ie_get_by_pos(ih, entry_pos); } else if (new_size > le32_to_cpu(ih->allocated_size)) { - errno = EOPNOTSUPP; - ntfs_log_perror("Denied to split INDEX BLOCK during entry removal"); + icx->pindex = pindex; + ret = ntfs_ib_split(icx, icx->ib); + if (ret == STATUS_OK) + ret = STATUS_KEEP_SEARCHING; goto out2; } } @@ -1725,7 +1736,6 @@ descend: if (icx->is_in_root) { if (ntfs_ir_truncate(icx, new_size)) goto out2; - ntfs_inode_mark_dirty(icx->actx->ntfs_ino); } else if (ntfs_icx_ib_write(icx)) goto out2; @@ -1736,7 +1746,7 @@ descend: if (ntfs_index_rm_leaf(icx)) goto out2; } else - if (ntfs_ib_write(icx, vcn, ib)) + if (ntfs_ib_write(icx, ib)) goto out2; ret = STATUS_OK; @@ -1757,10 +1767,10 @@ out: * * Return 0 on success or -1 on error with errno set to the error code. */ -int ntfs_index_rm(ntfs_index_context *icx) +static int ntfs_index_rm(ntfs_index_context *icx) { INDEX_HEADER *ih; - int err; + int err, ret = STATUS_OK; ntfs_log_trace("Entering\n"); @@ -1776,8 +1786,7 @@ int ntfs_index_rm(ntfs_index_context *icx) if (icx->entry->ie_flags & INDEX_ENTRY_NODE) { - if (ntfs_index_rm_node(icx)) - goto err_out; + ret = ntfs_index_rm_node(icx); } else if (icx->is_in_root || !ntfs_ih_one_entry(ih)) { @@ -1794,16 +1803,51 @@ int ntfs_index_rm(ntfs_index_context *icx) if (ntfs_index_rm_leaf(icx)) goto err_out; } - - ntfs_index_ctx_reinit(icx); - ntfs_log_trace("Done.\n"); - return 0; +out: + return ret; err_out: - err = errno; - ntfs_index_ctx_reinit(icx); - errno = err; - ntfs_log_trace("Failed.\n"); - return -1; + ret = STATUS_ERROR; + goto out; +} + +int ntfs_index_remove(ntfs_inode *ni, const void *key, const int keylen) +{ + int ret = STATUS_ERROR; + ntfs_index_context *icx; + + icx = ntfs_index_ctx_get(ni, NTFS_INDEX_I30, 4); + if (!icx) + return -1; + + while (1) { + + if (ntfs_index_lookup(key, keylen, icx)) + goto err_out; + + if (((FILE_NAME_ATTR *)icx->data)->file_attributes & + FILE_ATTR_REPARSE_POINT) { + errno = EOPNOTSUPP; + goto err_out; + } + + ret = ntfs_index_rm(icx); + if (ret == STATUS_ERROR) + goto err_out; + else if (ret == STATUS_OK) + break; + + ntfs_inode_mark_dirty(icx->actx->ntfs_ino); + ntfs_index_ctx_reinit(icx); + } + + ntfs_inode_mark_dirty(icx->actx->ntfs_ino); +out: + ntfs_index_ctx_put(icx); + return ret; +err_out: + ret = STATUS_ERROR; + ntfs_log_perror("Delete failed"); + goto out; } /** diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/index.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/index.h index 9d7fd88bc8..d4c4055bdd 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/index.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/index.h @@ -4,7 +4,7 @@ * Copyright (c) 2004 Anton Altaparmakov * Copyright (c) 2004-2005 Richard Russon * Copyright (c) 2005 Yura Pakhuchiy - * Copyright (c) 2006 Szabolcs Szakacsits + * Copyright (c) 2006-2008 Szabolcs Szakacsits * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -48,7 +48,6 @@ * @actx: attribute search context if in root or NULL otherwise * @ia: index block if @is_in_root is FALSE or NULL otherwise * @ia_na: opened INDEX_ALLOCATION attribute - * @ib_vcn: VCN from which @ia where read from * @parent_pos: parent entries' positions in the index block * @parent_vcn: entry's parent node or VCN_INDEX_ROOT_PARENT * @new_vcn: new VCN if we need to create a new index block @@ -64,11 +63,11 @@ * simply points into @entry. This is probably what the user is interested in. * * If @is_in_root is TRUE, @entry is in the index root attribute @ir described - * by the attribute search context @actx and inode @ni. @ia, @ib_vcn and + * by the attribute search context @actx and inode @ni. @ia and * @ib_dirty are undefined in this case. * * If @is_in_root is FALSE, @entry is in the index allocation attribute and @ia - * and @ib_vcn point to the index allocation block and VCN where it's placed, + * point to the index allocation block and VCN where it's placed, * respectively. @ir and @actx are NULL in this case. @ia_na is opened * INDEX_ALLOCATION attribute. @ib_dirty is TRUE if index block was changed and * FALSE otherwise. @@ -96,9 +95,7 @@ typedef struct { INDEX_BLOCK *ib; ntfs_attr *ia_na; int parent_pos[MAX_PARENT_VCN]; /* parent entries' positions */ - VCN ib_vcn; VCN parent_vcn[MAX_PARENT_VCN]; /* entry's parent nodes */ - int max_depth; /* number of the parent nodes */ int pindex; /* maximum it's the number of the parent nodes */ BOOL ib_dirty; u32 block_size; @@ -115,7 +112,7 @@ extern int ntfs_index_lookup(const void *key, const int key_len, extern int ntfs_index_add_filename(ntfs_inode *ni, FILE_NAME_ATTR *fn, MFT_REF mref); -extern int ntfs_index_rm(ntfs_index_context *ictx); +extern int ntfs_index_remove(ntfs_inode *ni, const void *key, const int keylen); extern INDEX_ROOT *ntfs_index_root_get(ntfs_inode *ni, ATTR_RECORD *attr); diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/inode.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/inode.c index fee018735f..e1c24ce83d 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/inode.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/inode.c @@ -2,8 +2,8 @@ * inode.c - Inode handling code. Originated from the Linux-NTFS project. * * Copyright (c) 2002-2005 Anton Altaparmakov - * Copyright (c) 2002-2006 Szabolcs Szakacsits - * Copyright (c) 2004-2005 Yura Pakhuchiy + * Copyright (c) 2002-2008 Szabolcs Szakacsits + * Copyright (c) 2004-2007 Yura Pakhuchiy * Copyright (c) 2004-2005 Richard Russon * * This program/include file is free software; you can redistribute it and/or @@ -51,6 +51,13 @@ #include "logging.h" #include "misc.h" +ntfs_inode *ntfs_inode_base(ntfs_inode *ni) +{ + if (ni->nr_extents == -1) + return ni->base_ni; + return ni; +} + /** * ntfs_inode_mark_dirty - set the inode (and its base inode if it exists) dirty * @ni: ntfs inode to set dirty @@ -80,7 +87,7 @@ static ntfs_inode *__ntfs_inode_allocate(ntfs_volume *vol) { ntfs_inode *ni; - ni = (ntfs_inode*)calloc(1, sizeof(ntfs_inode)); + ni = (ntfs_inode*)ntfs_calloc(sizeof(ntfs_inode)); if (ni) ni->vol = vol; return ni; @@ -145,23 +152,22 @@ static void __ntfs_inode_release(ntfs_inode *ni) ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref) { s64 l; - ntfs_inode *ni; + ntfs_inode *ni = NULL; ntfs_attr_search_ctx *ctx; - int err = 0; STANDARD_INFORMATION *std_info; - ntfs_log_trace("Entering for inode 0x%llx.\n", MREF(mref)); + ntfs_log_enter("Entering for inode %lld\n", (long long)MREF(mref)); if (!vol) { errno = EINVAL; - return NULL; + goto out; } ni = __ntfs_inode_allocate(vol); if (!ni) - return NULL; + goto out; if (ntfs_file_record_read(vol, mref, &ni->mrec, NULL)) goto err_out; if (!(ni->mrec->flags & MFT_RECORD_IN_USE)) { - err = ENOENT; + errno = ENOENT; goto err_out; } ni->mft_no = MREF(mref); @@ -171,9 +177,7 @@ ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref) /* Receive some basic information about inode. */ if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED, 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { - err = errno; - ntfs_log_trace("Failed to receive STANDARD_INFORMATION " - "attribute.\n"); + ntfs_log_perror("No STANDARD_INFORMATION in base record\n"); goto put_err_out; } std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + @@ -196,7 +200,8 @@ ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref) if (!l) goto put_err_out; if (l > 0x40000) { - err = EIO; + errno = EIO; + ntfs_log_perror("Too large attrlist (%lld)\n", (long long)l); goto put_err_out; } ni->attr_list_size = l; @@ -207,7 +212,9 @@ ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref) if (!l) goto put_err_out; if (l != ni->attr_list_size) { - err = EIO; + errno = EIO; + ntfs_log_perror("Unexpected attrlist size (%lld <> %u)\n", + (long long)l, ni->attr_list_size); goto put_err_out; } get_size: @@ -232,17 +239,16 @@ get_size: } } ntfs_attr_put_search_ctx(ctx); +out: + ntfs_log_leave("\n"); return ni; + put_err_out: - if (!err) - err = errno; ntfs_attr_put_search_ctx(ctx); err_out: - if (!err) - err = errno; __ntfs_inode_release(ni); - errno = err; - return NULL; + ni = NULL; + goto out; } /** @@ -258,6 +264,8 @@ err_out: * If it is an extent inode, we disconnect it from its base inode before we * destroy it. * + * It is OK to pass NULL to this function, it is just noop in this case. + * * Return 0 on success or -1 on error with errno set to the error code. On * error, @ni has not been freed. The user should attempt to handle the error * and call ntfs_inode_close() again. The following error codes are defined: @@ -269,17 +277,19 @@ err_out: */ int ntfs_inode_close(ntfs_inode *ni) { + int ret = -1; + if (!ni) return 0; - ntfs_log_trace("Entering for inode 0x%llx.\n", (long long)ni->mft_no); + ntfs_log_enter("Entering for inode %lld\n", (long long)ni->mft_no); /* If we have dirty metadata, write it out. */ if (NInoDirty(ni) || NInoAttrListDirty(ni)) { if (ntfs_inode_sync(ni)) { if (errno != EIO) errno = EBUSY; - return -1; + goto err; } } /* Is this a base inode with mapped extent inodes? */ @@ -288,7 +298,7 @@ int ntfs_inode_close(ntfs_inode *ni) if (ntfs_inode_close(ni->extent_nis[0])) { if (errno != EIO) errno = EBUSY; - return -1; + goto err; } } } else if (ni->nr_extents == -1) { @@ -342,7 +352,10 @@ int ntfs_inode_close(ntfs_inode *ni) } __ntfs_inode_release(ni); - return 0; + ret = 0; +err: + ntfs_log_leave("\n"); + return ret; } /** @@ -373,17 +386,20 @@ int ntfs_inode_close(ntfs_inode *ni) ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni, const MFT_REF mref) { u64 mft_no = MREF_LE(mref); - ntfs_inode *ni; + ntfs_inode *ni = NULL; ntfs_inode **extent_nis; int i; if (!base_ni) { errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); return NULL; } - ntfs_log_trace("Opening extent inode 0x%llx (base mft record 0x%llx).\n", + + ntfs_log_enter("Opening extent inode %lld (base mft record %lld).\n", (unsigned long long)mft_no, (unsigned long long)base_ni->mft_no); + /* Is the extent inode already open and attached to the base inode? */ if (base_ni->nr_extents > 0) { extent_nis = base_ni->extent_nis; @@ -397,21 +413,20 @@ ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni, const MFT_REF mref) seq_no = MSEQNO_LE(mref); if (seq_no && seq_no != le16_to_cpu( ni->mrec->sequence_number)) { - ntfs_log_debug("Found stale extent mft reference! " - "Corrupt file system. Run chkdsk.\n"); errno = EIO; - return NULL; + ntfs_log_perror("Found stale extent mft " + "reference mft=%lld", + (long long)ni->mft_no); + goto out; } - /* We are done, return the extent inode. */ - return ni; + goto out; } } /* Wasn't there, we need to load the extent inode. */ ni = __ntfs_inode_allocate(base_ni->vol); if (!ni) - return NULL; - if (ntfs_file_record_read(base_ni->vol, le64_to_cpu(mref), &ni->mrec, - NULL)) + goto out; + if (ntfs_file_record_read(base_ni->vol, le64_to_cpu(mref), &ni->mrec, NULL)) goto err_out; ni->mft_no = mft_no; ni->nr_extents = -1; @@ -431,11 +446,13 @@ ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni, const MFT_REF mref) base_ni->extent_nis = extent_nis; } base_ni->extent_nis[base_ni->nr_extents++] = ni; +out: + ntfs_log_leave("\n"); return ni; err_out: __ntfs_inode_release(ni); - ntfs_log_perror("Failed to open extent inode"); - return NULL; + ni = NULL; + goto out; } /** @@ -476,8 +493,7 @@ int ntfs_inode_attach_all_extents(ntfs_inode *ni) while ((u8*)ale < ni->attr_list + ni->attr_list_size) { if (ni->mft_no != MREF_LE(ale->mft_reference) && prev_attached != MREF_LE(ale->mft_reference)) { - if (!ntfs_extent_inode_open(ni, - MREF_LE(ale->mft_reference))) { + if (!ntfs_extent_inode_open(ni, ale->mft_reference)) { ntfs_log_trace("Couldn't attach extent inode.\n"); return -1; } @@ -498,20 +514,17 @@ static int ntfs_inode_sync_standard_information(ntfs_inode *ni) { ntfs_attr_search_ctx *ctx; STANDARD_INFORMATION *std_info; - int err; - ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no); + ntfs_log_trace("Entering for inode %lld\n", (long long)ni->mft_no); ctx = ntfs_attr_get_search_ctx(ni, NULL); if (!ctx) return -1; if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED, - 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { - err = errno; - ntfs_log_trace("Failed to receive STANDARD_INFORMATION " - "attribute.\n"); + 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { + ntfs_log_perror("Failed to sync standard info (inode %lld)", + (long long)ni->mft_no); ntfs_attr_put_search_ctx(ctx); - errno = err; return -1; } std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + @@ -569,7 +582,7 @@ static int ntfs_inode_sync_file_name(ntfs_inode *ni) if (!err) err = errno; ntfs_log_perror("Failed to open inode %lld with index", - le64_to_cpu(fn->parent_directory)); + (long long)le64_to_cpu(fn->parent_directory)); continue; } ictx = ntfs_index_ctx_get(index_ni, NTFS_INDEX_I30, 4); @@ -578,7 +591,8 @@ static int ntfs_inode_sync_file_name(ntfs_inode *ni) err = errno; ntfs_log_perror("Failed to get index ctx, inode %lld", (long long)index_ni->mft_no); - ntfs_inode_close(index_ni); + if (ni != index_ni && ntfs_inode_close(index_ni) && !err) + err = errno; continue; } if (ntfs_index_lookup(fn, sizeof(FILE_NAME_ATTR), ictx)) { @@ -591,7 +605,8 @@ static int ntfs_inode_sync_file_name(ntfs_inode *ni) ntfs_log_perror("Index lookup failed, inode %lld", (long long)index_ni->mft_no); ntfs_index_ctx_put(ictx); - ntfs_inode_close(index_ni); + if (ni != index_ni && ntfs_inode_close(index_ni) && !err) + err = errno; continue; } /* Update flags and file size. */ @@ -651,6 +666,7 @@ err_out: */ int ntfs_inode_sync(ntfs_inode *ni) { + int ret = 0; int err = 0; if (!ni) { @@ -659,7 +675,7 @@ int ntfs_inode_sync(ntfs_inode *ni) return -1; } - ntfs_log_trace("Entering for inode %lld\n", (long long)ni->mft_no); + ntfs_log_enter("Entering for inode %lld\n", (long long)ni->mft_no); /* Update STANDARD_INFORMATION. */ if ((ni->mrec->flags & MFT_RECORD_IN_USE) && ni->nr_extents != -1 && @@ -669,8 +685,6 @@ int ntfs_inode_sync(ntfs_inode *ni) if (err != EIO) err = EBUSY; } - ntfs_log_perror("Failed to sync standard info (inode %lld)", - (long long)ni->mft_no); } /* Update FILE_NAME's in the index. */ @@ -770,10 +784,13 @@ sync_inode: } } - if (!err) - return 0; - errno = err; - return -1; + if (err) { + errno = err; + ret = -1; + } + + ntfs_log_leave("\n"); + return ret; } /** @@ -797,16 +814,16 @@ int ntfs_inode_add_attrlist(ntfs_inode *ni) ntfs_attr *na; if (!ni) { - ntfs_log_trace("Invalid arguments.\n"); errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); return -1; } - ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no); + ntfs_log_trace("inode %llu\n", (unsigned long long) ni->mft_no); if (NInoAttrList(ni) || ni->nr_extents) { - ntfs_log_trace("Inode already has got attribute list.\n"); errno = EEXIST; + ntfs_log_perror("Inode already has attribute list"); return -1; } @@ -814,7 +831,6 @@ int ntfs_inode_add_attrlist(ntfs_inode *ni) ctx = ntfs_attr_get_search_ctx(ni, NULL); if (!ctx) { err = errno; - ntfs_log_trace("Couldn't get search context.\n"); goto err_out; } /* Walk through all attributes. */ @@ -824,7 +840,7 @@ int ntfs_inode_add_attrlist(ntfs_inode *ni) if (ctx->attr->type == AT_ATTRIBUTE_LIST) { err = EIO; - ntfs_log_trace("Eeek! Attribute list already present.\n"); + ntfs_log_perror("Attribute list already present"); goto put_err_out; } @@ -864,7 +880,8 @@ int ntfs_inode_add_attrlist(ntfs_inode *ni) /* Check for real error occurred. */ if (errno != ENOENT) { err = errno; - ntfs_log_trace("Attribute lookup failed.\n"); + ntfs_log_perror("%s: Attribute lookup failed, inode %lld", + __FUNCTION__, (long long)ni->mft_no); goto put_err_out; } @@ -882,8 +899,7 @@ int ntfs_inode_add_attrlist(ntfs_inode *ni) offsetof(ATTR_RECORD, resident_end))) { /* Failed to free space. */ err = errno; - ntfs_log_trace("Failed to free space for " - "$ATTRIBUTE_LIST.\n"); + ntfs_log_perror("Failed to free space for attrlist"); goto rollback; } } @@ -892,7 +908,7 @@ int ntfs_inode_add_attrlist(ntfs_inode *ni) if (ntfs_resident_attr_record_add(ni, AT_ATTRIBUTE_LIST, NULL, 0, NULL, 0, 0) < 0) { err = errno; - ntfs_log_trace("Couldn't add $ATTRIBUTE_LIST to MFT record.\n"); + ntfs_log_perror("Couldn't add $ATTRIBUTE_LIST to MFT"); goto rollback; } @@ -900,12 +916,12 @@ int ntfs_inode_add_attrlist(ntfs_inode *ni) na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); if (!na) { err = errno; - ntfs_log_trace("Failed to open just added $ATTRIBUTE_LIST.\n"); + ntfs_log_perror("Failed to open just added $ATTRIBUTE_LIST"); goto remove_attrlist_record; } if (ntfs_attr_truncate(na, al_len)) { err = errno; - ntfs_log_trace("Failed to resize just added $ATTRIBUTE_LIST.\n"); + ntfs_log_perror("Failed to resize just added $ATTRIBUTE_LIST"); ntfs_attr_close(na); goto remove_attrlist_record;; } @@ -923,11 +939,9 @@ remove_attrlist_record: if (!ntfs_attr_lookup(AT_ATTRIBUTE_LIST, NULL, 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { if (ntfs_attr_record_rm(ctx)) - ntfs_log_trace("Rollback failed. Failed to remove attribute " - "list record.\n"); + ntfs_log_perror("Rollback failed to remove attrlist"); } else - ntfs_log_trace("Rollback failed. Couldn't find attribute list " - "record.\n"); + ntfs_log_perror("Rollback failed to find attrlist"); /* Setup back in-memory runlist. */ ni->attr_list = al; ni->attr_list_size = al_len; @@ -947,11 +961,10 @@ rollback: sle64_to_cpu(ale->lowest_vcn), NULL, 0, ctx)) { if (ntfs_attr_record_move_to(ctx, ni)) - ntfs_log_trace("Rollback failed. Couldn't " - "back attribute to base MFT record.\n"); + ntfs_log_perror("Rollback failed to " + "move attribute"); } else - ntfs_log_trace("Rollback failed. ntfs_attr_lookup " - "failed.\n"); + ntfs_log_perror("Rollback failed to find attr"); ntfs_attr_reinit_search_ctx(ctx); } ale = (ATTR_LIST_ENTRY*)((u8*)ale + le16_to_cpu(ale->length)); @@ -970,8 +983,8 @@ err_out: } /** - * ntfs_inode_free_space - free space in the MFT record of inode - * @ni: ntfs inode in which MFT record free space + * ntfs_inode_free_space - free space in the MFT record of an inode + * @ni: ntfs inode in which MFT record needs more free space * @size: amount of space needed to free * * Return 0 on success or -1 on error with errno set to the error code. @@ -979,16 +992,16 @@ err_out: int ntfs_inode_free_space(ntfs_inode *ni, int size) { ntfs_attr_search_ctx *ctx; - int freed, err; + int freed; if (!ni || size < 0) { - ntfs_log_trace("Invalid arguments.\n"); errno = EINVAL; + ntfs_log_perror("%s: ni=%p size=%d", __FUNCTION__, ni, size); return -1; } - ntfs_log_trace("Entering for inode 0x%llx, size %d.\n", - (long long) ni->mft_no, size); + ntfs_log_trace("Entering for inode %lld, size %d\n", + (unsigned long long)ni->mft_no, size); freed = (le32_to_cpu(ni->mrec->bytes_allocated) - le32_to_cpu(ni->mrec->bytes_in_use)); @@ -997,138 +1010,96 @@ int ntfs_inode_free_space(ntfs_inode *ni, int size) return 0; ctx = ntfs_attr_get_search_ctx(ni, NULL); - if (!ctx) { - err = errno; - ntfs_log_trace("Failed to get attribute search context.\n"); - errno = err; + if (!ctx) return -1; - } - /* - * Chkdsk complain if $STANDARD_INFORMATION is not in the base MFT - * record. FIXME: I'm not sure in this, need to recheck. For now simply - * do not move $STANDARD_INFORMATION at all. - * - * Also we can't move $ATTRIBUTE_LIST from base MFT_RECORD, so position - * search context on first attribute after $STANDARD_INFORMATION and - * $ATTRIBUTE_LIST. - * - * Why we reposition instead of simply skip this attributes during - * enumeration? Because in case we have got only in-memory attribute - * list ntfs_attr_lookup will fail when it will try to find - * $ATTRIBUTE_LIST. + * $STANDARD_INFORMATION and $ATTRIBUTE_LIST must stay in the base MFT + * record, so position search context on the first attribute after them. */ - if (ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, CASE_SENSITIVE, 0, NULL, - 0, ctx)) { - if (errno != ENOENT) { - err = errno; - ntfs_log_trace("Attribute lookup failed.\n"); - goto put_err_out; - } - if (ctx->attr->type == AT_END) { - err = ENOSPC; - goto put_err_out; - } - } + if (ntfs_attr_position(AT_FILE_NAME, ctx)) + goto put_err_out; while (1) { int record_size; - /* * Check whether attribute is from different MFT record. If so, * find next, because we don't need such. */ while (ctx->ntfs_ino->mft_no != ni->mft_no) { - if (ntfs_attr_lookup(AT_UNUSED, NULL, 0, CASE_SENSITIVE, - 0, NULL, 0, ctx)) { - err = errno; - if (errno != ENOENT) { - ntfs_log_trace("Attribute lookup failed.\n"); - } else - err = ENOSPC; +retry: + if (ntfs_attr_position(AT_UNUSED, ctx)) goto put_err_out; - } } + if (ntfs_inode_base(ctx->ntfs_ino)->mft_no == FILE_MFT && + ctx->attr->type == AT_DATA) + goto retry; + + if (ctx->attr->type == AT_INDEX_ROOT) + goto retry; + record_size = le32_to_cpu(ctx->attr->length); - /* Move away attribute. */ if (ntfs_attr_record_move_away(ctx, 0)) { - err = errno; - ntfs_log_trace("Failed to move out attribute.\n"); + ntfs_log_perror("Failed to move out attribute #2"); break; } freed += record_size; - /* Check whether we done. */ + /* Check whether we are done. */ if (size <= freed) { ntfs_attr_put_search_ctx(ctx); return 0; } - /* - * Reposition to first attribute after $STANDARD_INFORMATION and - * $ATTRIBUTE_LIST (see comments upwards). + * Reposition to first attribute after $STANDARD_INFORMATION + * and $ATTRIBUTE_LIST instead of simply skipping this attribute + * because in the case when we have got only in-memory attribute + * list then ntfs_attr_lookup will fail when it tries to find + * $ATTRIBUTE_LIST. */ ntfs_attr_reinit_search_ctx(ctx); - if (ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, CASE_SENSITIVE, 0, - NULL, 0, ctx)) { - if (errno != ENOENT) { - err = errno; - ntfs_log_trace("Attribute lookup failed.\n"); - break; - } - if (ctx->attr->type == AT_END) { - err = ENOSPC; - break; - } - } + if (ntfs_attr_position(AT_FILE_NAME, ctx)) + break; } put_err_out: ntfs_attr_put_search_ctx(ctx); - if (err == ENOSPC) - ntfs_log_trace("No attributes left that can be moved out.\n"); - errno = err; + if (errno == ENOSPC) + ntfs_log_trace("No attributes left that could be moved out.\n"); return -1; } /** - * ntfs_inode_update_atime - update access time for ntfs inode - * @ni: ntfs inode for which update access time + * ntfs_inode_update_times - update selected time fields for ntfs inode + * @ni: ntfs inode for which update time fields + * @mask: select which time fields should be updated * - * This function usually get called when user read not metadata from inode. - * Do not update time for system files. + * This function updates time fields to current time. Fields to update are + * selected using @mask (see enum @ntfs_time_update_flags for posssible values). */ -void ntfs_inode_update_atime(ntfs_inode *ni) +void ntfs_inode_update_times(ntfs_inode *ni, ntfs_time_update_flags mask) { - if (!NVolReadOnly(ni->vol) && !NVolNoATime(ni->vol) && (ni->mft_no >= - FILE_first_user || ni->mft_no == FILE_root)) { - ni->last_access_time = time(NULL); - NInoFileNameSetDirty(ni); - NInoSetDirty(ni); + time_t now; + + if (!ni) { + ntfs_log_error("%s(): Invalid arguments.\n", __FUNCTION__); + return; } -} -/** - * ntfs_inode_update_time - update all times for ntfs inode - * @ni: ntfs inode for which update times - * - * This function updates last access, mft and data change times. Usually - * get called when user write not metadata to inode. Do not update time for - * system files. - */ -void ntfs_inode_update_time(ntfs_inode *ni) -{ - if (!NVolReadOnly(ni->vol) && - (ni->mft_no >= FILE_first_user || ni->mft_no == FILE_root)) { - time_t now; + if ((ni->mft_no < FILE_first_user && ni->mft_no != FILE_root) || + NVolReadOnly(ni->vol) || !mask) + return; - now = time(NULL); + now = time(NULL); + if (mask & NTFS_UPDATE_ATIME) + ni->last_access_time = now; + if (mask & NTFS_UPDATE_MTIME) ni->last_data_change_time = now; + if (mask & NTFS_UPDATE_CTIME) ni->last_mft_change_time = now; - NInoFileNameSetDirty(ni); - NInoSetDirty(ni); - } + + NInoFileNameSetDirty(ni); + NInoSetDirty(ni); } /** diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/inode.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/inode.h index 1dba17375a..c7f1186e20 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/inode.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/inode.h @@ -1,10 +1,10 @@ /* * inode.h - Defines for NTFS inode handling. Originated from the Linux-NTFS project. * - * Copyright (c) 2001,2002 Anton Altaparmakov - * Copyright (c) 2004-2005 Yura Pakhuchiy + * Copyright (c) 2001-2004 Anton Altaparmakov + * Copyright (c) 2004-2007 Yura Pakhuchiy * Copyright (c) 2004-2005 Richard Russon - * Copyright (c) 2006 Szabolcs Szakacsits + * Copyright (c) 2006-2008 Szabolcs Szakacsits * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -85,16 +85,13 @@ typedef enum { #define NInoAttrListTestAndSetDirty(ni) test_and_set_nino_al_flag(ni, Dirty) #define NInoAttrListTestAndClearDirty(ni) test_and_clear_nino_al_flag(ni, Dirty) -#define NInoFileNameDirty(ni) \ - test_nino_flag(ni, FileNameDirty) -#define NInoFileNameSetDirty(ni) \ - set_nino_flag(ni, FileNameDirty) -#define NInoFileNameClearDirty(ni) \ - clear_nino_flag(ni, FileNameDirty) +#define NInoFileNameDirty(ni) test_nino_flag(ni, FileNameDirty) +#define NInoFileNameSetDirty(ni) set_nino_flag(ni, FileNameDirty) +#define NInoFileNameClearDirty(ni) clear_nino_flag(ni, FileNameDirty) #define NInoFileNameTestAndSetDirty(ni) \ - test_and_set_nino_flag(ni, FileNameDirty) + test_and_set_nino_flag(ni, FileNameDirty) #define NInoFileNameTestAndClearDirty(ni) \ - test_and_clear_nino_flag(ni, FileNameDirty) + test_and_clear_nino_flag(ni, FileNameDirty) /** * struct _ntfs_inode - The NTFS in-memory inode structure. @@ -130,12 +127,14 @@ struct _ntfs_inode { inode of the base mft record. */ }; - /* Temp: for directory handling */ - void *private_data; /* ntfs_dt containing this inode */ - int ref_count; - /* Below fields are valid only for base inode. */ - s64 data_size; /* Data size stored in the filename index. */ + + /* + * These two fields are used to sync filename index and guaranteed to be + * correct, however value in index itself maybe wrong (windows itself + * do not update them properly). + */ + s64 data_size; /* Data size of unnamed DATA attribute. */ s64 allocated_size; /* Allocated size stored in the filename index. (NOTE: Equal to allocated size of the unnamed data attribute for normal or @@ -143,12 +142,28 @@ struct _ntfs_inode { of the unnamed data attribute for sparse or compressed files.) */ + /* + * These four fields are copy of relevant fields from + * STANDARD_INFORMATION attribute and used to sync it and FILE_NAME + * attribute in the index. + */ time_t creation_time; time_t last_data_change_time; time_t last_mft_change_time; time_t last_access_time; }; +typedef enum { + NTFS_UPDATE_ATIME = 1 << 0, + NTFS_UPDATE_MTIME = 1 << 1, + NTFS_UPDATE_CTIME = 1 << 2, +} ntfs_time_update_flags; + +#define NTFS_UPDATE_MCTIME (NTFS_UPDATE_MTIME | NTFS_UPDATE_CTIME) +#define NTFS_UPDATE_AMCTIME (NTFS_UPDATE_ATIME | NTFS_UPDATE_MCTIME) + +extern ntfs_inode *ntfs_inode_base(ntfs_inode *ni); + extern ntfs_inode *ntfs_inode_allocate(ntfs_volume *vol); extern ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref); @@ -162,8 +177,7 @@ extern int ntfs_inode_attach_all_extents(ntfs_inode *ni); extern void ntfs_inode_mark_dirty(ntfs_inode *ni); -extern void ntfs_inode_update_atime(ntfs_inode *ni); -extern void ntfs_inode_update_time(ntfs_inode *ni); +extern void ntfs_inode_update_times(ntfs_inode *ni, ntfs_time_update_flags mask); extern int ntfs_inode_sync(ntfs_inode *ni); diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/layout.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/layout.h index a3d0b27ef0..8670557e16 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/layout.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/layout.h @@ -2340,17 +2340,18 @@ typedef struct { control entry in the data part of the index. */ } __attribute__((__packed__)) key; - /* The (optional) index data is inserted here when creating. */ - // VCN vcn; If INDEX_ENTRY_NODE bit in ie_flags is set, the last - // eight bytes of this index entry contain the virtual - // cluster number of the index block that holds the - // entries immediately preceding the current entry. - // - // If the key_length is zero, then the vcn immediately - // follows the INDEX_ENTRY_HEADER. - // - // The address of the vcn of "ie" INDEX_ENTRY is given by - // (char*)ie + le16_to_cpu(ie->length) - sizeof(VCN) + /* The (optional) index data is inserted here when creating. + VCN vcn; If INDEX_ENTRY_NODE bit in ie_flags is set, the last + eight bytes of this index entry contain the virtual + cluster number of the index block that holds the + entries immediately preceding the current entry. + + If the key_length is zero, then the vcn immediately + follows the INDEX_ENTRY_HEADER. + + The address of the vcn of "ie" INDEX_ENTRY is given by + (char*)ie + le16_to_cpu(ie->length) - sizeof(VCN) + */ } __attribute__((__packed__)) INDEX_ENTRY; /** diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/lcnalloc.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/lcnalloc.c index 45b76f67af..3a8d5ed79f 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/lcnalloc.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/lcnalloc.c @@ -3,7 +3,7 @@ * * Copyright (c) 2002-2004 Anton Altaparmakov * Copyright (c) 2004 Yura Pakhuchiy - * Copyright (c) 2004-2007 Szabolcs Szakacsits + * Copyright (c) 2004-2008 Szabolcs Szakacsits * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -51,12 +51,12 @@ * the update of the mapping pairs which converges to the cubic Faulhaber's * formula as the function of the number of extents (fragments, runs). */ -#define NTFS_LCNALLOC_BSIZE 1024 +#define NTFS_LCNALLOC_BSIZE 4096 #define NTFS_LCNALLOC_SKIP NTFS_LCNALLOC_BSIZE static void ntfs_cluster_set_zone_pos(LCN start, LCN end, LCN *pos, LCN tc) { - ntfs_log_trace("pos: %lld tc: %lld\n", (long long)*pos, tc); + ntfs_log_trace("pos: %lld tc: %lld\n", (long long)*pos, (long long)tc); if (tc >= end) *pos = start; @@ -85,10 +85,15 @@ static s64 max_empty_bit_range(unsigned char *buf, int size) int max_range = 0; s64 start_pos = -1; - ntfs_log_trace("Entering"); + ntfs_log_trace("Entering\n"); for (i = 0; i < size; i++, buf++) { + if (*buf == 0) { + run += 8; + continue; + } + for (j = 0; j < 8; j++) { int bit = *buf & (1 << j); @@ -115,7 +120,7 @@ static int bitmap_writeback(ntfs_volume *vol, s64 pos, s64 size, void *b, { s64 written; - ntfs_log_trace("Entering"); + ntfs_log_trace("Entering\n"); if (!*writeback) return 0; @@ -126,7 +131,8 @@ static int bitmap_writeback(ntfs_volume *vol, s64 pos, s64 size, void *b, if (written != size) { if (!written) errno = EIO; - ntfs_log_perror("Bitmap write error (%lld, %lld)", pos, size); + ntfs_log_perror("Bitmap write error (%lld, %lld)", + (long long)pos, (long long)size); return -1; } @@ -189,16 +195,17 @@ runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count, u8 has_guess, used_zone_pos; int err = 0, rlpos, rlsize, buf_size; - ntfs_log_trace("Entering with count = 0x%llx, start_lcn = 0x%llx, " + ntfs_log_enter("Entering with count = 0x%llx, start_lcn = 0x%llx, " "zone = %s_ZONE.\n", (long long)count, (long long) start_lcn, zone == MFT_ZONE ? "MFT" : "DATA"); + if (!vol || count < 0 || start_lcn < -1 || !vol->lcnbmp_na || (s8)zone < FIRST_ZONE || zone > LAST_ZONE) { errno = EINVAL; ntfs_log_perror("%s: vcn: %lld, count: %lld, lcn: %lld", __FUNCTION__, (long long)start_vcn, (long long)count, (long long)start_lcn); - return NULL; + goto out; } /* Return empty runlist if @count == 0 */ @@ -209,12 +216,12 @@ runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count, rl[0].lcn = LCN_RL_NOT_MAPPED; rl[0].length = 0; } - return rl; + goto out; } buf = ntfs_malloc(NTFS_LCNALLOC_BSIZE); if (!buf) - return NULL; + goto out; /* * If no @start_lcn was requested, use the current zone * position otherwise use the requested @start_lcn. @@ -272,7 +279,7 @@ runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count, bmp_pos &= ~7; writeback = 0; - while (1) { + while (lcn < buf_size) { byte = buf + (lcn >> 3); bit = 1 << (lcn & 7); if (has_guess) { @@ -305,25 +312,44 @@ runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count, /* Allocate the bitmap bit. */ *byte |= bit; writeback = 1; + if (vol->free_clusters <= 0) + ntfs_log_error("Non-positive free clusters " + "(%lld)!\n", + (long long)vol->free_clusters); + else + vol->free_clusters--; /* * Coalesce with previous run if adjacent LCNs. * Otherwise, append a new run. */ - if (prev_lcn == lcn + bmp_pos - prev_run_len && rlpos) + if (prev_lcn == lcn + bmp_pos - prev_run_len && rlpos) { + ntfs_log_debug("Cluster coalesce: prev_lcn: " + "%lld lcn: %lld bmp_pos: %lld " + "prev_run_len: %lld\n", + (long long)prev_lcn, + (long long)lcn, (long long)bmp_pos, + (long long)prev_run_len); rl[rlpos - 1].length = ++prev_run_len; - else { + } else { if (rlpos) rl[rlpos].vcn = rl[rlpos - 1].vcn + prev_run_len; - else + else { rl[rlpos].vcn = start_vcn; + ntfs_log_debug("Start_vcn: %lld\n", + (long long)start_vcn); + } rl[rlpos].lcn = prev_lcn = lcn + bmp_pos; rl[rlpos].length = prev_run_len = 1; rlpos++; } + ntfs_log_debug("RUN: %-16lld %-16lld %-16lld\n", + (long long)rl[rlpos - 1].vcn, + (long long)rl[rlpos - 1].lcn, + (long long)rl[rlpos - 1].length); /* Done? */ if (!--clusters) { if (used_zone_pos) @@ -425,7 +451,7 @@ switch_to_data1_zone: search_zone = 2; search_zone = 1; zone_start = vol->mft_zone_pos; zone_end = vol->mft_zone_end; - if (!zone_start == vol->mft_zone_start) + if (zone_start == vol->mft_zone_start) pass = 2; break; } @@ -455,13 +481,15 @@ done_ret: goto err_ret; } done_err_ret: - ntfs_log_debug("At done_err_ret (follows done_ret).\n"); free(buf); - if (!err) - return rl; - ntfs_log_trace("Failed to allocate clusters (%d)", errno); - errno = err; - return NULL; + if (err) { + errno = err; + ntfs_log_perror("Failed to allocate clusters"); + rl = NULL; + } +out: + ntfs_log_leave("\n"); + return rl; wb_err_ret: ntfs_log_trace("At wb_err_ret.\n"); @@ -474,6 +502,7 @@ err_ret: rl[rlpos].vcn = rl[rlpos - 1].vcn + rl[rlpos - 1].length; rl[rlpos].lcn = LCN_RL_NOT_MAPPED; rl[rlpos].length = 0; + ntfs_debug_runlist_dump(rl); ntfs_cluster_free_from_rl(vol, rl); free(rl); rl = NULL; @@ -490,6 +519,9 @@ err_ret: */ int ntfs_cluster_free_from_rl(ntfs_volume *vol, runlist *rl) { + s64 nr_freed = 0; + int ret = -1; + ntfs_log_trace("Entering.\n"); for (; rl->length; rl++) { @@ -497,15 +529,27 @@ int ntfs_cluster_free_from_rl(ntfs_volume *vol, runlist *rl) ntfs_log_trace("Dealloc lcn 0x%llx, len 0x%llx.\n", (long long)rl->lcn, (long long)rl->length); - if (rl->lcn >= 0 && ntfs_bitmap_clear_run(vol->lcnbmp_na, - rl->lcn, rl->length)) { - int eo = errno; - ntfs_log_trace("Eeek! Deallocation of clusters failed.\n"); - errno = eo; - return -1; + if (rl->lcn >= 0) { + if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn, + rl->length)) { + ntfs_log_perror("Cluster deallocation failed " + "(%lld, %lld)", + (long long)rl->lcn, + (long long)rl->length); + goto out; + } + nr_freed += rl->length ; } } - return 0; + + ret = 0; +out: + vol->free_clusters += nr_freed; + if (vol->free_clusters > vol->nr_clusters) + ntfs_log_error("Too many free clusters (%lld > %lld)!", + (long long)vol->free_clusters, + (long long)vol->nr_clusters); + return ret; } /** @@ -527,7 +571,8 @@ int ntfs_cluster_free_from_rl(ntfs_volume *vol, runlist *rl) int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, s64 count) { runlist *rl; - s64 nr_freed, delta, to_free; + s64 delta, to_free, nr_freed = 0; + int ret = -1; if (!vol || !vol->lcnbmp_na || !na || start_vcn < 0 || (count < 0 && count != -1)) { @@ -535,21 +580,23 @@ int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, s64 count) errno = EINVAL; return -1; } - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, count 0x%llx, " + + ntfs_log_enter("Entering for inode 0x%llx, attr 0x%x, count 0x%llx, " "vcn 0x%llx.\n", (unsigned long long)na->ni->mft_no, na->type, (long long)count, (long long)start_vcn); rl = ntfs_attr_find_vcn(na, start_vcn); if (!rl) { if (errno == ENOENT) - return 0; - else - return -1; + ret = 0; + goto leave; } if (rl->lcn < 0 && rl->lcn != LCN_HOLE) { errno = EIO; - return -1; + ntfs_log_perror("%s: Unexpected lcn (%lld)", __FUNCTION__, + (long long)rl->lcn); + goto leave; } /* Find the starting cluster inside the run that needs freeing. */ @@ -563,14 +610,10 @@ int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, s64 count) if (rl->lcn != LCN_HOLE) { /* Do the actual freeing of the clusters in this run. */ if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn + delta, - to_free)) - return -1; - /* We have freed @to_free real clusters. */ + to_free)) + goto leave; nr_freed = to_free; - } else { - /* No real clusters were freed. */ - nr_freed = 0; - } + } /* Go to the next run and adjust the number of clusters left to free. */ ++rl; @@ -586,11 +629,10 @@ int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, s64 count) // list support! (AIA) if (rl->lcn < 0 && rl->lcn != LCN_HOLE) { // FIXME: Eeek! We need rollback! (AIA) - ntfs_log_trace("Eeek! invalid lcn (= %lli). Should attempt " - "to map runlist! Leaving inconsistent " - "metadata!\n", (long long)rl->lcn); errno = EIO; - return -1; + ntfs_log_perror("%s: Invalid lcn (%lli)", + __FUNCTION__, (long long)rl->lcn); + goto out; } /* The number of clusters in this run that need freeing. */ @@ -599,18 +641,13 @@ int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, s64 count) to_free = count; if (rl->lcn != LCN_HOLE) { - /* Do the actual freeing of the clusters in the run. */ if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn, to_free)) { - int eo = errno; - // FIXME: Eeek! We need rollback! (AIA) - ntfs_log_trace("Eeek! bitmap clear run failed. " - "Leaving inconsistent metadata!\n"); - errno = eo; - return -1; + ntfs_log_perror("%s: Clearing bitmap run failed", + __FUNCTION__); + goto out; } - /* We have freed @to_free real clusters. */ nr_freed += to_free; } @@ -620,12 +657,20 @@ int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, s64 count) if (count != -1 && count != 0) { // FIXME: Eeek! BUG() - ntfs_log_trace("Eeek! count still not zero (= %lli). Leaving " - "inconsistent metadata!\n", (long long)count); errno = EIO; - return -1; + ntfs_log_perror("%s: count still not zero (%lld)", __FUNCTION__, + (long long)count); + goto out; } - /* Done. Return the number of actual clusters that were freed. */ - return nr_freed; + ret = nr_freed; +out: + vol->free_clusters += nr_freed ; + if (vol->free_clusters > vol->nr_clusters) + ntfs_log_error("Too many free clusters (%lld > %lld)!", + (long long)vol->free_clusters, + (long long)vol->nr_clusters); +leave: + ntfs_log_leave("\n"); + return ret; } diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/logfile.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/logfile.c index 3fb04f3dc8..277ad1424b 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/logfile.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/logfile.c @@ -3,7 +3,7 @@ * * Copyright (c) 2002-2005 Anton Altaparmakov * Copyright (c) 2005 Yura Pakhuchiy - * Copyright (c) 2005-2006 Szabolcs Szakacsits + * Copyright (c) 2005-2009 Szabolcs Szakacsits * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -676,8 +676,8 @@ BOOL ntfs_is_logfile_clean(ntfs_attr *log_na, RESTART_PAGE_HEADER *rp) */ if (ra->client_in_use_list != LOGFILE_NO_CLIENT && !(ra->flags & RESTART_VOLUME_IS_CLEAN)) { - ntfs_log_error("$LogFile indicates unclean shutdown (%d, %d)\n", - le16_to_cpu(ra->client_in_use_list), + ntfs_log_error("The disk contains an unclean file system (%d, " + "%d).\n", le16_to_cpu(ra->client_in_use_list), le16_to_cpu(ra->flags)); return FALSE; } diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/logging.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/logging.c index 0b0485fa61..8e23d8a6cb 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/logging.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/logging.c @@ -2,7 +2,7 @@ * logging.c - Centralised logging. Originated from the Linux-NTFS project. * * Copyright (c) 2005 Richard Russon - * Copyright (c) 2005-2006 Szabolcs Szakacsits + * Copyright (c) 2005-2008 Szabolcs Szakacsits * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -50,13 +50,16 @@ #define PATH_SEP '/' #endif -/* Colour prefixes and a suffix */ -static const char *col_green = "\e[32m"; -static const char *col_cyan = "\e[36m"; -static const char *col_yellow = "\e[01;33m"; -static const char *col_red = "\e[01;31m"; -static const char *col_redinv = "\e[01;07;31m"; -static const char *col_end = "\e[0m"; +#ifdef DEBUG +static int tab; +#endif + +/* Some gcc 3.x, 4.[01].X crash with internal compiler error. */ +#if __GNUC__ <= 3 || (__GNUC__ == 4 && __GNUC_MINOR__ <= 1) +# define BROKEN_GCC_FORMAT_ATTRIBUTE +#else +# define BROKEN_GCC_FORMAT_ATTRIBUTE __attribute__((format(printf, 6, 0))) +#endif /** * struct ntfs_logging - Control info for the logging system @@ -67,7 +70,7 @@ static const char *col_end = "\e[0m"; struct ntfs_logging { u32 levels; u32 flags; - ntfs_log_handler *handler; + ntfs_log_handler *handler BROKEN_GCC_FORMAT_ATTRIBUTE; }; /** @@ -76,7 +79,8 @@ struct ntfs_logging { */ static struct ntfs_logging ntfs_log = { #ifdef DEBUG - NTFS_LOG_LEVEL_DEBUG | NTFS_LOG_LEVEL_TRACE | + NTFS_LOG_LEVEL_DEBUG | NTFS_LOG_LEVEL_TRACE | NTFS_LOG_LEVEL_ENTER | + NTFS_LOG_LEVEL_LEAVE | #endif NTFS_LOG_LEVEL_INFO | NTFS_LOG_LEVEL_QUIET | NTFS_LOG_LEVEL_WARNING | NTFS_LOG_LEVEL_ERROR | NTFS_LOG_LEVEL_PERROR | NTFS_LOG_LEVEL_CRITICAL | @@ -183,7 +187,7 @@ u32 ntfs_log_clear_flags(u32 flags) return old; } - +#ifndef __HAIKU__ /** * ntfs_log_get_stream - Default output streams for logging levels * @level: Log level @@ -195,9 +199,6 @@ u32 ntfs_log_clear_flags(u32 flags) */ static FILE * ntfs_log_get_stream(u32 level) { -#if defined(__BEOS__) || defined(__HAIKU__) - return NULL; -#else FILE *stream; switch (level) { @@ -210,6 +211,8 @@ static FILE * ntfs_log_get_stream(u32 level) case NTFS_LOG_LEVEL_DEBUG: case NTFS_LOG_LEVEL_TRACE: + case NTFS_LOG_LEVEL_ENTER: + case NTFS_LOG_LEVEL_LEAVE: case NTFS_LOG_LEVEL_WARNING: case NTFS_LOG_LEVEL_ERROR: case NTFS_LOG_LEVEL_CRITICAL: @@ -220,7 +223,6 @@ static FILE * ntfs_log_get_stream(u32 level) } return stream; -#endif } /** @@ -273,7 +275,7 @@ static const char * ntfs_log_get_prefix(u32 level) return prefix; } - +#endif /** * ntfs_log_set_handler - Provide an alternate logging handler @@ -288,7 +290,7 @@ void ntfs_log_set_handler(ntfs_log_handler *handler) ntfs_log.handler = handler; #ifdef HAVE_SYSLOG_H if (handler == ntfs_log_handler_syslog) - openlog("libntfs", LOG_PID, LOG_USER); + openlog("ntfs-3g", LOG_PID, LOG_USER); #endif } else ntfs_log.handler = ntfs_log_handler_null; @@ -359,23 +361,28 @@ int ntfs_log_handler_syslog(const char *function __attribute__((unused)), void *data __attribute__((unused)), const char *format, va_list args) { - char log[LOG_LINE_LEN]; + char logbuf[LOG_LINE_LEN]; int ret, olderr = errno; - ret = vsnprintf(log, LOG_LINE_LEN, format, args); +#ifndef DEBUG + if ((level & NTFS_LOG_LEVEL_PERROR) && errno == ENOSPC) + return 1; +#endif + ret = vsnprintf(logbuf, LOG_LINE_LEN, format, args); if (ret < 0) { vsyslog(LOG_NOTICE, format, args); - return 1; + ret = 1; + goto out; } if ((LOG_LINE_LEN > ret + 3) && (level & NTFS_LOG_LEVEL_PERROR)) { - strncat(log, ": ", LOG_LINE_LEN - ret - 1); - strncat(log, strerror(olderr), LOG_LINE_LEN - (ret + 3)); - ret = strlen(log); + strncat(logbuf, ": ", LOG_LINE_LEN - ret - 1); + strncat(logbuf, strerror(olderr), LOG_LINE_LEN - (ret + 3)); + ret = strlen(logbuf); } - syslog(LOG_NOTICE, "%s", log); - + syslog(LOG_NOTICE, "%s", logbuf); +out: errno = olderr; return ret; } @@ -405,49 +412,30 @@ int ntfs_log_handler_syslog(const char *function __attribute__((unused)), int ntfs_log_handler_fprintf(const char *function, const char *file, int line, u32 level, void *data, const char *format, va_list args) { -#if defined(__BEOS__) || defined(__HAIKU__) +#ifdef __HAIKU__ return 0; #else +#ifdef DEBUG + int i; +#endif int ret = 0; int olderr = errno; FILE *stream; - const char *col_prefix = NULL; - const char *col_suffix = NULL; if (!data) /* Interpret data as a FILE stream. */ return 0; /* If it's NULL, we can't do anything. */ stream = (FILE*)data; - if (ntfs_log.flags & NTFS_LOG_FLAG_COLOUR) { - /* Pick a colour determined by the log level */ - switch (level) { - case NTFS_LOG_LEVEL_DEBUG: - col_prefix = col_green; - col_suffix = col_end; - break; - case NTFS_LOG_LEVEL_TRACE: - col_prefix = col_cyan; - col_suffix = col_end; - break; - case NTFS_LOG_LEVEL_WARNING: - col_prefix = col_yellow; - col_suffix = col_end; - break; - case NTFS_LOG_LEVEL_ERROR: - case NTFS_LOG_LEVEL_PERROR: - col_prefix = col_red; - col_suffix = col_end; - break; - case NTFS_LOG_LEVEL_CRITICAL: - col_prefix = col_redinv; - col_suffix = col_end; - break; - } +#ifdef DEBUG + if (level == NTFS_LOG_LEVEL_LEAVE) { + if (tab) + tab--; + return 0; } - - if (col_prefix) - ret += fprintf(stream, col_prefix); - + + for (i = 0; i < tab; i++) + ret += fprintf(stream, " "); +#endif if ((ntfs_log.flags & NTFS_LOG_FLAG_ONLYNAME) && (strchr(file, PATH_SEP))) /* Abbreviate the filename */ file = strrchr(file, PATH_SEP) + 1; @@ -462,7 +450,7 @@ int ntfs_log_handler_fprintf(const char *function, const char *file, ret += fprintf(stream, "(%d) ", line); if ((ntfs_log.flags & NTFS_LOG_FLAG_FUNCTION) || /* Source function */ - (level & NTFS_LOG_LEVEL_TRACE)) + (level & NTFS_LOG_LEVEL_TRACE) || (level & NTFS_LOG_LEVEL_ENTER)) ret += fprintf(stream, "%s(): ", function); ret += vfprintf(stream, format, args); @@ -470,14 +458,14 @@ int ntfs_log_handler_fprintf(const char *function, const char *file, if (level & NTFS_LOG_LEVEL_PERROR) ret += fprintf(stream, ": %s\n", strerror(olderr)); - if (col_suffix) - ret += fprintf(stream, col_suffix); - - +#ifdef DEBUG + if (level == NTFS_LOG_LEVEL_ENTER) + tab++; +#endif fflush(stream); errno = olderr; return ret; -#endif +#endif } /** @@ -526,7 +514,7 @@ int ntfs_log_handler_null(const char *function __attribute__((unused)), const ch int ntfs_log_handler_stdout(const char *function, const char *file, int line, u32 level, void *data, const char *format, va_list args) { -#if defined(__BEOS__) || defined(__HAIKU__) +#ifdef __HAIKU__ return 0; #else if (!data) @@ -561,9 +549,9 @@ int ntfs_log_handler_stdout(const char *function, const char *file, int ntfs_log_handler_outerr(const char *function, const char *file, int line, u32 level, void *data, const char *format, va_list args) { -#if defined(__BEOS__) || defined(__HAIKU__) +#ifdef __HAIKU__ return 0; -#else +#else if (!data) data = ntfs_log_get_stream(level); @@ -595,9 +583,9 @@ int ntfs_log_handler_outerr(const char *function, const char *file, int ntfs_log_handler_stderr(const char *function, const char *file, int line, u32 level, void *data, const char *format, va_list args) { -#if defined(__BEOS__) || defined(__HAIKU__) +#ifdef __HAIKU__ return 0; -#else +#else if (!data) data = stderr; @@ -633,10 +621,6 @@ BOOL ntfs_log_parse_option(const char *option) } else if (strcmp(option, "--log-trace") == 0) { ntfs_log_set_levels(NTFS_LOG_LEVEL_TRACE); return TRUE; - } else if ((strcmp(option, "--log-colour") == 0) || - (strcmp(option, "--log-color") == 0)) { - ntfs_log_set_flags(NTFS_LOG_FLAG_COLOUR); - return TRUE; } ntfs_log_debug("Unknown logging option '%s'\n", option); diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/logging.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/logging.h index bc84734f2c..5abd49104e 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/logging.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/logging.h @@ -1,7 +1,8 @@ /* * logging.h - Centralised logging. Originated from the Linux-NTFS project. * - * Copyright (c) 2005 Richard Russon + * Copyright (c) 2005 Richard Russon + * Copyright (c) 2007-2008 Szabolcs Szakacsits * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -37,7 +38,12 @@ typedef int (ntfs_log_handler)(const char *function, const char *file, int line, u32 level, void *data, const char *format, va_list args); /* Set the logging handler from one of the functions, below. */ +#ifdef __HAIKU__ void ntfs_log_set_handler(ntfs_log_handler *handler); +#else +void ntfs_log_set_handler(ntfs_log_handler *handler + __attribute__((format(printf, 6, 0)))); +#endif /* Logging handlers */ ntfs_log_handler ntfs_log_handler_syslog __attribute__((format(printf, 6, 0))); @@ -75,6 +81,8 @@ int ntfs_log_redirect(const char *function, const char *file, int line, #define NTFS_LOG_LEVEL_ERROR (1 << 7) /* Operation failed, no damage done */ #define NTFS_LOG_LEVEL_PERROR (1 << 8) /* Message : standard error description */ #define NTFS_LOG_LEVEL_CRITICAL (1 << 9) /* Operation failed,damage may have occurred */ +#define NTFS_LOG_LEVEL_ENTER (1 << 10) /* Enter a function */ +#define NTFS_LOG_LEVEL_LEAVE (1 << 11) /* Leave a function */ /* Logging style flags - Manage the style of the output */ #define NTFS_LOG_FLAG_PREFIX (1 << 0) /* Prefix messages with "ERROR: ", etc */ @@ -82,7 +90,6 @@ int ntfs_log_redirect(const char *function, const char *file, int line, #define NTFS_LOG_FLAG_LINE (1 << 2) /* Show the line number of the message */ #define NTFS_LOG_FLAG_FUNCTION (1 << 3) /* Show the function name containing the message */ #define NTFS_LOG_FLAG_ONLYNAME (1 << 4) /* Only display the filename, not the pathname */ -#define NTFS_LOG_FLAG_COLOUR (1 << 5) /* Colour highlight some messages */ /* Macros to simplify logging. One for each level defined above. * Note, ntfs_log_debug/trace have effect only if DEBUG is defined. @@ -102,9 +109,13 @@ int ntfs_log_redirect(const char *function, const char *file, int line, #ifdef DEBUG #define ntfs_log_debug(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_DEBUG,NULL,FORMAT,##ARGS) #define ntfs_log_trace(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_TRACE,NULL,FORMAT,##ARGS) +#define ntfs_log_enter(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_ENTER,NULL,FORMAT,##ARGS) +#define ntfs_log_leave(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_LEAVE,NULL,FORMAT,##ARGS) #else #define ntfs_log_debug(FORMAT, ARGS...)do {} while (0) #define ntfs_log_trace(FORMAT, ARGS...)do {} while (0) +#define ntfs_log_enter(FORMAT, ARGS...)do {} while (0) +#define ntfs_log_leave(FORMAT, ARGS...)do {} while (0) #endif /* DEBUG */ #endif /* _LOGGING_H_ */ diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/mft.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/mft.c index 8220eade41..dc47f61f76 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/mft.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/mft.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2004 Anton Altaparmakov * Copyright (c) 2004-2005 Richard Russon - * Copyright (c) 2004-2006 Szabolcs Szakacsits + * Copyright (c) 2004-2008 Szabolcs Szakacsits * Copyright (c) 2005 Yura Pakhuchiy * * This program/include file is free software; you can redistribute it and/or @@ -81,9 +81,12 @@ int ntfs_mft_records_read(const ntfs_volume *vol, const MFT_REF mref, s64 br; VCN m; - ntfs_log_trace("Entering for inode 0x%llx.\n", MREF(mref)); + ntfs_log_trace("inode %llu\n", (unsigned long long)MREF(mref)); + if (!vol || !vol->mft_na || !b || count < 0) { errno = EINVAL; + ntfs_log_perror("%s: b=%p count=%lld mft=%llu", __FUNCTION__, + b, (long long)count, (unsigned long long)MREF(mref)); return -1; } m = MREF(mref); @@ -102,11 +105,9 @@ int ntfs_mft_records_read(const ntfs_volume *vol, const MFT_REF mref, if (br != count) { if (br != -1) errno = EIO; - if (br >= 0) - ntfs_log_debug("Error: partition is smaller than it should " - "be!\n"); - else - ntfs_log_perror("Error reading $Mft record(s)"); + ntfs_log_perror("Failed to read of MFT, mft=%llu count=%lld " + "br=%lld", (long long)m, (long long)count, + (long long)br); return -1; } return 0; @@ -144,7 +145,6 @@ int ntfs_mft_records_write(const ntfs_volume *vol, const MFT_REF mref, void *bmirr = NULL; int cnt = 0, res = 0; - ntfs_log_trace("Entering for inode 0x%llx.\n", MREF(mref)); if (!vol || !vol->mft_na || vol->mftmirr_size <= 0 || !b || count < 0) { errno = EINVAL; return -1; @@ -206,6 +206,40 @@ int ntfs_mft_records_write(const ntfs_volume *vol, const MFT_REF mref, return -1; } +int ntfs_mft_record_check(const ntfs_volume *vol, const MFT_REF mref, + MFT_RECORD *m) +{ + ATTR_RECORD *a; + int ret = -1; + + if (!ntfs_is_file_record(m->magic)) { + ntfs_log_error("Record %llu has no FILE magic (0x%x)\n", + (unsigned long long)MREF(mref), *(le32 *)m); + goto err_out; + } + + if (le32_to_cpu(m->bytes_allocated) != vol->mft_record_size) { + ntfs_log_error("Record %llu has corrupt allocation size " + "(%u <> %u)\n", (unsigned long long)MREF(mref), + vol->mft_record_size, + le32_to_cpu(m->bytes_allocated)); + goto err_out; + } + + a = (ATTR_RECORD *)((char *)m + le16_to_cpu(m->attrs_offset)); + if (p2n(a) < p2n(m) || (char *)a > (char *)m + vol->mft_record_size) { + ntfs_log_error("Record %llu is corrupt\n", + (unsigned long long)MREF(mref)); + goto err_out; + } + + ret = 0; +err_out: + if (ret) + errno = EIO; + return ret; +} + /** * ntfs_file_record_read - read a FILE record from the mft from disk * @vol: volume to read from @@ -241,41 +275,39 @@ int ntfs_file_record_read(const ntfs_volume *vol, const MFT_REF mref, MFT_RECORD **mrec, ATTR_RECORD **attr) { MFT_RECORD *m; - ATTR_RECORD *a; - int err; if (!vol || !mrec) { errno = EINVAL; + ntfs_log_perror("%s: mrec=%p", __FUNCTION__, mrec); return -1; } + m = *mrec; if (!m) { m = ntfs_malloc(vol->mft_record_size); if (!m) return -1; } - if (ntfs_mft_record_read(vol, mref, m)) { - err = errno; - goto read_failed; + if (ntfs_mft_record_read(vol, mref, m)) + goto err_out; + + if (ntfs_mft_record_check(vol, mref, m)) + goto err_out; + + if (MSEQNO(mref) && MSEQNO(mref) != le16_to_cpu(m->sequence_number)) { + ntfs_log_error("Record %llu has wrong SeqNo (%d <> %d)\n", + (unsigned long long)MREF(mref), MSEQNO(mref), + le16_to_cpu(m->sequence_number)); + errno = EIO; + goto err_out; } - if (!ntfs_is_file_record(m->magic)) - goto file_corrupt; - if (MSEQNO(mref) && MSEQNO(mref) != le16_to_cpu(m->sequence_number)) - goto file_corrupt; - a = (ATTR_RECORD*)((char*)m + le16_to_cpu(m->attrs_offset)); - if (p2n(a) < p2n(m) || (char*)a > (char*)m + vol->mft_record_size) - goto file_corrupt; *mrec = m; if (attr) - *attr = a; + *attr = (ATTR_RECORD*)((char*)m + le16_to_cpu(m->attrs_offset)); return 0; -file_corrupt: - ntfs_log_debug("ntfs_file_record_read(): file is corrupt.\n"); - err = EIO; -read_failed: +err_out: if (m != *mrec) free(m); - errno = err; return -1; } @@ -299,6 +331,7 @@ int ntfs_mft_record_layout(const ntfs_volume *vol, const MFT_REF mref, if (!vol || !mrec) { errno = EINVAL; + ntfs_log_perror("%s: mrec=%p", __FUNCTION__, mrec); return -1; } /* Aligned to 2-byte boundary. */ @@ -307,8 +340,8 @@ int ntfs_mft_record_layout(const ntfs_volume *vol, const MFT_REF mref, else { /* Abort if mref is > 32 bits. */ if (MREF(mref) & 0x0000ffff00000000ull) { - ntfs_log_debug("Mft reference exceeds 32 bits!\n"); errno = ERANGE; + ntfs_log_perror("Mft reference exceeds 32 bits"); return -1; } mrec->usa_ofs = cpu_to_le16((sizeof(MFT_RECORD) + 1) & ~1); @@ -371,29 +404,26 @@ int ntfs_mft_record_layout(const ntfs_volume *vol, const MFT_REF mref, int ntfs_mft_record_format(const ntfs_volume *vol, const MFT_REF mref) { MFT_RECORD *m; - int err; + int ret = -1; - if (!vol || !vol->mft_na) { - errno = EINVAL; - return -1; - } + ntfs_log_enter("Entering\n"); + m = ntfs_calloc(vol->mft_record_size); if (!m) - return -1; - if (ntfs_mft_record_layout(vol, mref, m)) { - err = errno; - free(m); - errno = err; - return -1; - } - if (ntfs_mft_record_write(vol, mref, m)) { - err = errno; - free(m); - errno = err; - return -1; - } + goto out; + + if (ntfs_mft_record_layout(vol, mref, m)) + goto free_m; + + if (ntfs_mft_record_write(vol, mref, m)) + goto free_m; + + ret = 0; +free_m: free(m); - return 0; +out: + ntfs_log_leave("\n"); + return ret; } static const char *es = " Leaving inconsistent metadata. Run chkdsk."; @@ -411,10 +441,19 @@ static inline unsigned int ntfs_ffz(unsigned int word) return ffs(~word) - 1; } +static int ntfs_is_mft(ntfs_inode *ni) +{ + if (ni && ni->mft_no == FILE_MFT) + return 1; + return 0; +} + #ifndef PAGE_SIZE #define PAGE_SIZE 4096 #endif +#define RESERVED_MFT_RECORDS 64 + /** * ntfs_mft_bitmap_find_free_rec - find a free mft record in the mft bitmap * @vol: volume on which to search for a free mft record @@ -439,7 +478,10 @@ static int ntfs_mft_bitmap_find_free_rec(ntfs_volume *vol, ntfs_inode *base_ni) u8 *buf, *byte; unsigned int size; u8 pass, b; + int ret = -1; + ntfs_log_enter("Entering\n"); + mftbmp_na = vol->mftbmp_na; /* * Set the end of the pass making sure we do not overflow the mft @@ -455,21 +497,25 @@ static int ntfs_mft_bitmap_find_free_rec(ntfs_volume *vol, ntfs_inode *base_ni) data_pos = vol->mft_data_pos; else data_pos = base_ni->mft_no + 1; - if (data_pos < 24) - data_pos = 24; + if (data_pos < RESERVED_MFT_RECORDS) + data_pos = RESERVED_MFT_RECORDS; if (data_pos >= pass_end) { - data_pos = 24; + data_pos = RESERVED_MFT_RECORDS; pass = 2; /* This happens on a freshly formatted volume. */ if (data_pos >= pass_end) { errno = ENOSPC; - return -1; + goto leave; } } + if (ntfs_is_mft(base_ni)) { + data_pos = 0; + pass = 2; + } pass_start = data_pos; buf = ntfs_malloc(PAGE_SIZE); if (!buf) - return -1; + goto leave; ntfs_log_debug("Starting bitmap search: pass %u, pass_start 0x%llx, " "pass_end 0x%llx, data_pos 0x%llx.\n", pass, @@ -488,10 +534,9 @@ static int ntfs_mft_bitmap_find_free_rec(ntfs_volume *vol, ntfs_inode *base_ni) size = ll; ll = ntfs_attr_pread(mftbmp_na, ofs, size, buf); if (ll < 0) { - ntfs_log_error("Failed to read mft bitmap " - "attribute, aborting.\n"); + ntfs_log_perror("Failed to read $MFT bitmap"); free(buf); - return -1; + goto leave; } ntfs_log_debug("Read 0x%llx bytes.\n", (long long)ll); /* If we read at least one byte, search @buf for a zero bit. */ @@ -506,14 +551,24 @@ static int ntfs_mft_bitmap_find_free_rec(ntfs_volume *vol, ntfs_inode *base_ni) byte ? *byte : -1, b); for (; bit < size && data_pos + bit < pass_end; bit &= ~7ull, bit += 8) { + /* + * If we're extending $MFT and running out of the first + * mft record (base record) then give up searching since + * no guarantee that the found record will be accessible. + */ + if (ntfs_is_mft(base_ni) && bit > 400) + goto out; + byte = buf + (bit >> 3); if (*byte == 0xff) continue; + /* Note: ffz() result must be zero based. */ b = ntfs_ffz((unsigned long)*byte); if (b < 8 && b >= (bit & 7)) { free(buf); - return data_pos + (bit & ~7ull) + b; + ret = data_pos + (bit & ~7ull) + b; + goto leave; } } ntfs_log_debug("After inner for loop: size 0x%x, " @@ -537,7 +592,7 @@ static int ntfs_mft_bitmap_find_free_rec(ntfs_volume *vol, ntfs_inode *base_ni) * part of the zone which we omitted earlier. */ pass_end = pass_start; - data_pos = pass_start = 24; + data_pos = pass_start = RESERVED_MFT_RECORDS; ntfs_log_debug("pass %i, pass_start 0x%llx, pass_end " "0x%llx.\n", pass, (long long)pass_start, (long long)pass_end); @@ -546,42 +601,59 @@ static int ntfs_mft_bitmap_find_free_rec(ntfs_volume *vol, ntfs_inode *base_ni) } } /* No free mft records in currently initialized mft bitmap. */ +out: free(buf); errno = ENOSPC; - return -1; +leave: + ntfs_log_leave("\n"); + return ret; +} + +static int ntfs_mft_attr_extend(ntfs_attr *na) +{ + int ret = STATUS_ERROR; + ntfs_log_enter("Entering\n"); + + if (!NInoAttrList(na->ni)) { + if (ntfs_inode_add_attrlist(na->ni)) { + ntfs_log_perror("%s: Can not add attrlist #3", __FUNCTION__); + goto out; + } + /* We can't sync the $MFT inode since its runlist is bogus. */ + ret = STATUS_KEEP_SEARCHING; + goto out; + } + + if (ntfs_attr_update_mapping_pairs(na, 0)) { + ntfs_log_perror("%s: MP update failed", __FUNCTION__); + goto out; + } + + ret = STATUS_OK; +out: + ntfs_log_leave("\n"); + return ret; } /** - * ntfs_mft_bitmap_extend_allocation - extend mft bitmap attribute by a cluster - * @vol: volume on which to extend the mft bitmap attribute - * - * Extend the mft bitmap attribute on the ntfs volume @vol by one cluster. - * - * Note: Only changes allocated_size, i.e. does not touch initialized_size or - * data_size. - * - * Return 0 on success and -1 on error with errno set to the error code. + * ntfs_mft_bitmap_extend_allocation_i - see ntfs_mft_bitmap_extend_allocation */ -static int ntfs_mft_bitmap_extend_allocation(ntfs_volume *vol) +static int ntfs_mft_bitmap_extend_allocation_i(ntfs_volume *vol) { LCN lcn; s64 ll = 0; /* silence compiler warning */ - ntfs_attr *mftbmp_na, *lcnbmp_na; + ntfs_attr *mftbmp_na; runlist_element *rl, *rl2 = NULL; /* silence compiler warning */ ntfs_attr_search_ctx *ctx; MFT_RECORD *m = NULL; /* silence compiler warning */ ATTR_RECORD *a = NULL; /* silence compiler warning */ - int ret, mp_size; + int err, mp_size; + int ret = STATUS_ERROR; u32 old_alen = 0; /* silence compiler warning */ - u8 b, tb; - struct { - u8 added_cluster:1; - u8 added_run:1; - u8 mp_rebuilt:1; - } status = { 0, 0, 0 }; + BOOL mp_rebuilt = FALSE; + BOOL update_mp = FALSE; mftbmp_na = vol->mftbmp_na; - lcnbmp_na = vol->lcnbmp_na; /* * Determine the last lcn of the mft bitmap. The allocated size of the * mft bitmap cannot be zero so we are ok to do this. @@ -593,73 +665,41 @@ static int ntfs_mft_bitmap_extend_allocation(ntfs_volume *vol) "cluster of mft bitmap attribute.\n"); if (rl) errno = EIO; - return -1; + return STATUS_ERROR; } lcn = rl->lcn + rl->length; - /* - * Attempt to get the cluster following the last allocated cluster by - * hand as it may be in the MFT zone so the allocator would not give it - * to us. - */ - ret = (int)ntfs_attr_pread(lcnbmp_na, lcn >> 3, 1, &b); - if (ret < 0) { - ntfs_log_error("Failed to read from lcn bitmap.\n"); - return -1; + + rl2 = ntfs_cluster_alloc(vol, rl[1].vcn, 1, lcn, DATA_ZONE); + if (!rl2) { + ntfs_log_error("Failed to allocate a cluster for " + "the mft bitmap.\n"); + return STATUS_ERROR; } - ntfs_log_debug("Read %i byte%s.\n", ret, ret == 1 ? "" : "s"); - tb = 1 << (lcn & 7ull); - if (ret == 1 && b != 0xff && !(b & tb)) { - /* Next cluster is free, allocate it. */ - b |= tb; - ret = (int)ntfs_attr_pwrite(lcnbmp_na, lcn >> 3, 1, &b); - if (ret < 1) { - ntfs_log_error("Failed to write to lcn " - "bitmap.\n"); - if (!ret) - errno = EIO; - return -1; - } - /* Update the mft bitmap runlist. */ - rl->length++; - rl[1].vcn++; - status.added_cluster = 1; - ntfs_log_debug("Appending one cluster to mft bitmap.\n"); - } else { - /* Allocate a cluster from the DATA_ZONE. */ - rl2 = ntfs_cluster_alloc(vol, rl[1].vcn, 1, lcn, DATA_ZONE); - if (!rl2) { - ntfs_log_error("Failed to allocate a cluster for " - "the mft bitmap.\n"); - return -1; - } - rl = ntfs_runlists_merge(mftbmp_na->rl, rl2); - if (!rl) { - ret = errno; - ntfs_log_error("Failed to merge runlists for mft " - "bitmap.\n"); - if (ntfs_cluster_free_from_rl(vol, rl2)) - ntfs_log_error("Failed to deallocate " - "cluster.%s\n", es); - free(rl2); - errno = ret; - return -1; - } - mftbmp_na->rl = rl; - status.added_run = 1; - ntfs_log_debug("Adding one run to mft bitmap.\n"); - /* Find the last run in the new runlist. */ - for (; rl[1].length; rl++) - ; + rl = ntfs_runlists_merge(mftbmp_na->rl, rl2); + if (!rl) { + err = errno; + ntfs_log_error("Failed to merge runlists for mft " + "bitmap.\n"); + if (ntfs_cluster_free_from_rl(vol, rl2)) + ntfs_log_error("Failed to deallocate " + "cluster.%s\n", es); + free(rl2); + errno = err; + return STATUS_ERROR; } + mftbmp_na->rl = rl; + ntfs_log_debug("Adding one run to mft bitmap.\n"); + /* Find the last run in the new runlist. */ + for (; rl[1].length; rl++) + ; /* * Update the attribute record as well. Note: @rl is the last * (non-terminator) runlist element of mft bitmap. */ ctx = ntfs_attr_get_search_ctx(mftbmp_na->ni, NULL); - if (!ctx) { - ntfs_log_error("Failed to get search context.\n"); + if (!ctx) goto undo_alloc; - } + if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name, mftbmp_na->name_len, 0, rl[1].vcn, NULL, 0, ctx)) { ntfs_log_error("Failed to find last attribute extent of " @@ -688,15 +728,17 @@ static int ntfs_mft_bitmap_extend_allocation(ntfs_volume *vol) old_alen = le32_to_cpu(a->length); if (ntfs_attr_record_resize(m, a, mp_size + le16_to_cpu(a->mapping_pairs_offset))) { - // TODO: Deal with this by moving this extent to a new mft - // record or by starting a new extent in a new mft record. - ntfs_log_error("Not enough space in this mft record to " - "accommodate extended mft bitmap attribute " - "extent. Cannot handle this yet.\n"); - errno = EOPNOTSUPP; + ntfs_log_info("extending $MFT bitmap\n"); + ret = ntfs_mft_attr_extend(vol->mftbmp_na); + if (ret == STATUS_OK) + goto ok; + if (ret == STATUS_ERROR) { + ntfs_log_perror("%s: ntfs_mft_attr_extend failed", __FUNCTION__); + update_mp = TRUE; + } goto undo_alloc; } - status.mp_rebuilt = 1; + mp_rebuilt = TRUE; /* Generate the mapping pairs array directly into the attr record. */ if (ntfs_mapping_pairs_build(vol, (u8*)a + le16_to_cpu(a->mapping_pairs_offset), mp_size, rl2, ll, @@ -727,14 +769,16 @@ static int ntfs_mft_bitmap_extend_allocation(ntfs_volume *vol) } a = ctx->attr; } +ok: mftbmp_na->allocated_size += vol->cluster_size; a->allocated_size = cpu_to_sle64(mftbmp_na->allocated_size); /* Ensure the changes make it to disk. */ ntfs_inode_mark_dirty(ctx->ntfs_ino); ntfs_attr_put_search_ctx(ctx); - return 0; + return STATUS_OK; + restore_undo_alloc: - ret = errno; + err = errno; ntfs_attr_reinit_search_ctx(ctx); if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name, mftbmp_na->name_len, 0, rl[1].vcn, NULL, 0, ctx)) { @@ -746,29 +790,27 @@ restore_undo_alloc: * The only thing that is now wrong is ->allocated_size of the * base attribute extent which chkdsk should be able to fix. */ - errno = ret; - return -1; + errno = err; + return STATUS_ERROR; } m = ctx->mrec; a = ctx->attr; a->highest_vcn = cpu_to_sle64(rl[1].vcn - 2); - errno = ret; + errno = err; undo_alloc: - ret = errno; - if (status.added_cluster) { - /* Truncate the last run in the runlist by one cluster. */ - rl->length--; - rl[1].vcn--; - } else if (status.added_run) { - lcn = rl->lcn; - /* Remove the last run from the runlist. */ - rl->lcn = rl[1].lcn; - rl->length = 0; - } - /* Deallocate the cluster. */ - if (ntfs_bitmap_clear_bit(lcnbmp_na, lcn)) + err = errno; + + /* Remove the last run from the runlist. */ + lcn = rl->lcn; + rl->lcn = rl[1].lcn; + rl->length = 0; + + /* FIXME: use an ntfs_cluster_free_* function */ + if (ntfs_bitmap_clear_bit(vol->lcnbmp_na, lcn)) ntfs_log_error("Failed to free cluster.%s\n", es); - if (status.mp_rebuilt) { + else + vol->free_clusters++; + if (mp_rebuilt) { if (ntfs_mapping_pairs_build(vol, (u8*)a + le16_to_cpu(a->mapping_pairs_offset), old_alen - le16_to_cpu(a->mapping_pairs_offset), @@ -780,12 +822,36 @@ undo_alloc: "record.%s\n", es); ntfs_inode_mark_dirty(ctx->ntfs_ino); } + if (update_mp) { + if (ntfs_attr_update_mapping_pairs(vol->mftbmp_na, 0)) + ntfs_log_perror("%s: MP update failed", __FUNCTION__); + } if (ctx) ntfs_attr_put_search_ctx(ctx); - errno = ret; - return -1; + errno = err; + return ret; } +/** + * ntfs_mft_bitmap_extend_allocation - extend mft bitmap attribute by a cluster + * @vol: volume on which to extend the mft bitmap attribute + * + * Extend the mft bitmap attribute on the ntfs volume @vol by one cluster. + * + * Note: Only changes allocated_size, i.e. does not touch initialized_size or + * data_size. + * + * Return 0 on success and -1 on error with errno set to the error code. + */ +static int ntfs_mft_bitmap_extend_allocation(ntfs_volume *vol) +{ + int ret; + + ntfs_log_enter("Entering\n"); + ret = ntfs_mft_bitmap_extend_allocation_i(vol); + ntfs_log_leave("\n"); + return ret; +} /** * ntfs_mft_bitmap_extend_initialized - extend mft bitmap initialized data * @vol: volume on which to extend the mft bitmap attribute @@ -805,13 +871,15 @@ static int ntfs_mft_bitmap_extend_initialized(ntfs_volume *vol) ntfs_attr_search_ctx *ctx; ATTR_RECORD *a; int err; + int ret = -1; + ntfs_log_enter("Entering\n"); + mftbmp_na = vol->mftbmp_na; ctx = ntfs_attr_get_search_ctx(mftbmp_na->ni, NULL); - if (!ctx) { - ntfs_log_error("Failed to get search context.\n"); - return -1; - } + if (!ctx) + goto out; + if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name, mftbmp_na->name_len, 0, 0, NULL, 0, ctx)) { ntfs_log_error("Failed to find first attribute extent of " @@ -836,7 +904,9 @@ static int ntfs_mft_bitmap_extend_initialized(ntfs_volume *vol) ll = ntfs_attr_pwrite(mftbmp_na, old_initialized_size, 8, &ll); if (ll == 8) { ntfs_log_debug("Wrote eight initialized bytes to mft bitmap.\n"); - return 0; + vol->free_mft_records += (8 * 8); + ret = 0; + goto out; } ntfs_log_error("Failed to write to mft bitmap.\n"); err = errno; @@ -844,10 +914,9 @@ static int ntfs_mft_bitmap_extend_initialized(ntfs_volume *vol) err = EIO; /* Try to recover from the error. */ ctx = ntfs_attr_get_search_ctx(mftbmp_na->ni, NULL); - if (!ctx) { - ntfs_log_error("Failed to get search context.%s\n", es); + if (!ctx) goto err_out; - } + if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name, mftbmp_na->name_len, 0, 0, NULL, 0, ctx)) { ntfs_log_error("Failed to find first attribute extent of " @@ -872,7 +941,9 @@ put_err_out: (long long)mftbmp_na->initialized_size); err_out: errno = err; - return -1; +out: + ntfs_log_leave("\n"); + return ret; } /** @@ -899,10 +970,13 @@ static int ntfs_mft_data_extend_allocation(ntfs_volume *vol) MFT_RECORD *m = NULL; /* silence compiler warning */ ATTR_RECORD *a = NULL; /* silence compiler warning */ int err, mp_size; + int ret = STATUS_ERROR; u32 old_alen = 0; /* silence compiler warning */ BOOL mp_rebuilt = FALSE; + BOOL update_mp = FALSE; - ntfs_log_debug("Extending mft data allocation.\n"); + ntfs_log_enter("Extending mft data allocation.\n"); + mft_na = vol->mft_na; /* * Determine the preferred allocation location, i.e. the last lcn of @@ -911,13 +985,15 @@ static int ntfs_mft_data_extend_allocation(ntfs_volume *vol) */ rl = ntfs_attr_find_vcn(mft_na, (mft_na->allocated_size - 1) >> vol->cluster_size_bits); + if (!rl || !rl->length || rl->lcn < 0) { ntfs_log_error("Failed to determine last allocated " "cluster of mft data attribute.\n"); if (rl) errno = EIO; - return -1; + goto out; } + lcn = rl->lcn + rl->length; ntfs_log_debug("Last lcn of mft data attribute is 0x%llx.\n", (long long)lcn); /* Minimum allocation is one mft record worth of clusters. */ @@ -928,18 +1004,16 @@ static int ntfs_mft_data_extend_allocation(ntfs_volume *vol) nr = vol->mft_record_size << 4 >> vol->cluster_size_bits; if (!nr) nr = min_nr; - ntfs_log_debug("Trying mft data allocation with default cluster count " - "%lli.\n", (long long)nr); + old_last_vcn = rl[1].vcn; do { rl2 = ntfs_cluster_alloc(vol, old_last_vcn, nr, lcn, MFT_ZONE); if (rl2) break; if (errno != ENOSPC || nr == min_nr) { - ntfs_log_error("Failed to allocate the minimal " - "number of clusters (%lli) for the " - "mft data attribute.\n", (long long)nr); - return -1; + ntfs_log_perror("Failed to allocate (%lld) clusters " + "for $MFT", (long long)nr); + goto out; } /* * There is not enough space to do the allocation, but there @@ -950,6 +1024,9 @@ static int ntfs_mft_data_extend_allocation(ntfs_volume *vol) ntfs_log_debug("Retrying mft data allocation with minimal cluster " "count %lli.\n", (long long)nr); } while (1); + + ntfs_log_debug("Allocated %lld clusters.\n", (long long)nr); + rl = ntfs_runlists_merge(mft_na->rl, rl2); if (!rl) { err = errno; @@ -960,19 +1037,18 @@ static int ntfs_mft_data_extend_allocation(ntfs_volume *vol) "from the mft data attribute.%s\n", es); free(rl2); errno = err; - return -1; + goto out; } mft_na->rl = rl; - ntfs_log_debug("Allocated %lli clusters.\n", nr); + /* Find the last run in the new runlist. */ for (; rl[1].length; rl++) ; /* Update the attribute record as well. */ ctx = ntfs_attr_get_search_ctx(mft_na->ni, NULL); - if (!ctx) { - ntfs_log_error("Failed to get search context.\n"); + if (!ctx) goto undo_alloc; - } + if (ntfs_attr_lookup(mft_na->type, mft_na->name, mft_na->name_len, 0, rl[1].vcn, NULL, 0, ctx)) { ntfs_log_error("Failed to find last attribute extent of " @@ -1001,13 +1077,13 @@ static int ntfs_mft_data_extend_allocation(ntfs_volume *vol) old_alen = le32_to_cpu(a->length); if (ntfs_attr_record_resize(m, a, mp_size + le16_to_cpu(a->mapping_pairs_offset))) { - // TODO: Deal with this by moving this extent to a new mft - // record or by starting a new extent in a new mft record. - // Note: Use the special reserved mft records and ensure that - // this extent is not required to find the mft record in - // question. - errno = EOPNOTSUPP; - ntfs_log_perror("Not enough space to extended mft data"); + ret = ntfs_mft_attr_extend(vol->mft_na); + if (ret == STATUS_OK) + goto ok; + if (ret == STATUS_ERROR) { + ntfs_log_perror("%s: ntfs_mft_attr_extend failed", __FUNCTION__); + update_mp = TRUE; + } goto undo_alloc; } mp_rebuilt = TRUE; @@ -1045,12 +1121,17 @@ static int ntfs_mft_data_extend_allocation(ntfs_volume *vol) } a = ctx->attr; } +ok: mft_na->allocated_size += nr << vol->cluster_size_bits; a->allocated_size = cpu_to_sle64(mft_na->allocated_size); /* Ensure the changes make it to disk. */ ntfs_inode_mark_dirty(ctx->ntfs_ino); ntfs_attr_put_search_ctx(ctx); - return 0; + ret = STATUS_OK; +out: + ntfs_log_leave("\n"); + return ret; + restore_undo_alloc: err = errno; ntfs_attr_reinit_search_ctx(ctx); @@ -1065,7 +1146,8 @@ restore_undo_alloc: * base attribute extent which chkdsk should be able to fix. */ errno = err; - return -1; + ret = STATUS_ERROR; + goto out; } m = ctx->mrec; a = ctx->attr; @@ -1091,10 +1173,14 @@ undo_alloc: "record.%s\n", es); ntfs_inode_mark_dirty(ctx->ntfs_ino); } + if (update_mp) { + if (ntfs_attr_update_mapping_pairs(vol->mft_na, 0)) + ntfs_log_perror("%s: MP update failed", __FUNCTION__); + } if (ctx) ntfs_attr_put_search_ctx(ctx); errno = err; - return -1; + goto out; } @@ -1105,7 +1191,7 @@ static int ntfs_mft_record_init(ntfs_volume *vol, s64 size) s64 old_data_initialized, old_data_size; ntfs_attr_search_ctx *ctx; - ntfs_log_trace("Entering\n"); + ntfs_log_enter("Entering\n"); /* NOTE: Caller must sanity check vol, vol->mft_na and vol->mftbmp_na */ @@ -1125,7 +1211,7 @@ static int ntfs_mft_record_init(ntfs_volume *vol, s64 size) (long long)mft_na->data_size, (long long)mft_na->initialized_size); while (size > mft_na->allocated_size) { - if (ntfs_mft_data_extend_allocation(vol)) + if (ntfs_mft_data_extend_allocation(vol) == STATUS_ERROR) goto out; ntfs_log_debug("Status of mft data after allocation extension: " "allocated_size 0x%llx, data_size 0x%llx, " @@ -1152,17 +1238,16 @@ static int ntfs_mft_record_init(ntfs_volume *vol, s64 size) mft_na->data_size = mft_na->initialized_size; ntfs_log_debug("Initializing mft record 0x%llx.\n", (long long)ll2); if (ntfs_mft_record_format(vol, ll2) < 0) { - ntfs_log_error("Failed to format mft record.\n"); + ntfs_log_perror("Failed to format mft record"); goto undo_data_init; } } /* Update the mft data attribute record to reflect the new sizes. */ ctx = ntfs_attr_get_search_ctx(mft_na->ni, NULL); - if (!ctx) { - ntfs_log_error("Failed to get search context.\n"); + if (!ctx) goto undo_data_init; - } + if (ntfs_attr_lookup(mft_na->type, mft_na->name, mft_na->name_len, 0, 0, NULL, 0, ctx)) { ntfs_log_error("Failed to find first attribute extent of " @@ -1172,6 +1257,7 @@ static int ntfs_mft_record_init(ntfs_volume *vol, s64 size) } ctx->attr->initialized_size = cpu_to_sle64(mft_na->initialized_size); ctx->attr->data_size = cpu_to_sle64(mft_na->data_size); + ctx->attr->allocated_size = cpu_to_sle64(mft_na->allocated_size); /* Ensure the changes make it to disk. */ ntfs_inode_mark_dirty(ctx->ntfs_ino); @@ -1187,17 +1273,14 @@ static int ntfs_mft_record_init(ntfs_volume *vol, s64 size) if (mft_na->data_size > mft_na->allocated_size || mft_na->initialized_size > mft_na->data_size) NTFS_BUG("mft_na sanity checks failed"); - // BUG_ON(mft_na->initialized_size > mft_na->data_size); - // BUG_ON(mft_na->data_size > mft_na->allocated_size); /* Sync MFT to minimize data loss if there won't be clean unmount. */ - if (ntfs_inode_sync(mft_na->ni)) { - ntfs_log_error("Failed to sync $MFT."); + if (ntfs_inode_sync(mft_na->ni)) goto undo_data_init; - } ret = 0; out: + ntfs_log_leave("\n"); return ret; undo_data_init: @@ -1206,6 +1289,215 @@ undo_data_init: goto out; } +static int ntfs_mft_rec_init(ntfs_volume *vol, s64 size) +{ + int ret = -1; + ntfs_attr *mft_na, *mftbmp_na; + s64 old_data_initialized, old_data_size; + ntfs_attr_search_ctx *ctx; + + ntfs_log_enter("Entering\n"); + + mft_na = vol->mft_na; + mftbmp_na = vol->mftbmp_na; + + if (size > mft_na->allocated_size || size > mft_na->initialized_size) { + errno = EIO; + ntfs_log_perror("%s: unexpected $MFT sizes, see below", __FUNCTION__); + ntfs_log_error("$MFT: size=%lld allocated_size=%lld " + "data_size=%lld initialized_size=%lld\n", + (long long)size, + (long long)mft_na->allocated_size, + (long long)mft_na->data_size, + (long long)mft_na->initialized_size); + goto out; + } + + old_data_initialized = mft_na->initialized_size; + old_data_size = mft_na->data_size; + + /* Update the mft data attribute record to reflect the new sizes. */ + ctx = ntfs_attr_get_search_ctx(mft_na->ni, NULL); + if (!ctx) + goto undo_data_init; + + if (ntfs_attr_lookup(mft_na->type, mft_na->name, mft_na->name_len, 0, + 0, NULL, 0, ctx)) { + ntfs_log_error("Failed to find first attribute extent of " + "mft data attribute.\n"); + ntfs_attr_put_search_ctx(ctx); + goto undo_data_init; + } + ctx->attr->initialized_size = cpu_to_sle64(mft_na->initialized_size); + ctx->attr->data_size = cpu_to_sle64(mft_na->data_size); + + /* CHECKME: ctx->attr->allocation_size is already ok? */ + + /* Ensure the changes make it to disk. */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + + /* Sanity checks. */ + if (mft_na->data_size > mft_na->allocated_size || + mft_na->initialized_size > mft_na->data_size) + NTFS_BUG("mft_na sanity checks failed"); +out: + ntfs_log_leave("\n"); + return ret; + +undo_data_init: + mft_na->initialized_size = old_data_initialized; + mft_na->data_size = old_data_size; + goto out; +} + +static ntfs_inode *ntfs_mft_rec_alloc(ntfs_volume *vol) +{ + s64 ll, bit; + ntfs_attr *mft_na, *mftbmp_na; + MFT_RECORD *m; + ntfs_inode *ni = NULL; + ntfs_inode *base_ni; + int err; + u16 seq_no, usn; + + ntfs_log_enter("Entering\n"); + + mft_na = vol->mft_na; + mftbmp_na = vol->mftbmp_na; + + base_ni = mft_na->ni; + + bit = ntfs_mft_bitmap_find_free_rec(vol, base_ni); + if (bit >= 0) + goto found_free_rec; + + if (errno != ENOSPC) + goto out; + + errno = ENOSPC; + /* strerror() is intentionally used below, we want to log this error. */ + ntfs_log_error("No free mft record for $MFT: %s\n", strerror(errno)); + goto err_out; + +found_free_rec: + if (ntfs_bitmap_set_bit(mftbmp_na, bit)) { + ntfs_log_error("Failed to allocate bit in mft bitmap #2\n"); + goto err_out; + } + + ll = (bit + 1) << vol->mft_record_size_bits; + if (ll > mft_na->initialized_size) + if (ntfs_mft_rec_init(vol, ll) < 0) + goto undo_mftbmp_alloc; + /* + * We now have allocated and initialized the mft record. Need to read + * it from disk and re-format it, preserving the sequence number if it + * is not zero as well as the update sequence number if it is not zero + * or -1 (0xffff). + */ + m = ntfs_malloc(vol->mft_record_size); + if (!m) + goto undo_mftbmp_alloc; + + if (ntfs_mft_record_read(vol, bit, m)) { + free(m); + goto undo_mftbmp_alloc; + } + /* Sanity check that the mft record is really not in use. */ + if (ntfs_is_file_record(m->magic) && (m->flags & MFT_RECORD_IN_USE)) { + ntfs_log_error("Inode %lld is used but it wasn't marked in " + "$MFT bitmap. Fixed.\n", (long long)bit); + free(m); + goto undo_mftbmp_alloc; + } + + seq_no = m->sequence_number; + usn = *(u16*)((u8*)m + le16_to_cpu(m->usa_ofs)); + if (ntfs_mft_record_layout(vol, bit, m)) { + ntfs_log_error("Failed to re-format mft record.\n"); + free(m); + goto undo_mftbmp_alloc; + } + if (le16_to_cpu(seq_no)) + m->sequence_number = seq_no; + seq_no = le16_to_cpu(usn); + if (seq_no && seq_no != 0xffff) + *(u16*)((u8*)m + le16_to_cpu(m->usa_ofs)) = usn; + /* Set the mft record itself in use. */ + m->flags |= MFT_RECORD_IN_USE; + /* Now need to open an ntfs inode for the mft record. */ + ni = ntfs_inode_allocate(vol); + if (!ni) { + ntfs_log_error("Failed to allocate buffer for inode.\n"); + free(m); + goto undo_mftbmp_alloc; + } + ni->mft_no = bit; + ni->mrec = m; + /* + * If we are allocating an extent mft record, make the opened inode an + * extent inode and attach it to the base inode. Also, set the base + * mft record reference in the extent inode. + */ + ni->nr_extents = -1; + ni->base_ni = base_ni; + m->base_mft_record = MK_LE_MREF(base_ni->mft_no, + le16_to_cpu(base_ni->mrec->sequence_number)); + /* + * Attach the extent inode to the base inode, reallocating + * memory if needed. + */ + if (!(base_ni->nr_extents & 3)) { + ntfs_inode **extent_nis; + int i; + + i = (base_ni->nr_extents + 4) * sizeof(ntfs_inode *); + extent_nis = ntfs_malloc(i); + if (!extent_nis) { + free(m); + free(ni); + goto undo_mftbmp_alloc; + } + if (base_ni->nr_extents) { + memcpy(extent_nis, base_ni->extent_nis, + i - 4 * sizeof(ntfs_inode *)); + free(base_ni->extent_nis); + } + base_ni->extent_nis = extent_nis; + } + base_ni->extent_nis[base_ni->nr_extents++] = ni; + + /* Make sure the allocated inode is written out to disk later. */ + ntfs_inode_mark_dirty(ni); + /* Initialize time, allocated and data size in ntfs_inode struct. */ + ni->data_size = ni->allocated_size = 0; + ni->flags = 0; + ni->creation_time = ni->last_data_change_time = + ni->last_mft_change_time = + ni->last_access_time = time(NULL); + /* Update the default mft allocation position if it was used. */ + if (!base_ni) + vol->mft_data_pos = bit + 1; + /* Return the opened, allocated inode of the allocated mft record. */ + ntfs_log_error("allocated %sinode %lld\n", + base_ni ? "extent " : "", (long long)bit); +out: + ntfs_log_leave("\n"); + return ni; + +undo_mftbmp_alloc: + err = errno; + if (ntfs_bitmap_clear_bit(mftbmp_na, bit)) + ntfs_log_error("Failed to clear bit in mft bitmap.%s\n", es); + errno = err; +err_out: + if (!errno) + errno = EIO; + ni = NULL; + goto out; +} + /** * ntfs_mft_record_alloc - allocate an mft record on an ntfs volume * @vol: volume on which to allocate the mft record @@ -1294,32 +1586,37 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, ntfs_inode *base_ni) s64 ll, bit; ntfs_attr *mft_na, *mftbmp_na; MFT_RECORD *m; - ntfs_inode *ni; + ntfs_inode *ni = NULL; int err; u16 seq_no, usn; if (base_ni) - ntfs_log_trace("Entering (allocating an extent mft record for " - "base mft record 0x%llx).\n", - (long long)base_ni->mft_no); + ntfs_log_enter("Entering (allocating an extent mft record for " + "base mft record %lld).\n", + (long long)base_ni->mft_no); else - ntfs_log_trace("Entering (allocating a base mft record).\n"); + ntfs_log_enter("Entering (allocating a base mft record)\n"); if (!vol || !vol->mft_na || !vol->mftbmp_na) { errno = EINVAL; - return NULL; + goto out; } + if (ntfs_is_mft(base_ni)) { + ni = ntfs_mft_rec_alloc(vol); + goto out; + } + mft_na = vol->mft_na; mftbmp_na = vol->mftbmp_na; retry: bit = ntfs_mft_bitmap_find_free_rec(vol, base_ni); if (bit >= 0) { - ntfs_log_debug("Found free record (#1), bit 0x%llx.\n", + ntfs_log_debug("found free record (#1) at %lld\n", (long long)bit); goto found_free_rec; } if (errno != ENOSPC) - return NULL; + goto out; /* * No free mft records left. If the mft bitmap already covers more * than the currently used mft records, the next records are all free, @@ -1330,11 +1627,11 @@ retry: */ ll = mft_na->initialized_size >> vol->mft_record_size_bits; if (mftbmp_na->initialized_size << 3 > ll && - mftbmp_na->initialized_size > 3) { + mftbmp_na->initialized_size > RESERVED_MFT_RECORDS / 8) { bit = ll; - if (bit < 24) - bit = 24; - ntfs_log_debug("Found free record (#2), bit 0x%llx.\n", + if (bit < RESERVED_MFT_RECORDS) + bit = RESERVED_MFT_RECORDS; + ntfs_log_debug("found free record (#2) at %lld\n", (long long)bit); goto found_free_rec; } @@ -1349,10 +1646,17 @@ retry: (long long)mftbmp_na->data_size, (long long)mftbmp_na->initialized_size); if (mftbmp_na->initialized_size + 8 > mftbmp_na->allocated_size) { - /* Need to extend bitmap by one more cluster. */ - ntfs_log_debug("mftbmp: initialized_size + 8 > allocated_size.\n"); - if (ntfs_mft_bitmap_extend_allocation(vol)) + + int ret = ntfs_mft_bitmap_extend_allocation(vol); + + if (ret == STATUS_ERROR) goto err_out; + if (ret == STATUS_KEEP_SEARCHING) { + ret = ntfs_mft_bitmap_extend_allocation(vol); + if (ret != STATUS_OK) + goto err_out; + } + ntfs_log_debug("Status of mftbmp after allocation extension: " "allocated_size 0x%llx, data_size 0x%llx, " "initialized_size 0x%llx.\n", @@ -1374,14 +1678,13 @@ retry: (long long)mftbmp_na->allocated_size, (long long)mftbmp_na->data_size, (long long)mftbmp_na->initialized_size); - ntfs_log_debug("Found free record (#3), bit 0x%llx.\n", (long long)bit); + ntfs_log_debug("found free record (#3) at %lld\n", (long long)bit); found_free_rec: /* @bit is the found free mft record, allocate it in the mft bitmap. */ if (ntfs_bitmap_set_bit(mftbmp_na, bit)) { ntfs_log_error("Failed to allocate bit in mft bitmap.\n"); goto err_out; } - ntfs_log_debug("Set bit 0x%llx in mft bitmap.\n", (long long)bit); /* The mft bitmap is now uptodate. Deal with mft data attribute now. */ ll = (bit + 1) << vol->mft_record_size_bits; @@ -1400,7 +1703,6 @@ found_free_rec: goto undo_mftbmp_alloc; if (ntfs_mft_record_read(vol, bit, m)) { - ntfs_log_perror("Error reading mft %lld", (long long)bit); free(m); goto undo_mftbmp_alloc; } @@ -1459,7 +1761,7 @@ found_free_rec: free(ni); goto undo_mftbmp_alloc; } - if (base_ni->extent_nis) { + if (base_ni->nr_extents) { memcpy(extent_nis, base_ni->extent_nis, i - 4 * sizeof(ntfs_inode *)); free(base_ni->extent_nis); @@ -1480,8 +1782,11 @@ found_free_rec: if (!base_ni) vol->mft_data_pos = bit + 1; /* Return the opened, allocated inode of the allocated mft record. */ - ntfs_log_debug("Returning opened, allocated %sinode 0x%llx.\n", + ntfs_log_debug("allocated %sinode 0x%llx.\n", base_ni ? "extent " : "", (long long)bit); + vol->free_mft_records--; +out: + ntfs_log_leave("\n"); return ni; undo_mftbmp_alloc: @@ -1492,7 +1797,8 @@ undo_mftbmp_alloc: err_out: if (!errno) errno = EIO; - return NULL; + ni = NULL; + goto out; } /** @@ -1550,8 +1856,10 @@ int ntfs_mft_record_free(ntfs_volume *vol, ntfs_inode *ni) } /* Throw away the now freed inode. */ - if (!ntfs_inode_close(ni)) + if (!ntfs_inode_close(ni)) { + vol->free_mft_records++; return 0; + } err = errno; /* Rollback what we did... */ diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/mft.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/mft.h index b6bf18754f..bb15f0f315 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/mft.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/mft.h @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2002 Anton Altaparmakov * Copyright (c) 2004-2005 Richard Russon - * Copyright (c) 2006 Szabolcs Szakacsits + * Copyright (c) 2006-2008 Szabolcs Szakacsits * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -27,6 +27,7 @@ #include "volume.h" #include "inode.h" #include "layout.h" +#include "logging.h" extern int ntfs_mft_records_read(const ntfs_volume *vol, const MFT_REF mref, const s64 count, MFT_RECORD *b); @@ -49,9 +50,17 @@ extern int ntfs_mft_records_read(const ntfs_volume *vol, const MFT_REF mref, static __inline__ int ntfs_mft_record_read(const ntfs_volume *vol, const MFT_REF mref, MFT_RECORD *b) { - return ntfs_mft_records_read(vol, mref, 1, b); + int ret; + + ntfs_log_enter("Entering for inode %lld\n", (long long)MREF(mref)); + ret = ntfs_mft_records_read(vol, mref, 1, b); + ntfs_log_leave("\n"); + return ret; } +extern int ntfs_mft_record_check(const ntfs_volume *vol, const MFT_REF mref, + MFT_RECORD *m); + extern int ntfs_file_record_read(const ntfs_volume *vol, const MFT_REF mref, MFT_RECORD **mrec, ATTR_RECORD **attr); @@ -76,7 +85,12 @@ extern int ntfs_mft_records_write(const ntfs_volume *vol, const MFT_REF mref, static __inline__ int ntfs_mft_record_write(const ntfs_volume *vol, const MFT_REF mref, MFT_RECORD *b) { - return ntfs_mft_records_write(vol, mref, 1, b); + int ret; + + ntfs_log_enter("Entering for inode %lld\n", (long long)MREF(mref)); + ret = ntfs_mft_records_write(vol, mref, 1, b); + ntfs_log_leave("\n"); + return ret; } /** diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/misc.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/misc.c index 043482ff75..9e38347e47 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/misc.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/misc.c @@ -34,18 +34,3 @@ void *ntfs_malloc(size_t size) return p; } -#if defined(__BEOS__) || defined(__HAIKU__) -#include -int ntfs_snprintf(char *buff, size_t size, const char *format, ...) -{ - int ret; - char buffer[BUF_SIZE]; - va_list args; - va_start(args, format); - memset(buffer,0,BUF_SIZE); - ret = sprintf(buffer, format, args); - va_end(args); - strncpy(buff,buffer,size); - return ret; - } -#endif diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/misc.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/misc.h index 2282d900c9..67cc1eb670 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/misc.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/misc.h @@ -4,10 +4,5 @@ void *ntfs_calloc(size_t size); void *ntfs_malloc(size_t size); -#if defined(__BEOS__) || defined(__HAIKU__) -#define BUF_SIZE 16384 -int ntfs_snprintf(char *buff, size_t size, const char *format, ...); -#define snprintf ntfs_snprintf -#endif - #endif /* _NTFS_MISC_H_ */ + diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/mst.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/mst.c index 4cd1f15446..470942d64b 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/mst.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/mst.c @@ -2,7 +2,7 @@ * mst.c - Multi sector fixup handling code. Originated from the Linux-NTFS project. * * Copyright (c) 2000-2004 Anton Altaparmakov - * Copyright (c) 2006 Szabolcs Szakacsits + * Copyright (c) 2006-2009 Szabolcs Szakacsits * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -63,6 +63,9 @@ int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size) (u32)(usa_ofs + (usa_count * 2)) > size || (size >> NTFS_BLOCK_SIZE_BITS) != usa_count) { errno = EINVAL; + ntfs_log_perror("%s: magic: 0x%08x size: %d usa_ofs: %d " + "usa_count: %d", __FUNCTION__, *(le32 *)b, + size, usa_ofs, usa_count); return -1; } /* Position of usn in update sequence array. */ @@ -89,8 +92,12 @@ int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size) * Set the magic to "BAAD" and return failure. * Note that magic_BAAD is already converted to le32. */ - b->magic = magic_BAAD; errno = EIO; + ntfs_log_perror("Incomplete multi-sector transfer: " + "magic: 0x%08x size: %d usa_ofs: %d usa_count:" + " %d data: %d usn: %d", *(le32 *)b, size, + usa_ofs, usa_count, *data_pos, usn); + b->magic = magic_BAAD; return -1; } data_pos += NTFS_BLOCK_SIZE/sizeof(u16); @@ -142,6 +149,7 @@ int ntfs_mst_pre_write_fixup(NTFS_RECORD *b, const u32 size) if (!b || ntfs_is_baad_record(b->magic) || ntfs_is_hole_record(b->magic)) { errno = EINVAL; + ntfs_log_perror("%s: bad argument", __FUNCTION__); return -1; } /* Setup the variables. */ @@ -153,6 +161,7 @@ int ntfs_mst_pre_write_fixup(NTFS_RECORD *b, const u32 size) (u32)(usa_ofs + (usa_count * 2)) > size || (size >> NTFS_BLOCK_SIZE_BITS) != usa_count) { errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); return -1; } /* Position of usn in update sequence array. */ diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/runlist.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/runlist.c index 714bd0e67f..f39698de94 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/runlist.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/runlist.c @@ -3,7 +3,7 @@ * * Copyright (c) 2002-2005 Anton Altaparmakov * Copyright (c) 2002-2005 Richard Russon - * Copyright (c) 2002-2006 Szabolcs Szakacsits + * Copyright (c) 2002-2008 Szabolcs Szakacsits * Copyright (c) 2004 Yura Pakhuchiy * * This program/include file is free software; you can redistribute it and/or @@ -463,39 +463,10 @@ static runlist_element *ntfs_rl_split(runlist_element *dst, int dsize, /** - * ntfs_runlists_merge - merge two runlists into one - * @drl: original runlist to be worked on - * @srl: new runlist to be merged into @drl - * - * First we sanity check the two runlists @srl and @drl to make sure that they - * are sensible and can be merged. The runlist @srl must be either after the - * runlist @drl or completely within a hole (or unmapped region) in @drl. - * - * Merging of runlists is necessary in two cases: - * 1. When attribute lists are used and a further extent is being mapped. - * 2. When new clusters are allocated to fill a hole or extend a file. - * - * There are four possible ways @srl can be merged. It can: - * - be inserted at the beginning of a hole, - * - split the hole in two and be inserted between the two fragments, - * - be appended at the end of a hole, or it can - * - replace the whole hole. - * It can also be appended to the end of the runlist, which is just a variant - * of the insert case. - * - * On success, return a pointer to the new, combined, runlist. Note, both - * runlists @drl and @srl are deallocated before returning so you cannot use - * the pointers for anything any more. (Strictly speaking the returned runlist - * may be the same as @dst but this is irrelevant.) - * - * On error, return NULL, with errno set to the error code. Both runlists are - * left unmodified. The following error codes are defined: - * ENOMEM Not enough memory to allocate runlist array. - * EINVAL Invalid parameters were passed in. - * ERANGE The runlists overlap and cannot be merged. + * ntfs_runlists_merge_i - see ntfs_runlists_merge */ -runlist_element *ntfs_runlists_merge(runlist_element *drl, - runlist_element *srl) +static runlist_element *ntfs_runlists_merge_i(runlist_element *drl, + runlist_element *srl) { int di, si; /* Current index into @[ds]rl. */ int sstart; /* First index with lcn > LCN_RL_NOT_MAPPED. */ @@ -544,9 +515,8 @@ runlist_element *ntfs_runlists_merge(runlist_element *drl, /* Can't have an entirely unmapped source runlist. */ if (!srl[si].length) { - ntfs_log_debug("Eeek! ntfs_runlists_merge() received entirely " - "unmapped source runlist.\n"); errno = EINVAL; + ntfs_log_perror("%s: unmapped source runlist", __FUNCTION__); return NULL; } @@ -567,8 +537,8 @@ runlist_element *ntfs_runlists_merge(runlist_element *drl, /* Sanity check for illegal overlaps. */ if ((drl[di].vcn == srl[si].vcn) && (drl[di].lcn >= 0) && (srl[si].lcn >= 0)) { - ntfs_log_debug("Run lists overlap. Cannot merge!\n"); errno = ERANGE; + ntfs_log_perror("Run lists overlap. Cannot merge"); return NULL; } @@ -702,6 +672,49 @@ critical_error: return drl; } +/** + * ntfs_runlists_merge - merge two runlists into one + * @drl: original runlist to be worked on + * @srl: new runlist to be merged into @drl + * + * First we sanity check the two runlists @srl and @drl to make sure that they + * are sensible and can be merged. The runlist @srl must be either after the + * runlist @drl or completely within a hole (or unmapped region) in @drl. + * + * Merging of runlists is necessary in two cases: + * 1. When attribute lists are used and a further extent is being mapped. + * 2. When new clusters are allocated to fill a hole or extend a file. + * + * There are four possible ways @srl can be merged. It can: + * - be inserted at the beginning of a hole, + * - split the hole in two and be inserted between the two fragments, + * - be appended at the end of a hole, or it can + * - replace the whole hole. + * It can also be appended to the end of the runlist, which is just a variant + * of the insert case. + * + * On success, return a pointer to the new, combined, runlist. Note, both + * runlists @drl and @srl are deallocated before returning so you cannot use + * the pointers for anything any more. (Strictly speaking the returned runlist + * may be the same as @dst but this is irrelevant.) + * + * On error, return NULL, with errno set to the error code. Both runlists are + * left unmodified. The following error codes are defined: + * ENOMEM Not enough memory to allocate runlist array. + * EINVAL Invalid parameters were passed in. + * ERANGE The runlists overlap and cannot be merged. + */ +runlist_element *ntfs_runlists_merge(runlist_element *drl, + runlist_element *srl) +{ + runlist_element *rl; + + ntfs_log_enter("Entering\n"); + rl = ntfs_runlists_merge_i(drl, srl); + ntfs_log_leave("\n"); + return rl; +} + /** * ntfs_mapping_pairs_decompress - convert mapping pairs array to runlist * @vol: ntfs volume on which the attribute resides @@ -729,7 +742,7 @@ critical_error: * two into one, if that is possible (we check for overlap and discard the new * runlist if overlap present before returning NULL, with errno = ERANGE). */ -runlist_element *ntfs_mapping_pairs_decompress(const ntfs_volume *vol, +runlist_element *ntfs_mapping_pairs_decompress_i(const ntfs_volume *vol, const ATTR_RECORD *attr, runlist_element *old_rl) { VCN vcn; /* Current vcn. */ @@ -953,6 +966,17 @@ err_out: return NULL; } +runlist_element *ntfs_mapping_pairs_decompress(const ntfs_volume *vol, + const ATTR_RECORD *attr, runlist_element *old_rl) +{ + runlist_element *rle; + + ntfs_log_enter("Entering\n"); + rle = ntfs_mapping_pairs_decompress_i(vol, attr, old_rl); + ntfs_log_leave("\n"); + return rle; +} + /** * ntfs_rl_vcn_to_lcn - convert a vcn into a lcn given a runlist * @rl: runlist to use for conversion @@ -1642,16 +1666,16 @@ int ntfs_rl_sparse(runlist *rl) runlist *rlc; if (!rl) { - ntfs_log_trace("Invalid argument passed.\n"); errno = EINVAL; + ntfs_log_perror("%s: ", __FUNCTION__); return -1; } for (rlc = rl; rlc->length; rlc++) if (rlc->lcn < 0) { if (rlc->lcn != LCN_HOLE) { - ntfs_log_trace("Received unmapped runlist.\n"); errno = EINVAL; + ntfs_log_perror("%s: bad runlist", __FUNCTION__); return -1; } return 1; @@ -1672,16 +1696,16 @@ s64 ntfs_rl_get_compressed_size(ntfs_volume *vol, runlist *rl) s64 ret = 0; if (!rl) { - ntfs_log_trace("Invalid argument passed.\n"); errno = EINVAL; + ntfs_log_perror("%s: ", __FUNCTION__); return -1; } for (rlc = rl; rlc->length; rlc++) { if (rlc->lcn < 0) { if (rlc->lcn != LCN_HOLE) { - ntfs_log_trace("Received unmapped runlist.\n"); errno = EINVAL; + ntfs_log_perror("%s: bad runlist", __FUNCTION__); return -1; } } else diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/security.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/security.c index 926a1829af..4b54b699da 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/security.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/security.c @@ -43,6 +43,7 @@ #include "attrib.h" #include "security.h" #include "misc.h" +#include "bitmap.h" /* * The zero GUID. @@ -338,3 +339,32 @@ int ntfs_sd_add_everyone(ntfs_inode *ni) return ret; } +/** + * ntfs_security_hash - calculate the hash of a security descriptor + * @sd: self-relative security descriptor whose hash to calculate + * @length: size in bytes of the security descritor @sd + * + * Calculate the hash of the self-relative security descriptor @sd of length + * @length bytes. + * + * This hash is used in the $Secure system file as the primary key for the $SDH + * index and is also stored in the header of each security descriptor in the + * $SDS data stream as well as in the index data of both the $SII and $SDH + * indexes. In all three cases it forms part of the SDS_ENTRY_HEADER + * structure. + * + * Return the calculated security hash in little endian. + */ +le32 ntfs_security_hash(const SECURITY_DESCRIPTOR_RELATIVE *sd, const u32 len) +{ + const le32 *pos = (const le32 *)sd; + const le32 *end = pos + (len >> 2); + u32 hash = 0; + + while (pos < end) { + hash = le32_to_cpup(pos) + ntfs_rol32(hash, 3); + pos++; + } + return cpu_to_le32(hash); +} + diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/security.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/security.h index e903f67221..04cc30a9d2 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/security.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/security.h @@ -55,4 +55,7 @@ extern char *ntfs_sid_to_mbs(const SID *sid, char *sid_str, extern void ntfs_generate_guid(GUID *guid); extern int ntfs_sd_add_everyone(ntfs_inode *ni); +extern le32 ntfs_security_hash(const SECURITY_DESCRIPTOR_RELATIVE *sd, + const u32 len); + #endif /* defined _NTFS_SECURITY_H */ diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/unistr.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/unistr.c index 7f12892f4e..c0703197c4 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/unistr.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/unistr.c @@ -2,7 +2,9 @@ * unistr.c - Unicode string handling. Originated from the Linux-NTFS project. * * Copyright (c) 2000-2004 Anton Altaparmakov - * Copyright (c) 2002-2006 Szabolcs Szakacsits + * Copyright (c) 2002-2009 Szabolcs Szakacsits + * Copyright (c) 2008-2009 Jean-Pierre Andre + * Copyright (c) 2008 Bernhard Kaindl * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -39,7 +41,11 @@ #ifdef HAVE_ERRNO_H #include #endif +#ifdef HAVE_LOCALE_H +#include +#endif +#include "compat.h" #include "attrib.h" #include "types.h" #include "unistr.h" @@ -47,6 +53,8 @@ #include "logging.h" #include "misc.h" +#define NOREVBOM 0 /* JPA rejecting U+FFFE and U+FFFF, open to debate */ + /* * IMPORTANT * ========= @@ -54,7 +62,9 @@ * All these routines assume that the Unicode characters are in little endian * encoding inside the strings!!! */ - +#ifndef __HAIKU__ +static int use_utf8 = 1; /* use UTF-8 encoding for file names */ +#endif /* * This is used by the name collation functions to quickly determine what * characters are (in)valid. @@ -373,7 +383,326 @@ int ntfs_file_values_compare(const FILE_NAME_ATTR *file_name_attr1, err_val, ic, upcase, upcase_len); } -#if defined(__BEOS__) || defined(__HAIKU__) +#ifndef __HAIKU__ +/* + NTFS uses Unicode (UTF-16LE [NTFS-3G uses UCS-2LE, which is enough + for now]) for path names, but the Unicode code points need to be + converted before a path can be accessed under NTFS. For 7 bit ASCII/ANSI, + glibc does this even without a locale in a hard-coded fashion as that + appears to be is easy because the low 7-bit ASCII range appears to be + available in all charsets but it does not convert anything if + there was some error with the locale setup or none set up like + when mount is called during early boot where he (by policy) do + not use locales (and may be not available if /usr is not yet mounted), + so this patch fixes the resulting issues for systems which use + UTF-8 and for others, specifying the locale in fstab brings them + the encoding which they want. + + If no locale is defined or there was a problem with setting one + up and whenever nl_langinfo(CODESET) returns a sting starting with + "ANSI", use an internal UCS-2LE <-> UTF-8 codeset converter to fix + the bug where NTFS-3G does not show any path names which include + international characters!!! (and also fails on creating them) as result. + + Author: Bernhard Kaindl + Jean-Pierre Andre made it compliant with RFC3629/RFC2781. +*/ + +/* + * Return the amount of 8-bit elements in UTF-8 needed (without the terminating + * null) to store a given UTF-16LE string. + * + * Return -1 with errno set if string has invalid byte sequence or too long. + */ +static int utf16_to_utf8_size(const ntfschar *ins, const int ins_len, int outs_len) +{ + int i, ret = -1; + int count = 0; + BOOL surrog; + + surrog = FALSE; + for (i = 0; i < ins_len && ins[i]; i++) { + unsigned short c = le16_to_cpu(ins[i]); + if (surrog) { + if ((c >= 0xdc00) && (c < 0xe000)) { + surrog = FALSE; + count += 4; + } else + goto fail; + } else + if (c < 0x80) + count++; + else if (c < 0x800) + count += 2; + else if (c < 0xd800) + count += 3; + else if (c < 0xdc00) + surrog = TRUE; +#if NOREVBOM + else if ((c >= 0xe000) && (c < 0xfffe)) +#else + else if (c >= 0xe000) +#endif + count += 3; + else + goto fail; + if (count > outs_len) { + errno = ENAMETOOLONG; + goto out; + } + } + if (surrog) + goto fail; + + ret = count; +out: + return ret; +fail: + errno = EILSEQ; + goto out; +} + +/* + * ntfs_utf16_to_utf8 - convert a little endian UTF16LE string to an UTF-8 string + * @ins: input utf16 string buffer + * @ins_len: length of input string in utf16 characters + * @outs: on return contains the (allocated) output multibyte string + * @outs_len: length of output buffer in bytes + * + * Return -1 with errno set if string has invalid byte sequence or too long. + */ +static int ntfs_utf16_to_utf8(const ntfschar *ins, const int ins_len, + char **outs, int outs_len) +{ + char *t; + int i, size, ret = -1; + ntfschar halfpair; + + halfpair = 0; + if (!*outs) + outs_len = PATH_MAX; + + size = utf16_to_utf8_size(ins, ins_len, outs_len); + + if (size < 0) + goto out; + + if (!*outs) { + outs_len = size + 1; + *outs = ntfs_malloc(outs_len); + if (!*outs) + goto out; + } + + t = *outs; + + for (i = 0; i < ins_len && ins[i]; i++) { + unsigned short c = le16_to_cpu(ins[i]); + /* size not double-checked */ + if (halfpair) { + if ((c >= 0xdc00) && (c < 0xe000)) { + *t++ = 0xf0 + (((halfpair + 64) >> 8) & 7); + *t++ = 0x80 + (((halfpair + 64) >> 2) & 63); + *t++ = 0x80 + ((c >> 6) & 15) + ((halfpair & 3) << 4); + *t++ = 0x80 + (c & 63); + halfpair = 0; + } else + goto fail; + } else if (c < 0x80) { + *t++ = c; + } else { + if (c < 0x800) { + *t++ = (0xc0 | ((c >> 6) & 0x3f)); + *t++ = 0x80 | (c & 0x3f); + } else if (c < 0xd800) { + *t++ = 0xe0 | (c >> 12); + *t++ = 0x80 | ((c >> 6) & 0x3f); + *t++ = 0x80 | (c & 0x3f); + } else if (c < 0xdc00) + halfpair = c; + else if (c >= 0xe000) { + *t++ = 0xe0 | (c >> 12); + *t++ = 0x80 | ((c >> 6) & 0x3f); + *t++ = 0x80 | (c & 0x3f); + } else + goto fail; + } + } + *t = '\0'; + ret = t - *outs; +out: + return ret; +fail: + errno = EILSEQ; + goto out; +} + +/* + * Return the amount of 16-bit elements in UTF-16LE needed + * (without the terminating null) to store given UTF-8 string. + * + * Return -1 with errno set if it's longer than PATH_MAX or string is invalid. + * + * Note: This does not check whether the input sequence is a valid utf8 string, + * and should be used only in context where such check is made! + */ +static int utf8_to_utf16_size(const char *s) +{ + int ret = -1; + unsigned int byte; + size_t count = 0; + + while ((byte = *((const unsigned char *)s++))) { + if (++count >= PATH_MAX) + goto fail; + if (byte >= 0xF5) { + errno = EILSEQ; + goto out; + } + if (!*s) + break; + if (byte >= 0xC0) + s++; + if (!*s) + break; + if (byte >= 0xE0) + s++; + if (!*s) + break; + if (byte >= 0xF0) { + s++; + if (++count >= PATH_MAX) + goto fail; + } + } + ret = count; +out: + return ret; +fail: + errno = ENAMETOOLONG; + goto out; +} +/* + * This converts one UTF-8 sequence to cpu-endian Unicode value + * within range U+0 .. U+10ffff and excluding U+D800 .. U+DFFF + * + * Return the number of used utf8 bytes or -1 with errno set + * if sequence is invalid. + */ +static int utf8_to_unicode(u32 *wc, const char *s) +{ + unsigned int byte = *((const unsigned char *)s); + + /* single byte */ + if (byte == 0) { + *wc = (u32) 0; + return 0; + } else if (byte < 0x80) { + *wc = (u32) byte; + return 1; + /* double byte */ + } else if (byte < 0xc2) { + goto fail; + } else if (byte < 0xE0) { + if (strlen(s) < 2) + goto fail; + if ((s[1] & 0xC0) == 0x80) { + *wc = ((u32)(byte & 0x1F) << 6) + | ((u32)(s[1] & 0x3F)); + return 2; + } else + goto fail; + /* three-byte */ + } else if (byte < 0xF0) { + if (strlen(s) < 3) + goto fail; + if (((s[1] & 0xC0) == 0x80) && ((s[2] & 0xC0) == 0x80)) { + *wc = ((u32)(byte & 0x0F) << 12) + | ((u32)(s[1] & 0x3F) << 6) + | ((u32)(s[2] & 0x3F)); + /* Check valid ranges */ +#if NOREVBOM + if (((*wc >= 0x800) && (*wc <= 0xD7FF)) + || ((*wc >= 0xe000) && (*wc <= 0xFFFD))) + return 3; +#else + if (((*wc >= 0x800) && (*wc <= 0xD7FF)) + || ((*wc >= 0xe000) && (*wc <= 0xFFFF))) + return 3; +#endif + } + goto fail; + /* four-byte */ + } else if (byte < 0xF5) { + if (strlen(s) < 4) + goto fail; + if (((s[1] & 0xC0) == 0x80) && ((s[2] & 0xC0) == 0x80) + && ((s[3] & 0xC0) == 0x80)) { + *wc = ((u32)(byte & 0x07) << 18) + | ((u32)(s[1] & 0x3F) << 12) + | ((u32)(s[2] & 0x3F) << 6) + | ((u32)(s[3] & 0x3F)); + /* Check valid ranges */ + if ((*wc <= 0x10ffff) && (*wc >= 0x10000)) + return 4; + } + goto fail; + } +fail: + errno = EILSEQ; + return -1; +} + +/** + * ntfs_utf8_to_utf16 - convert a UTF-8 string to a UTF-16LE string + * @ins: input multibyte string buffer + * @outs: on return contains the (allocated) output utf16 string + * @outs_len: length of output buffer in utf16 characters + * + * Return -1 with errno set. + */ +static int ntfs_utf8_to_utf16(const char *ins, ntfschar **outs) +{ + const char *t = ins; + u32 wc; + ntfschar *outpos; + int shorts, ret = -1; + + shorts = utf8_to_utf16_size(ins); + if (shorts < 0) + goto fail; + + if (!*outs) { + *outs = ntfs_malloc((shorts + 1) * sizeof(ntfschar)); + if (!*outs) + goto fail; + } + + outpos = *outs; + + while(1) { + int m = utf8_to_unicode(&wc, t); + if (m < 0) + goto fail; + if (wc < 0x10000) + *outpos++ = cpu_to_le16(wc); + else { + wc -= 0x10000; + *outpos++ = cpu_to_le16((wc >> 10) + 0xd800); + *outpos++ = cpu_to_le16((wc & 0x3ff) + 0xdc00); + } + if (m == 0) + break; + t += m; + } + + ret = --outpos - *outs; +fail: + return ret; +} +#endif + +#ifdef __HAIKU__ /* Encode a single wide character into a sequence of utf8 bytes. * Returns the number of bytes consumed, or 0 on error. */ @@ -562,10 +891,10 @@ int ntfs_ucstombs(const ntfschar *ins, const int ins_len, char **outs, int outs_ * ENAMETOOLONG Destination buffer is too small for input string. * ENOMEM Not enough memory to allocate destination buffer. */ -int ntfs_mbstoucs(const char *ins, ntfschar **outs, int outs_len) +int ntfs_mbstoucs(const char *ins, ntfschar **outs) { int in_len = strlen(ins); - int out_len = outs_len; + int out_len = 0; if(ntfs_duputf82uni((unsigned char*)ins,in_len,outs,&out_len)==0) return out_len; else @@ -620,6 +949,8 @@ int ntfs_ucstombs(const ntfschar *ins, const int ins_len, char **outs, errno = ENAMETOOLONG; return -1; } + if (use_utf8) + return ntfs_utf16_to_utf8(ins, ins_len, outs, outs_len); if (!mbs) { mbs_len = (ins_len + 1) * MB_CUR_MAX; mbs = ntfs_malloc(mbs_len); @@ -692,17 +1023,16 @@ err_out: * ntfs_mbstoucs - convert a multibyte string to a little endian Unicode string * @ins: input multibyte string buffer * @outs: on return contains the (allocated) output Unicode string - * @outs_len: length of output buffer in Unicode characters * * Convert the input multibyte string @ins, from the current locale into the * corresponding little endian, 2-byte Unicode string. * - * If *@outs is NULL, the function allocates the string and the caller is - * responsible for calling free(*@outs); when finished with it. + * The function allocates the string and the caller is responsible for calling + * free(*@outs); when finished with it. * * On success the function returns the number of Unicode characters written to * the output string *@outs (>= 0), not counting the terminating Unicode NULL - * character. If the output string buffer was allocated, *@outs is set to it. + * character. * * On error, -1 is returned, and errno is set to the error code. The following * error codes can be expected: @@ -712,7 +1042,7 @@ err_out: * ENAMETOOLONG Destination buffer is too small for input string. * ENOMEM Not enough memory to allocate destination buffer. */ -int ntfs_mbstoucs(const char *ins, ntfschar **outs, int outs_len) +int ntfs_mbstoucs(const char *ins, ntfschar **outs) { ntfschar *ucs; const char *s; @@ -726,12 +1056,10 @@ int ntfs_mbstoucs(const char *ins, ntfschar **outs, int outs_len) errno = EINVAL; return -1; } - ucs = *outs; - ucs_len = outs_len; - if (ucs && !ucs_len) { - errno = ENAMETOOLONG; - return -1; - } + + if (use_utf8) + return ntfs_utf8_to_utf16(ins, outs); + /* Determine the size of the multi-byte string in bytes. */ ins_size = strlen(ins); /* Determine the length of the multi-byte string. */ @@ -763,31 +1091,21 @@ int ntfs_mbstoucs(const char *ins, ntfschar **outs, int outs_len) } /* Add the NULL terminator. */ ins_len++; - if (!ucs) { - ucs_len = ins_len; - ucs = ntfs_malloc(ucs_len * sizeof(ntfschar)); - if (!ucs) - return -1; - } + ucs_len = ins_len; + ucs = ntfs_malloc(ucs_len * sizeof(ntfschar)); + if (!ucs) + return -1; #ifdef HAVE_MBSINIT memset(&mbstate, 0, sizeof(mbstate)); #else mbtowc(NULL, NULL, 0); #endif for (i = o = cnt = 0; i < ins_size; i += cnt, o++) { - /* Reallocate memory if necessary or abort. */ + /* Reallocate memory if necessary. */ if (o >= ucs_len) { ntfschar *tc; - if (ucs == *outs) { - errno = ENAMETOOLONG; - return -1; - } - /* - * We will never get here but hey, it's only a bit of - * extra code... - */ ucs_len = (ucs_len * sizeof(ntfschar) + 64) & ~63; - tc = (ntfschar*)realloc(ucs, ucs_len); + tc = realloc(ucs, ucs_len); if (!tc) goto err_out; ucs = tc; @@ -827,19 +1145,14 @@ int ntfs_mbstoucs(const char *ins, ntfschar **outs, int outs_len) #endif /* Now write the NULL character. */ ucs[o] = cpu_to_le16(L'\0'); - if (*outs != ucs) - *outs = ucs; + *outs = ucs; return o; err_out: - if (ucs != *outs) { - int eo = errno; - free(ucs); - errno = eo; - } + free(ucs); return -1; } -#endif // defined(__BEOS__) || defined(__HAIKU__) +#endif //__HAIKU__ /** * ntfs_upcase_table_build - build the default upcase table for NTFS @@ -928,7 +1241,7 @@ ntfschar *ntfs_str2ucs(const char *s, int *len) { ntfschar *ucs = NULL; - if (s && ((*len = ntfs_mbstoucs(s, &ucs, 0)) == -1)) { + if (s && ((*len = ntfs_mbstoucs(s, &ucs)) == -1)) { ntfs_log_perror("Couldn't convert '%s' to Unicode", s); return NULL; } @@ -958,3 +1271,26 @@ void ntfs_ucsfree(ntfschar *ucs) free(ucs); } +/* + * Define the character encoding to be used. + * Use UTF-8 unless specified otherwise. + */ +#ifndef __HAIKU__ + +int ntfs_set_char_encoding(const char *locale) +{ + use_utf8 = 0; + if (!locale || strstr(locale,"utf8") || strstr(locale,"UTF8") + || strstr(locale,"utf-8") || strstr(locale,"UTF-8")) + use_utf8 = 1; + else + if (setlocale(LC_ALL, locale)) + use_utf8 = 0; + else { + ntfs_log_error("Invalid locale, encoding to UTF-8\n"); + use_utf8 = 1; + } + return 0; /* always successful */ +} +#endif + diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/unistr.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/unistr.h index b45101e939..6de4bacc52 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/unistr.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/unistr.h @@ -57,7 +57,7 @@ extern int ntfs_file_values_compare(const FILE_NAME_ATTR *file_name_attr1, extern int ntfs_ucstombs(const ntfschar *ins, const int ins_len, char **outs, int outs_len); -extern int ntfs_mbstoucs(const char *ins, ntfschar **outs, int outs_len); +extern int ntfs_mbstoucs(const char *ins, ntfschar **outs); extern void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len); diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/unix_io.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/unix_io.c index 573097075f..75aebceced 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/unix_io.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/unix_io.c @@ -111,9 +111,7 @@ static int ntfs_device_unix_io_open(struct ntfs_device *dev, int flags) if ((flags & O_RDWR) != O_RDWR) NDevSetReadOnly(dev); - -/* locking not implemented in BeOS */ -#if !defined(__BEOS__) && !defined(__HAIKU__) + memset(&flk, 0, sizeof(flk)); if (NDevReadOnly(dev)) flk.l_type = F_RDLCK; @@ -129,7 +127,7 @@ static int ntfs_device_unix_io_open(struct ntfs_device *dev, int flags) ntfs_log_perror("Failed to close '%s'", dev->d_name); goto err_out; } -#endif + NDevSetOpen(dev); return 0; err_out: @@ -161,15 +159,13 @@ static int ntfs_device_unix_io_close(struct ntfs_device *dev) ntfs_log_perror("Failed to fsync device %s", dev->d_name); return -1; } -/* locking not implemented in BeOS */ -#if !defined(__BEOS__) && !defined(__HAIKU__) + memset(&flk, 0, sizeof(flk)); flk.l_type = F_UNLCK; flk.l_whence = SEEK_SET; flk.l_start = flk.l_len = 0LL; if (fcntl(DEV_FD(dev), F_SETLK, &flk)) ntfs_log_perror("Could not unlock %s", dev->d_name); -#endif if (close(DEV_FD(dev))) { ntfs_log_perror("Failed to close device %s", dev->d_name); return -1; @@ -247,11 +243,7 @@ static s64 ntfs_device_unix_io_write(struct ntfs_device *dev, const void *buf, static s64 ntfs_device_unix_io_pread(struct ntfs_device *dev, void *buf, s64 count, s64 offset) { -#if defined(__BEOS__) || defined(__HAIKU__) - return read_pos(DEV_FD(dev), offset, buf,count); -#else return pread(DEV_FD(dev), buf, count, offset); -#endif } /** @@ -273,11 +265,7 @@ static s64 ntfs_device_unix_io_pwrite(struct ntfs_device *dev, const void *buf, return -1; } NDevSetDirty(dev); -#if defined(__BEOS__) || defined(__HAIKU__) - return write_pos(DEV_FD(dev), offset, buf,count); -#else return pwrite(DEV_FD(dev), buf, count, offset); -#endif } /** diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/version.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/version.c deleted file mode 100644 index 76b742f88f..0000000000 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/version.c +++ /dev/null @@ -1,40 +0,0 @@ -/** - * version.c - Info about the NTFS library. Originated from the Linux-NTFS project. - * - * Copyright (c) 2005 Anton Altaparmakov - * Copyright (c) 2005 Richard Russon - * - * This program/include file is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program/include file is distributed in the hope that it will be - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program (in the main directory of the NTFS-3G - * distribution in the file COPYING); if not, write to the Free Software - * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "version.h" - -/* FIXME: merge libntfs into the user space NTFS driver */ -static const char *libntfs_version_string = "22:0:0"; - -/** - * ntfs_libntfs_version - query version number of the ntfs library libntfs - * - * Returns pointer to a text string representing the version of libntfs. - */ -const char *ntfs_libntfs_version(void) -{ - return libntfs_version_string; -} diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/version.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/version.h deleted file mode 100644 index 5f94d11a0e..0000000000 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/version.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * version.h - Info about the NTFS library. Originated from the Linux-NTFS project. - * - * Copyright (c) 2005 Anton Altaparmakov - * Copyright (c) 2005 Richard Russon - * - * This program/include file is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program/include file is distributed in the hope that it will be - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program (in the main directory of the NTFS-3G - * distribution in the file COPYING); if not, write to the Free Software - * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef _NTFS_VERSION_H_ -#define _NTFS_VERSION_H_ - -extern const char *ntfs_libntfs_version(void); - -#endif /* _NTFS_VERSION_H_ */ - diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/volume.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/volume.c index a3ee7ec840..c1aff923c8 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/volume.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/volume.c @@ -2,7 +2,7 @@ * volume.c - NTFS volume handling code. Originated from the Linux-NTFS project. * * Copyright (c) 2000-2006 Anton Altaparmakov - * Copyright (c) 2002-2006 Szabolcs Szakacsits + * Copyright (c) 2002-2009 Szabolcs Szakacsits * Copyright (c) 2004-2005 Richard Russon * * This program/include file is free software; you can redistribute it and/or @@ -49,7 +49,11 @@ #ifdef HAVE_LIMITS_H #include #endif +#ifdef HAVE_LOCALE_H +#include +#endif +#include "compat.h" #include "volume.h" #include "attrib.h" #include "mft.h" @@ -63,9 +67,51 @@ #include "logging.h" #include "misc.h" -#ifndef PATH_MAX -#define PATH_MAX 4096 -#endif +const char *ntfs_home = +"Ntfs-3g news, support and information: http://ntfs-3g.org\n"; + +static const char *invalid_ntfs_msg = +"The device '%s' doesn't seem to have a valid NTFS.\n" +"Maybe the wrong device is used? Or the whole disk instead of a\n" +"partition (e.g. /dev/sda, not /dev/sda1)? Or the other way around?\n"; + +static const char *corrupt_volume_msg = +"NTFS is either inconsistent, or there is a hardware fault, or it's a\n" +"SoftRAID/FakeRAID hardware. In the first case run chkdsk /f on Windows\n" +"then reboot into Windows twice. The usage of the /f parameter is very\n" +"important! If the device is a SoftRAID/FakeRAID then first activate\n" +"it and mount a different device under the /dev/mapper/ directory, (e.g.\n" +"/dev/mapper/nvidia_eahaabcc1). Please see the 'dmraid' documentation\n" +"for more details.\n"; + +static const char *hibernated_volume_msg = +"The NTFS partition is hibernated. Please resume and shutdown Windows\n" +"properly, or mount the volume read-only with the 'ro' mount option, or\n" +"mount the volume read-write with the 'remove_hiberfile' mount option.\n" +"For example type on the command line:\n" +"\n" +" mount -t ntfs-3g -o remove_hiberfile %s %s\n" +"\n"; + +static const char *unclean_journal_msg = +"Write access is denied because the disk wasn't safely powered\n" +"off and the 'norecover' mount option was specified.\n"; + +static const char *opened_volume_msg = +"Mount is denied because the NTFS volume is already exclusively opened.\n" +"The volume may be already mounted, or another software may use it which\n" +"could be identified for example by the help of the 'fuser' command.\n"; + +static const char *fakeraid_msg = +"Either the device is missing or it's powered down, or you have\n" +"SoftRAID hardware and must use an activated, different device under\n" +"/dev/mapper/, (e.g. /dev/mapper/nvidia_eahaabcc1) to mount NTFS.\n" +"Please see the 'dmraid' documentation for help.\n"; + +static const char *access_denied_msg = +"Please check '%s' and the ntfs-3g binary permissions,\n" +"and the mounting user ID. More explanation is provided at\n" +"http://ntfs-3g.org/support.html#unprivileged\n"; /** * ntfs_volume_alloc - Create an NTFS volume object and initialise it @@ -76,17 +122,15 @@ */ ntfs_volume *ntfs_volume_alloc(void) { - return calloc(1, sizeof(ntfs_volume)); + return ntfs_calloc(sizeof(ntfs_volume)); } - static void ntfs_attr_free(ntfs_attr **na) { if (na && *na) { ntfs_attr_close(*na); *na = NULL; - } else - ntfs_log_error("Tried to free NULL attribute pointer (%p)\n", na); + } } static int ntfs_inode_free(ntfs_inode **ni) @@ -96,8 +140,7 @@ static int ntfs_inode_free(ntfs_inode **ni) if (ni && *ni) { ret = ntfs_inode_close(*ni); *ni = NULL; - } else - ntfs_log_error("Tried to free NULL inode pointer (%p)\n", ni); + } return ret; } @@ -211,25 +254,14 @@ static int ntfs_mft_load(ntfs_volume *vol) ntfs_log_perror("Error reading $MFT"); goto error_exit; } - if (ntfs_is_baad_record(mb->magic)) { - ntfs_log_error("Incomplete multi sector transfer detected in " - "$MFT.\n"); - goto io_error_exit; - } - if (!ntfs_is_mft_record(mb->magic)) { - ntfs_log_error("$MFT has invalid magic.\n"); - goto io_error_exit; - } - ctx = ntfs_attr_get_search_ctx(vol->mft_ni, NULL); - if (!ctx) { - ntfs_log_perror("Failed to allocate attribute search context"); + + if (ntfs_mft_record_check(vol, 0, mb)) goto error_exit; - } - if (p2n(ctx->attr) < p2n(mb) || - (char*)ctx->attr > (char*)mb + vol->mft_record_size) { - ntfs_log_error("$MFT is corrupt.\n"); - goto io_error_exit; - } + + ctx = ntfs_attr_get_search_ctx(vol->mft_ni, NULL); + if (!ctx) + goto error_exit; + /* Find the $ATTRIBUTE_LIST attribute in $MFT if present. */ if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { @@ -429,13 +461,10 @@ ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, unsigned long flags) ntfs_volume *vol; NTFS_BOOT_SECTOR *bs; int eo; -#ifdef DEBUG - const char *OK = "OK\n"; - const char *FAILED = "FAILED\n"; -#endif if (!dev || !dev->d_ops || !dev->d_name) { errno = EINVAL; + ntfs_log_perror("%s: dev = %p", __FUNCTION__, dev); return NULL; } @@ -447,6 +476,7 @@ ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, unsigned long flags) vol = ntfs_volume_alloc(); if (!vol) goto error_exit; + /* Create the default upcase table. */ vol->upcase_len = 65536; vol->upcase = ntfs_malloc(vol->upcase_len * sizeof(ntfschar)); @@ -455,32 +485,29 @@ ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, unsigned long flags) ntfs_upcase_table_build(vol->upcase, vol->upcase_len * sizeof(ntfschar)); + if (flags & MS_RDONLY) NVolSetReadOnly(vol); - if (flags & MS_NOATIME) - NVolSetNoATime(vol); - ntfs_log_debug("Reading bootsector... "); - if (dev->d_ops->open(dev, NVolReadOnly(vol) ? O_RDONLY: O_RDWR)) { - ntfs_log_debug(FAILED); - ntfs_log_perror("Error opening partition device"); + + /* ...->open needs bracketing to compile with glibc 2.7 */ + if ((dev->d_ops->open)(dev, NVolReadOnly(vol) ? O_RDONLY: O_RDWR)) { + ntfs_log_perror("Error opening '%s'", dev->d_name); goto error_exit; } /* Attach the device to the volume. */ vol->dev = dev; + /* Now read the bootsector. */ br = ntfs_pread(dev, 0, sizeof(NTFS_BOOT_SECTOR), bs); if (br != sizeof(NTFS_BOOT_SECTOR)) { - ntfs_log_debug(FAILED); if (br != -1) errno = EINVAL; if (!br) - ntfs_log_error("Partition is smaller than bootsector " - "size.\n"); + ntfs_log_error("Failed to read bootsector (size=0)\n"); else ntfs_log_perror("Error reading bootsector"); goto error_exit; } - ntfs_log_debug(OK); if (!ntfs_boot_sector_is_ntfs(bs)) { errno = EINVAL; goto error_exit; @@ -498,9 +525,7 @@ ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, unsigned long flags) "%s\n", strerror(errno)); /* We now initialize the cluster allocator. */ - - mft_zone_size = min(vol->nr_clusters >> 3, /* 12.5% */ - 200 * 1000 * 1024 >> vol->cluster_size_bits); + mft_zone_size = vol->nr_clusters >> 3; /* 12.5% */ /* Setup the mft zone. */ vol->mft_zone_start = vol->mft_zone_pos = vol->mft_lcn; @@ -540,9 +565,9 @@ ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, unsigned long flags) * respective zone. */ vol->data1_zone_pos = vol->mft_zone_end; - ntfs_log_debug("data1_zone_pos = 0x%llx\n", vol->data1_zone_pos); + ntfs_log_debug("data1_zone_pos = %lld\n", (long long)vol->data1_zone_pos); vol->data2_zone_pos = 0; - ntfs_log_debug("data2_zone_pos = 0x%llx\n", vol->data2_zone_pos); + ntfs_log_debug("data2_zone_pos = %lld\n", (long long)vol->data2_zone_pos); /* Set the mft data allocation position to mft record 24. */ vol->mft_data_pos = 24; @@ -552,22 +577,16 @@ ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, unsigned long flags) */ /* Need to setup $MFT so we can use the library read functions. */ - ntfs_log_debug("Loading $MFT... "); if (ntfs_mft_load(vol) < 0) { - ntfs_log_debug(FAILED); ntfs_log_perror("Failed to load $MFT"); goto error_exit; } - ntfs_log_debug(OK); /* Need to setup $MFTMirr so we can use the write functions, too. */ - ntfs_log_debug("Loading $MFTMirr... "); if (ntfs_mftmirr_load(vol) < 0) { - ntfs_log_debug(FAILED); ntfs_log_perror("Failed to load $MFTMirr"); goto error_exit; } - ntfs_log_debug(OK); return vol; error_exit: eo = errno; @@ -646,7 +665,7 @@ static ntfs_inode *ntfs_hiberfile_open(ntfs_volume *vol) return NULL; } - unicode_len = ntfs_mbstoucs(hiberfile, &unicode, 0); + unicode_len = ntfs_mbstoucs(hiberfile, &unicode); if (unicode_len < 0) { ntfs_log_perror("Couldn't convert 'hiberfil.sys' to Unicode"); goto out; @@ -684,11 +703,11 @@ out: * Return: 0 if Windows isn't hibernated for sure * -1 otherwise and errno is set to the appropriate value */ -static int ntfs_volume_check_hiberfile(ntfs_volume *vol) +int ntfs_volume_check_hiberfile(ntfs_volume *vol, int verbose) { ntfs_inode *ni; ntfs_attr *na = NULL; - int i, bytes_read, err; + int bytes_read, err; char *buf = NULL; ni = ntfs_hiberfile_open(vol); @@ -714,23 +733,18 @@ static int ntfs_volume_check_hiberfile(ntfs_volume *vol) goto out; } if (bytes_read < NTFS_HIBERFILE_HEADER_SIZE) { - ntfs_log_error("Hibernated non-system partition, refused to " - "mount.\n"); + if (verbose) + ntfs_log_error("Hibernated non-system partition, " + "refused to mount.\n"); errno = EPERM; goto out; } if (memcmp(buf, "hibr", 4) == 0) { - ntfs_log_error("Windows is hibernated, refused to mount.\n"); + if (verbose) + ntfs_log_error("Windows is hibernated, refused to mount.\n"); errno = EPERM; goto out; } - for (i = 0; i < NTFS_HIBERFILE_HEADER_SIZE; i++) { - if (buf[i]) { - ntfs_log_error("Windows is hibernated, won't mount.\n"); - errno = EPERM; - goto out; - } - } /* All right, all header bytes are zero */ errno = 0; out: @@ -753,10 +767,9 @@ out: * to mount as the ntfs volume. * * @flags is an optional second parameter. The same flags are used as for - * the mount system call (man 2 mount). Currently only the following flags - * are implemented: + * the mount system call (man 2 mount). Currently only the following flag + * is implemented: * MS_RDONLY - mount volume read-only - * MS_NOATIME - do not update access time * * The function opens the device @dev and verifies that it contains a valid * bootsector. Then, it allocates an ntfs_volume structure and initializes @@ -770,10 +783,6 @@ out: ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) { s64 l; -#ifdef DEBUG - const char *OK = "OK\n"; - const char *FAILED = "FAILED\n"; -#endif ntfs_volume *vol; u8 *m = NULL, *m2 = NULL; ntfs_attr_search_ctx *ctx = NULL; @@ -786,10 +795,8 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) u32 u; vol = ntfs_volume_startup(dev, flags); - if (!vol) { - ntfs_log_perror("Failed to startup volume"); + if (!vol) return NULL; - } /* Load data from $MFT and $MFTMirr and compare the contents. */ m = ntfs_malloc(vol->mftmirr_size << vol->mft_record_size_bits); @@ -819,7 +826,7 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) } vol->mftmirr_size = l; } - ntfs_log_debug("Comparing $MFTMirr to $MFT... "); + ntfs_log_debug("Comparing $MFTMirr to $MFT...\n"); for (i = 0; i < vol->mftmirr_size; ++i) { MFT_RECORD *mrec, *mrec2; const char *ESTR[12] = { "$MFT", "$MFTMirr", "$LogFile", @@ -837,14 +844,12 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) mrec = (MFT_RECORD*)(m + i * vol->mft_record_size); if (mrec->flags & MFT_RECORD_IN_USE) { if (ntfs_is_baad_recordp(mrec)) { - ntfs_log_debug("FAILED\n"); ntfs_log_error("$MFT error: Incomplete multi " "sector transfer detected in " "'%s'.\n", s); goto io_error_exit; } if (!ntfs_is_mft_recordp(mrec)) { - ntfs_log_debug("FAILED\n"); ntfs_log_error("$MFT error: Invalid mft " "record for '%s'.\n", s); goto io_error_exit; @@ -853,62 +858,59 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) mrec2 = (MFT_RECORD*)(m2 + i * vol->mft_record_size); if (mrec2->flags & MFT_RECORD_IN_USE) { if (ntfs_is_baad_recordp(mrec2)) { - ntfs_log_debug("FAILED\n"); ntfs_log_error("$MFTMirr error: Incomplete " "multi sector transfer " "detected in '%s'.\n", s); goto io_error_exit; } if (!ntfs_is_mft_recordp(mrec2)) { - ntfs_log_debug("FAILED\n"); ntfs_log_error("$MFTMirr error: Invalid mft " "record for '%s'.\n", s); goto io_error_exit; } } if (memcmp(mrec, mrec2, ntfs_mft_record_get_data_size(mrec))) { - ntfs_log_debug(FAILED); ntfs_log_error("$MFTMirr does not match $MFT (record " "%d).\n", i); goto io_error_exit; } } - ntfs_log_debug(OK); free(m2); free(m); m = m2 = NULL; /* Now load the bitmap from $Bitmap. */ - ntfs_log_debug("Loading $Bitmap... "); + ntfs_log_debug("Loading $Bitmap...\n"); vol->lcnbmp_ni = ntfs_inode_open(vol, FILE_Bitmap); if (!vol->lcnbmp_ni) { - ntfs_log_debug(FAILED); - ntfs_log_perror("Failed to open inode"); + ntfs_log_perror("Failed to open inode FILE_Bitmap"); goto error_exit; } - /* Get an ntfs attribute for $Bitmap/$DATA. */ + vol->lcnbmp_na = ntfs_attr_open(vol->lcnbmp_ni, AT_DATA, AT_UNNAMED, 0); if (!vol->lcnbmp_na) { - ntfs_log_debug(FAILED); ntfs_log_perror("Failed to open ntfs attribute"); goto error_exit; } - /* Done with the $Bitmap mft record. */ - ntfs_log_debug(OK); + + if (vol->lcnbmp_na->data_size > vol->lcnbmp_na->allocated_size) { + ntfs_log_error("Corrupt cluster map size (%lld > %lld)\n", + (long long)vol->lcnbmp_na->data_size, + (long long)vol->lcnbmp_na->allocated_size); + goto io_error_exit; + } /* Now load the upcase table from $UpCase. */ - ntfs_log_debug("Loading $UpCase... "); + ntfs_log_debug("Loading $UpCase...\n"); ni = ntfs_inode_open(vol, FILE_UpCase); if (!ni) { - ntfs_log_debug(FAILED); - ntfs_log_perror("Failed to open inode"); + ntfs_log_perror("Failed to open inode FILE_UpCase"); goto error_exit; } /* Get an ntfs attribute for $UpCase/$DATA. */ na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); if (!na) { - ntfs_log_debug(FAILED); ntfs_log_perror("Failed to open ntfs attribute"); goto error_exit; } @@ -919,7 +921,6 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) * characters. */ if (na->data_size & ~0x1ffffffffULL) { - ntfs_log_debug(FAILED); ntfs_log_error("Error: Upcase table is too big (max 32-bit " "allowed).\n"); errno = EINVAL; @@ -930,15 +931,12 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) /* Throw away default table. */ free(vol->upcase); vol->upcase = ntfs_malloc(na->data_size); - if (!vol->upcase) { - ntfs_log_debug(FAILED); + if (!vol->upcase) goto error_exit; - } } /* Read in the $DATA attribute value into the buffer. */ l = ntfs_attr_pread(na, 0, na->data_size, vol->upcase); if (l != na->data_size) { - ntfs_log_debug(FAILED); ntfs_log_error("Failed to read $UpCase, unexpected length " "(%lld != %lld).\n", (long long)l, (long long)na->data_size); @@ -946,7 +944,6 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) goto error_exit; } /* Done with the $UpCase mft record. */ - ntfs_log_debug(OK); ntfs_attr_close(na); if (ntfs_inode_close(ni)) { ntfs_log_perror("Failed to close $UpCase"); @@ -957,24 +954,20 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) * Now load $Volume and set the version information and flags in the * vol structure accordingly. */ - ntfs_log_debug("Loading $Volume... "); + ntfs_log_debug("Loading $Volume...\n"); vol->vol_ni = ntfs_inode_open(vol, FILE_Volume); if (!vol->vol_ni) { - ntfs_log_debug(FAILED); - ntfs_log_perror("Failed to open inode"); + ntfs_log_perror("Failed to open inode FILE_Volume"); goto error_exit; } /* Get a search context for the $Volume/$VOLUME_INFORMATION lookup. */ ctx = ntfs_attr_get_search_ctx(vol->vol_ni, NULL); - if (!ctx) { - ntfs_log_debug(FAILED); - ntfs_log_perror("Failed to allocate attribute search context"); + if (!ctx) goto error_exit; - } + /* Find the $VOLUME_INFORMATION attribute. */ if (ntfs_attr_lookup(AT_VOLUME_INFORMATION, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { - ntfs_log_debug(FAILED); ntfs_log_perror("$VOLUME_INFORMATION attribute not found in " "$Volume"); goto error_exit; @@ -982,7 +975,6 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) a = ctx->attr; /* Has to be resident. */ if (a->non_resident) { - ntfs_log_debug(FAILED); ntfs_log_error("Attribute $VOLUME_INFORMATION must be " "resident but it isn't.\n"); errno = EIO; @@ -995,7 +987,6 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) le32_to_cpu(ctx->mrec->bytes_in_use) || le16_to_cpu(a->value_offset) + le32_to_cpu( a->value_length) > le32_to_cpu(a->length)) { - ntfs_log_debug(FAILED); ntfs_log_error("$VOLUME_INFORMATION in $Volume is corrupt.\n"); errno = EIO; goto error_exit; @@ -1013,7 +1004,6 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) if (ntfs_attr_lookup(AT_VOLUME_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { if (errno != ENOENT) { - ntfs_log_debug(FAILED); ntfs_log_perror("Failed to lookup of $VOLUME_NAME in " "$Volume failed"); goto error_exit; @@ -1024,16 +1014,13 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) * had zero length. */ vol->vol_name = ntfs_malloc(1); - if (!vol->vol_name) { - ntfs_log_debug(FAILED); + if (!vol->vol_name) goto error_exit; - } vol->vol_name[0] = '\0'; } else { a = ctx->attr; /* Has to be resident. */ if (a->non_resident) { - ntfs_log_debug(FAILED); ntfs_log_error("$VOLUME_NAME must be resident.\n"); errno = EIO; goto error_exit; @@ -1052,10 +1039,9 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) ntfs_log_debug("Forcing name into ASCII by replacing " "non-ASCII characters with underscores.\n"); vol->vol_name = ntfs_malloc(u + 1); - if (!vol->vol_name) { - ntfs_log_debug(FAILED); + if (!vol->vol_name) goto error_exit; - } + for (j = 0; j < (s32)u; j++) { ntfschar uc = le16_to_cpu(vname[j]); if (uc > 0xff) @@ -1065,27 +1051,23 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) vol->vol_name[u] = '\0'; } } - ntfs_log_debug(OK); ntfs_attr_put_search_ctx(ctx); ctx = NULL; /* Now load the attribute definitions from $AttrDef. */ - ntfs_log_debug("Loading $AttrDef... "); + ntfs_log_debug("Loading $AttrDef...\n"); ni = ntfs_inode_open(vol, FILE_AttrDef); if (!ni) { - ntfs_log_debug(FAILED); ntfs_log_perror("Failed to open $AttrDef"); goto error_exit; } /* Get an ntfs attribute for $AttrDef/$DATA. */ na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); if (!na) { - ntfs_log_debug(FAILED); ntfs_log_perror("Failed to open ntfs attribute"); goto error_exit; } /* Check we don't overflow 32-bits. */ if (na->data_size > 0xffffffffLL) { - ntfs_log_debug(FAILED); ntfs_log_error("Attribute definition table is too big (max " "32-bit allowed).\n"); errno = EINVAL; @@ -1093,14 +1075,11 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) } vol->attrdef_len = na->data_size; vol->attrdef = ntfs_malloc(na->data_size); - if (!vol->attrdef) { - ntfs_log_debug(FAILED); + if (!vol->attrdef) goto error_exit; - } /* Read in the $DATA attribute value into the buffer. */ l = ntfs_attr_pread(na, 0, na->data_size, vol->attrdef); if (l != na->data_size) { - ntfs_log_debug(FAILED); ntfs_log_error("Failed to read $AttrDef, unexpected length " "(%lld != %lld).\n", (long long)l, (long long)na->data_size); @@ -1108,7 +1087,6 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) goto error_exit; } /* Done with the $AttrDef mft record. */ - ntfs_log_debug(OK); ntfs_attr_close(na); if (ntfs_inode_close(ni)) { ntfs_log_perror("Failed to close $AttrDef"); @@ -1119,12 +1097,14 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) * We care only about read-write mounts. */ if (!(flags & MS_RDONLY)) { - if (ntfs_volume_check_hiberfile(vol) < 0) + if (!(flags & MS_IGNORE_HIBERFILE) && + ntfs_volume_check_hiberfile(vol, 1) < 0) goto error_exit; if (ntfs_volume_check_logfile(vol) < 0) { - if (!(flags & MS_FORCE)) + if (!(flags & MS_RECOVER)) goto error_exit; - ntfs_log_info("WARNING: Forced mount, reset $LogFile.\n"); + ntfs_log_info("The file system wasn't safely " + "closed on Windows. Fixing.\n"); if (ntfs_logfile_reset(vol)) goto error_exit; } @@ -1154,9 +1134,8 @@ error_exit: * * @flags is an optional second parameter. The same flags are used as for * the mount system call (man 2 mount). Currently only the following flags - * are implemented: + * is implemented: * MS_RDONLY - mount volume read-only - * MS_NOATIME - do not update access time * * The function opens the device or file @name and verifies that it contains a * valid bootsector. Then, it allocates an ntfs_volume structure and initializes @@ -1463,10 +1442,9 @@ int ntfs_volume_write_flags(ntfs_volume *vol, const u16 flags) } /* Get a pointer to the volume information attribute. */ ctx = ntfs_attr_get_search_ctx(vol->vol_ni, NULL); - if (!ctx) { - ntfs_log_perror("Failed to allocate attribute search context"); + if (!ctx) return -1; - } + if (ntfs_attr_lookup(AT_VOLUME_INFORMATION, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { ntfs_log_error("Attribute $VOLUME_INFORMATION was not found " @@ -1497,13 +1475,92 @@ int ntfs_volume_write_flags(ntfs_volume *vol, const u16 flags) vol->flags = c->flags = flags & VOLUME_FLAGS_MASK; /* Write them to disk. */ ntfs_inode_mark_dirty(vol->vol_ni); - if (ntfs_inode_sync(vol->vol_ni)) { - ntfs_log_perror("Error writing $Volume"); + if (ntfs_inode_sync(vol->vol_ni)) goto err_out; - } + ret = 0; /* success */ err_out: ntfs_attr_put_search_ctx(ctx); return ret; } +int ntfs_volume_error(int err) +{ + int ret; + + switch (err) { + case 0: + ret = NTFS_VOLUME_OK; + break; + case EINVAL: + ret = NTFS_VOLUME_NOT_NTFS; + break; + case EIO: + ret = NTFS_VOLUME_CORRUPT; + break; + case EPERM: + ret = NTFS_VOLUME_HIBERNATED; + break; + case EOPNOTSUPP: + ret = NTFS_VOLUME_UNCLEAN_UNMOUNT; + break; + case EBUSY: + ret = NTFS_VOLUME_LOCKED; + break; + case ENXIO: + ret = NTFS_VOLUME_RAID; + break; + case EACCES: + ret = NTFS_VOLUME_NO_PRIVILEGE; + break; + default: + ret = NTFS_VOLUME_UNKNOWN_REASON; + break; + } + return ret; +} + + +void ntfs_mount_error(const char *volume, const char *mntpoint, int err) +{ + switch (err) { + case NTFS_VOLUME_NOT_NTFS: + ntfs_log_error(invalid_ntfs_msg, volume); + break; + case NTFS_VOLUME_CORRUPT: + ntfs_log_error("%s", corrupt_volume_msg); + break; + case NTFS_VOLUME_HIBERNATED: + ntfs_log_error(hibernated_volume_msg, volume, mntpoint); + break; + case NTFS_VOLUME_UNCLEAN_UNMOUNT: + ntfs_log_error("%s", unclean_journal_msg); + break; + case NTFS_VOLUME_LOCKED: + ntfs_log_error("%s", opened_volume_msg); + break; + case NTFS_VOLUME_RAID: + ntfs_log_error("%s", fakeraid_msg); + break; + case NTFS_VOLUME_NO_PRIVILEGE: + ntfs_log_error(access_denied_msg, volume); + break; + } +} + +int ntfs_set_locale(void) +{ +#ifndef __HAIKU__ + const char *locale; + + locale = setlocale(LC_ALL, ""); + if (!locale) { + locale = setlocale(LC_ALL, NULL); + ntfs_log_error("Couldn't set local environment, using default " + "'%s'.\n", locale); + return 1; + } +#endif + return 0; +} + diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/volume.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/volume.h index e0d6aa0613..8ebef49fa1 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/volume.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/volume.h @@ -4,7 +4,7 @@ * Copyright (c) 2000-2004 Anton Altaparmakov * Copyright (c) 2004-2005 Richard Russon * Copyright (c) 2005-2006 Yura Pakhuchiy - * Copyright (c) 2005-2006 Szabolcs Szakacsits + * Copyright (c) 2005-2009 Szabolcs Szakacsits * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -43,30 +43,21 @@ #endif /* - * Under Cygwin, DJGPP and FreeBSD we do not have MS_RDONLY and MS_NOATIME, + * Under Cygwin, DJGPP and FreeBSD we do not have MS_RDONLY, * so we define them ourselves. */ #ifndef MS_RDONLY #define MS_RDONLY 1 #endif -/* - * Solaris defines MS_RDONLY but not MS_NOATIME thus we need to carefully - * define MS_NOATIME. - */ -#ifndef MS_NOATIME -#if (MS_RDONLY != 1) -# define MS_NOATIME 1 -#else -# define MS_NOATIME 2 -#endif -#endif #define MS_EXCLUSIVE 0x08000000 -#ifndef MS_FORCE -#define MS_FORCE 0x10000000 +#ifndef MS_RECOVER +#define MS_RECOVER 0x10000000 #endif +#define MS_IGNORE_HIBERFILE 0x20000000 + /* Forward declaration */ typedef struct _ntfs_volume ntfs_volume; @@ -89,6 +80,22 @@ typedef enum { extern int ntfs_check_if_mounted(const char *file, unsigned long *mnt_flags); +typedef enum { + NTFS_VOLUME_OK = 0, + NTFS_VOLUME_SYNTAX_ERROR = 11, + NTFS_VOLUME_NOT_NTFS = 12, + NTFS_VOLUME_CORRUPT = 13, + NTFS_VOLUME_HIBERNATED = 14, + NTFS_VOLUME_UNCLEAN_UNMOUNT = 15, + NTFS_VOLUME_LOCKED = 16, + NTFS_VOLUME_RAID = 17, + NTFS_VOLUME_UNKNOWN_REASON = 18, + NTFS_VOLUME_NO_PRIVILEGE = 19, + NTFS_VOLUME_OUT_OF_MEMORY = 20, + NTFS_VOLUME_FUSE_ERROR = 21, + NTFS_VOLUME_INSECURE = 22 +} ntfs_volume_status; + /** * enum ntfs_volume_state_bits - * @@ -98,7 +105,6 @@ typedef enum { NV_ReadOnly, /* 1: Volume is read-only. */ NV_CaseSensitive, /* 1: Volume is mounted case-sensitive. */ NV_LogFileEmpty, /* 1: $logFile journal is empty. */ - NV_NoATime, /* 1: Do not update access time. */ } ntfs_volume_state_bits; #define test_nvol_flag(nv, flag) test_bit(NV_##flag, (nv)->state) @@ -117,10 +123,6 @@ typedef enum { #define NVolSetLogFileEmpty(nv) set_nvol_flag(nv, LogFileEmpty) #define NVolClearLogFileEmpty(nv) clear_nvol_flag(nv, LogFileEmpty) -#define NVolNoATime(nv) test_nvol_flag(nv, NoATime) -#define NVolSetNoATime(nv) set_nvol_flag(nv, NoATime) -#define NVolClearNoATime(nv) clear_nvol_flag(nv, NoATime) - /* * NTFS version 1.1 and 1.2 are used by Windows NT4. * NTFS version 2.x is used by Windows 2000 Beta @@ -212,12 +214,13 @@ struct _ntfs_volume { s32 attrdef_len; /* Size of the attribute definition table in bytes. */ - /* Temp: for directory handling */ - void *private_data; /* ntfs_dir for . */ - void *private_bmp1; /* ntfs_bmp for $MFT/$BITMAP */ - void *private_bmp2; /* ntfs_bmp for $Bitmap */ + s64 free_clusters; /* Track the number of free clusters which + greatly improves statfs() performance */ + s64 free_mft_records; /* Same for free mft records (see above) */ }; +extern const char *ntfs_home; + extern ntfs_volume *ntfs_volume_alloc(void); extern ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, @@ -230,9 +233,15 @@ extern ntfs_volume *ntfs_mount(const char *name, unsigned long flags); extern int ntfs_umount(ntfs_volume *vol, const BOOL force); extern int ntfs_version_is_supported(ntfs_volume *vol); +extern int ntfs_volume_check_hiberfile(ntfs_volume *vol, int verbose); extern int ntfs_logfile_reset(ntfs_volume *vol); extern int ntfs_volume_write_flags(ntfs_volume *vol, const u16 flags); +extern int ntfs_volume_error(int err); +extern void ntfs_mount_error(const char *vol, const char *mntpoint, int err); + +extern int ntfs_set_locale(void); + #endif /* defined _NTFS_VOLUME_H */ diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/win32_io.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/win32_io.c index 90a7bda2fc..ed9fa5218e 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/win32_io.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/win32_io.c @@ -52,6 +52,10 @@ typedef struct ntfs_volume ntfs_volume; #include "types.h" #include "device.h" +#ifndef MAX_PATH +#define MAX_PATH 1024 +#endif + #ifndef NTFS_BLOCK_SIZE #define NTFS_BLOCK_SIZE 512 #define NTFS_BLOCK_SIZE_BITS 9 diff --git a/src/add-ons/kernel/file_systems/ntfs/lock.h b/src/add-ons/kernel/file_systems/ntfs/lock.h index abfb327a3f..f36ff96372 100644 --- a/src/add-ons/kernel/file_systems/ntfs/lock.h +++ b/src/add-ons/kernel/file_systems/ntfs/lock.h @@ -5,54 +5,9 @@ #include -#ifdef __HAIKU__ - typedef recursive_lock lock; #define LOCK(l) recursive_lock_lock(&l); #define UNLOCK(l) recursive_lock_unlock(&l); -#else - -typedef struct lock lock; -typedef struct mlock mlock; - -struct lock { - sem_id s; - long c; -}; - -struct mlock { - sem_id s; -}; - -extern int new_lock(lock *l, const char *name); -extern int free_lock(lock *l); - -static inline status_t LOCK(lock *l) -{ - if (atomic_add(&(l->c), -1) <= 0) - return acquire_sem(l->s); - return B_OK; -} - -static inline status_t UNLOCK(lock *l) -{ - if (atomic_add(&(l->c), 1) < 0) - return release_sem(l->s); - return B_OK; -} - - -#define LOCK(l) if (atomic_add(&l.c, -1) <= 0) acquire_sem(l.s); -#define UNLOCK(l) if (atomic_add(&l.c, 1) < 0) release_sem(l.s); - -extern int new_mlock(mlock *l, long c, const char *name); -extern int free_mlock(mlock *l); - -#define LOCKM(l,cnt) acquire_sem_etc(l.s, cnt, 0, 0) -#define UNLOCKM(l,cnt) release_sem_etc(l.s, cnt, 0) - -#endif //__HAIKU__ - #endif diff --git a/src/add-ons/kernel/file_systems/ntfs/ntfs.h b/src/add-ons/kernel/file_systems/ntfs/ntfs.h index 029eacd5d3..793a0fb9fa 100644 --- a/src/add-ons/kernel/file_systems/ntfs/ntfs.h +++ b/src/add-ons/kernel/file_systems/ntfs/ntfs.h @@ -24,8 +24,6 @@ extern "C" { #endif -#ifdef __HAIKU__ - #include #include #include @@ -38,15 +36,6 @@ extern "C" { #include #include -#else - -#include "fsproto.h" -#include "lock.h" - -#define publish_vnode new_vnode - -#endif - #include "config.h" #include "attrib.h" #include "inode.h" @@ -56,7 +45,6 @@ extern "C" { #include "layout.h" #include "index.h" #include "utils.h" -#include "version.h" #include "ntfstime.h" #include "misc.h" #include "utils.h" @@ -111,16 +99,10 @@ typedef struct nspace { ntfs_volume *ntvol; char devicePath[MAX_PATH]; -#ifdef __HAIKU__ dev_t id; -#else - nspace_id id; -#endif int free_cluster_count; char volumeLabel[MAX_PATH]; - ulong flags; - int state; s64 free_clusters; long free_mft; @@ -131,10 +113,7 @@ typedef struct nspace BOOL debug; BOOL noatime; BOOL no_detach; - - lock vlock; - } nspace; diff --git a/src/add-ons/kernel/file_systems/ntfs/ntfsdir.c b/src/add-ons/kernel/file_systems/ntfs/ntfsdir.c index 8a5904c867..7f57a09dc1 100644 --- a/src/add-ons/kernel/file_systems/ntfs/ntfsdir.c +++ b/src/add-ons/kernel/file_systems/ntfs/ntfsdir.c @@ -38,10 +38,6 @@ #include "ntfsdir.h" //callback function for readdir() -static int _ntfs_dirent_filler(void *_dirent, const ntfschar *name, - const int name_len, const int name_type, const s64 pos, - const MFT_REF mref, const unsigned dt_type); - static int _ntfs_dirent_filler(void *_dirent, const ntfschar *name, const int name_len, const int name_type, const s64 pos, const MFT_REF mref, const unsigned dt_type) @@ -58,13 +54,17 @@ static int _ntfs_dirent_filler(void *_dirent, const ntfschar *name, cookie->readed = 0; return -1; } else { - if (ntfs_ucstombs(name, name_len, &filename, 0) < 0) - return -1; - strcpy(cookie->name,filename); - cookie->ino=MREF(mref); - cookie->readed = 1; - return 0; - } + if (ntfs_ucstombs(name, name_len, &filename, 0) >= 0) { + if(filename) { + strcpy(cookie->name,filename); + cookie->ino=MREF(mref); + cookie->readed = 1; + free(filename); + return 0; + } + } + return -1; + } } return 0; } @@ -114,6 +114,7 @@ fs_opendir( fs_volume *_vol, fs_vnode *_node, void** _cookie ) cookie->pos = 0; cookie->ino = 0; cookie->readed = 0; + cookie->last = 0; cookie->name[0] = 0; cookie->show_sys_files = ns->show_sys_files; *_cookie = (void*)cookie; @@ -159,36 +160,37 @@ fs_readdir( fs_volume *_vol, fs_vnode *_node, void *_cookie, struct dirent *buf, LOCK_VOL(ns); - ERRPRINT("fs_readdir - ENTER\n"); + ERRPRINT("fs_readdir - ENTER (sizeof(buf)=%d, bufsize=%d, num=%d\n",sizeof(buf),bufsize,*num); if (!ns || !node || !cookie || !num || bufsize < sizeof(buf)) { result = EINVAL; goto exit; } - if(cookie->readed == 1) { + if(cookie->readed == 1 || cookie->last == 1) { result = ENOENT; goto exit; } ni = ntfs_inode_open(ns->ntvol, node->vnid); if(ni==NULL) { - ERRPRINT("fs_readdir - Dir not opened\n"); + ERRPRINT("fs_readdir - dir not opened\n"); result = ENOENT; goto exit; } result = ntfs_readdir(ni, &cookie->pos, cookie, (ntfs_filldir_t)_ntfs_dirent_filler); - if(result==0) { - realLen = nameLength>255?255:nameLength; - buf->d_dev = ns->id; - buf->d_ino = cookie->ino; - memcpy(buf->d_name,cookie->name, realLen+1); - buf->d_reclen = sizeof(buf) + realLen - 1; - result = B_NO_ERROR; - } - else - result = ENOENT; + + realLen = nameLength>255?255:nameLength; + buf->d_dev = ns->id; + buf->d_ino = cookie->ino; + memcpy(buf->d_name,cookie->name, realLen+1); + buf->d_reclen = sizeof(buf) + realLen - 1; + + if(result==0) + cookie->last = 1; + + result = B_NO_ERROR; ERRPRINT("fs_readdir - FILE: [%s]\n",buf->d_name); @@ -225,6 +227,7 @@ fs_rewinddir( fs_volume *_vol, fs_vnode *vnode, void *_cookie ) cookie->pos = 0; cookie->ino = 0; cookie->readed = 0; + cookie->last = 0; cookie->name[0] = 0; result = B_NO_ERROR; } diff --git a/src/add-ons/kernel/file_systems/ntfs/ntfsdir.h b/src/add-ons/kernel/file_systems/ntfs/ntfsdir.h index e6cbcc2252..4325645c4d 100644 --- a/src/add-ons/kernel/file_systems/ntfs/ntfsdir.h +++ b/src/add-ons/kernel/file_systems/ntfs/ntfsdir.h @@ -26,6 +26,7 @@ typedef struct dircookie { u64 pos; int readed; + int last; ino_t ino; BOOL show_sys_files; char name[MAX_PATH]; diff --git a/src/add-ons/kernel/file_systems/ntfs/settings/ntfs b/src/add-ons/kernel/file_systems/ntfs/settings/ntfs index 1277fe8771..3a8e574252 100644 --- a/src/add-ons/kernel/file_systems/ntfs/settings/ntfs +++ b/src/add-ons/kernel/file_systems/ntfs/settings/ntfs @@ -4,5 +4,5 @@ # /boot/home/config/settings/kernel/drivers/ # hide_sys_files true -read_only true +read_only false no_atime true diff --git a/src/add-ons/kernel/file_systems/ntfs/utils.c b/src/add-ons/kernel/file_systems/ntfs/utils.c index 9a5c945a72..90b400d720 100644 --- a/src/add-ons/kernel/file_systems/ntfs/utils.c +++ b/src/add-ons/kernel/file_systems/ntfs/utils.c @@ -69,12 +69,10 @@ #include "volume.h" #include "debug.h" #include "dir.h" -#include "version.h" #include "logging.h" #include "misc.h" const char *ntfs_bugs = "Developers' email address: "NTFS_DEV_LIST"\n"; -const char *ntfs_home = "Linux NTFS homepage: http://www.linux-ntfs.org\n"; const char *ntfs_gpl = "This program is free software, released under the GNU " "General Public License\nand you are welcome to redistribute it under " "certain conditions. It comes with\nABSOLUTELY NO WARRANTY; for " @@ -82,23 +80,6 @@ const char *ntfs_gpl = "This program is free software, released under the GNU " "\"COPYING\" distributed with this program, or online at:\n" "http://www.gnu.org/copyleft/gpl.html\n"; -/** - * utils_set_locale - */ - /* -int utils_set_locale(void) -{ - const char *locale; - - locale = setlocale(LC_ALL, ""); - if (!locale) { - locale = setlocale(LC_ALL, NULL); - ntfs_log_error("Couldn't set local environment, using default '%s'.\n", locale); - return 1; - } else { - return 0; - } -}*/ /** * utils_valid_device - Perform some safety checks on the device, before we start diff --git a/src/add-ons/kernel/file_systems/ntfs/volume_util.c b/src/add-ons/kernel/file_systems/ntfs/volume_util.c index 9450470db1..5837da97eb 100644 --- a/src/add-ons/kernel/file_systems/ntfs/volume_util.c +++ b/src/add-ons/kernel/file_systems/ntfs/volume_util.c @@ -35,7 +35,6 @@ #include "inode.h" #include "runlist.h" #include "utils.h" -#include "version.h" #include "ntfs.h" #include "volume_util.h" @@ -166,7 +165,7 @@ int ntfs_change_label(ntfs_volume *vol, char *label) goto err_out; } } - label_len = ntfs_mbstoucs(label, &new_label, 0); + label_len = ntfs_mbstoucs(label, &new_label); if (label_len == -1) { ntfs_log_perror("Unable to convert label string to Unicode"); goto err_out;