Added NTFS file system add-on written by Troeglazov Gerasim (a.k.a. 3dEyes**).
It's based on ntfs-3g-0.20070102-BETA, and therefore under the GPL license. Writing is currently disabled for safety reasons, and you'll have to mount it manually, as the identify hooks are not yet implemented. Thanks a lot for your work! git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@19897 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -6,3 +6,5 @@ SubInclude HAIKU_TOP src add-ons kernel file_systems googlefs ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel file_systems iso9660 ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel file_systems nfs ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel file_systems udf ;
|
||||
|
||||
SubIncludeGPL HAIKU_TOP src add-ons kernel file_systems ntfs ;
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
SubDir HAIKU_TOP src add-ons kernel file_systems ntfs ;
|
||||
|
||||
SubDirHdrs [ FDirName $(SUBDIR) libntfs ] ;
|
||||
|
||||
SubDirCcFlags -DHAVE_CONFIG_H=1 -D_READ_ONLY_=1 ;
|
||||
SubDirC++Flags -DHAVE_CONFIG_H=1 -D_READ_ONLY_=1 ;
|
||||
|
||||
UsePrivateHeaders kernel ;
|
||||
|
||||
KernelAddon ntfs :
|
||||
attributes.c
|
||||
mime_table.c
|
||||
utils.c
|
||||
ntfsdir.c
|
||||
volume_util.c
|
||||
fs_func.c
|
||||
kernel_interface.cpp
|
||||
kernel_cpp.cpp
|
||||
:
|
||||
libntfs.a
|
||||
;
|
||||
|
||||
SEARCH on [ FGristFiles
|
||||
kernel_cpp.cpp
|
||||
] = [ FDirName $(HAIKU_TOP) src system kernel util ] ;
|
||||
|
||||
SubIncludeGPL HAIKU_TOP src add-ons kernel file_systems ntfs libntfs ;
|
||||
|
||||
@@ -0,0 +1,336 @@
|
||||
/*
|
||||
Copyright 1999-2001, Be Incorporated. All Rights Reserved.
|
||||
This file may be used under the terms of the Be Sample Code License.
|
||||
*/
|
||||
/* attributes.c
|
||||
* handles mime type information for ntfs
|
||||
* gets/sets mime information in vnode
|
||||
*/
|
||||
|
||||
|
||||
#define MIME_STRING_TYPE 'MIMS'
|
||||
|
||||
#include <SupportDefs.h>
|
||||
#include <KernelExport.h>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <fs_attr.h>
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include "ntfs.h"
|
||||
#include "attributes.h"
|
||||
#include "mime_table.h"
|
||||
|
||||
int32 kBeOSTypeCookie = 0x1234;
|
||||
|
||||
status_t set_mime(vnode *node, const char *filename)
|
||||
{
|
||||
struct ext_mime *p;
|
||||
int32 namelen, ext_len;
|
||||
|
||||
ERRPRINT("set_mime - for [%s]\n", filename);
|
||||
|
||||
node->mime = NULL;
|
||||
|
||||
namelen = strlen(filename);
|
||||
|
||||
for (p=mimes;p->extension;p++) {
|
||||
ext_len = strlen(p->extension);
|
||||
|
||||
if (namelen <= ext_len)
|
||||
continue;
|
||||
|
||||
if (filename[namelen-ext_len-1] != '.')
|
||||
continue;
|
||||
|
||||
if (!strcasecmp(filename + namelen - ext_len, p->extension))
|
||||
break;
|
||||
}
|
||||
|
||||
node->mime = p->mime;
|
||||
|
||||
return B_NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
fs_open_attrib_dir(void *_ns, void *_node, void **_cookie)
|
||||
{
|
||||
nspace *ns = (nspace *)_ns;
|
||||
int result = B_NO_ERROR;
|
||||
|
||||
ERRPRINT("fs_open_attrdir - ENTER\n");
|
||||
|
||||
LOCK_VOL(ns);
|
||||
|
||||
if ((*_cookie = malloc(sizeof(uint32))) == NULL) {
|
||||
result = ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
*(int32 *)(*_cookie) = 0;
|
||||
|
||||
exit:
|
||||
|
||||
ERRPRINT("fs_open_attrdir - EXIT, result is %s\n", strerror(result));
|
||||
|
||||
UNLOCK_VOL(ns);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
status_t
|
||||
fs_close_attrib_dir(void *_ns, void *_node, void *_cookie)
|
||||
{
|
||||
nspace *ns = (nspace *)_ns;
|
||||
|
||||
ERRPRINT("fs_close_attrdir - ENTER\n");
|
||||
|
||||
LOCK_VOL(ns);
|
||||
|
||||
*(int32 *)_cookie = 1;
|
||||
|
||||
ERRPRINT("fs_close_attrdir - EXIT\n");
|
||||
|
||||
UNLOCK_VOL(ns);
|
||||
|
||||
return B_NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
fs_free_attrib_dir_cookie(void *_ns, void *_node, void *_cookie)
|
||||
{
|
||||
nspace *ns = (nspace *)_ns;
|
||||
int result = B_NO_ERROR;
|
||||
|
||||
LOCK_VOL(ns);
|
||||
|
||||
ERRPRINT("fs_free_attrib_dir_cookie - ENTER\n");
|
||||
|
||||
if (_cookie == NULL) {
|
||||
ERRPRINT("fs_free_attrib_dir_cookie - error: called with null cookie\n");
|
||||
result = EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
*(int32 *)_cookie = 0x87654321;
|
||||
free(_cookie);
|
||||
|
||||
exit:
|
||||
|
||||
ERRPRINT("fs_free_attrib_dir_cookie - EXIT, result is %s\n", strerror(result));
|
||||
|
||||
UNLOCK_VOL(ns);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
fs_rewind_attrib_dir(void *_ns, void *_node, void *_cookie)
|
||||
{
|
||||
nspace *ns = (nspace *)_ns;
|
||||
int result = B_NO_ERROR;
|
||||
|
||||
LOCK_VOL(ns);
|
||||
|
||||
ERRPRINT("fs_rewind_attrcookie - ENTER\n");
|
||||
|
||||
if (_cookie == NULL) {
|
||||
ERRPRINT("fs_rewind_attrcookie - error: fs_rewind_attrcookie called with null cookie\n");
|
||||
result = EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
*(uint32 *)_cookie = 0;
|
||||
|
||||
exit:
|
||||
|
||||
ERRPRINT("fs_rewind_attrcookie - EXIT, result is %s\n", strerror(result));
|
||||
|
||||
UNLOCK_VOL(ns);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
fs_read_attrib_dir(void *_ns, void *_node, void *_cookie, struct dirent *entry, size_t bufsize, uint32 *num)
|
||||
{
|
||||
nspace *ns = (nspace *)_ns;
|
||||
vnode *node = (vnode *)_node;
|
||||
int32 *cookie = (int32 *)_cookie;
|
||||
|
||||
LOCK_VOL(ns);
|
||||
|
||||
ERRPRINT("fs_read_attrdir - ENTER\n");
|
||||
|
||||
*num = 0;
|
||||
|
||||
if ((*cookie == 0) && (node->mime)) {
|
||||
*num = 1;
|
||||
|
||||
entry->d_ino = node->vnid;
|
||||
entry->d_dev = ns->id;
|
||||
entry->d_reclen = 10;
|
||||
strcpy(entry->d_name, "BEOS:TYPE");
|
||||
}
|
||||
|
||||
*cookie = 1;
|
||||
|
||||
ERRPRINT("fs_read_attrdir - EXIT\n");
|
||||
|
||||
UNLOCK_VOL(ns);
|
||||
|
||||
return B_NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
fs_open_attrib(void *_ns, void *_node, const char *name, int openMode, fs_cookie *_cookie)
|
||||
{
|
||||
nspace *ns = (nspace *)_ns;
|
||||
vnode *node = (vnode *)_node;
|
||||
int result = B_NO_ERROR;
|
||||
|
||||
LOCK_VOL(ns);
|
||||
|
||||
ERRPRINT("fs_open_attrib - ENTER\n");
|
||||
|
||||
if (strcmp(name, "BEOS:TYPE")) {
|
||||
result = ENOENT;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (node->mime == NULL) {
|
||||
result = ENOENT;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
*_cookie = &kBeOSTypeCookie;
|
||||
|
||||
exit:
|
||||
|
||||
ERRPRINT("fs_open_attrib - EXIT, result is %s\n", strerror(result));
|
||||
|
||||
UNLOCK_VOL(ns);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
fs_close_attrib(void *_ns, void *_node, fs_cookie cookie)
|
||||
{
|
||||
return B_NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
fs_free_attrib_cookie(void *_ns, void *_node, fs_cookie cookie)
|
||||
{
|
||||
return B_NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
fs_read_attrib_stat(void *_ns, void *_node, fs_cookie _cookie, struct stat *stat)
|
||||
{
|
||||
nspace *ns = (nspace *)_ns;
|
||||
vnode *node = (vnode *)_node;
|
||||
int result = B_NO_ERROR;
|
||||
|
||||
LOCK_VOL(ns);
|
||||
|
||||
ERRPRINT("fs_read_attr_stat - ENTER\n");
|
||||
|
||||
if (_cookie != &kBeOSTypeCookie) {
|
||||
result = ENOENT;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (node->mime == NULL) {
|
||||
result = ENOENT;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
stat->st_type = MIME_STRING_TYPE;
|
||||
stat->st_size = strlen(node->mime) + 1;
|
||||
|
||||
exit:
|
||||
|
||||
ERRPRINT("fs_read_attrib_stat - EXIT, result is %s\n", strerror(result));
|
||||
|
||||
UNLOCK_VOL(ns);
|
||||
|
||||
return B_NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
fs_read_attrib(void *_ns, void *_node, fs_cookie _cookie, off_t pos, void *buffer, size_t *_length)
|
||||
{
|
||||
nspace *ns = (nspace *)_ns;
|
||||
vnode *node = (vnode *)_node;
|
||||
int result = B_NO_ERROR;
|
||||
|
||||
LOCK_VOL(ns);
|
||||
|
||||
ERRPRINT("fs_read_attr - ENTER\n");
|
||||
|
||||
if (_cookie != &kBeOSTypeCookie) {
|
||||
result = ENOENT;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (node->mime == NULL) {
|
||||
result = ENOENT;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if ((pos < 0) || (pos > strlen(node->mime))) {
|
||||
result = EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
strncpy(buffer, node->mime + pos, *_length - 1);
|
||||
((char *)buffer)[*_length - 1] = 0;
|
||||
*_length = strlen(buffer) + 1;
|
||||
|
||||
exit:
|
||||
|
||||
ERRPRINT("fs_read_attr - EXIT, result is %s\n", strerror(result));
|
||||
|
||||
UNLOCK_VOL(ns);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
fs_write_attrib(void *_ns, void *_node, fs_cookie _cookie, off_t pos,
|
||||
const void *buffer, size_t *_length)
|
||||
{
|
||||
nspace *ns = (nspace *)_ns;
|
||||
int result = B_NO_ERROR;
|
||||
|
||||
LOCK_VOL(ns);
|
||||
|
||||
ERRPRINT("fs_write_attr - ENTER\n");
|
||||
|
||||
*_length = 0;
|
||||
|
||||
if (_cookie != &kBeOSTypeCookie) {
|
||||
result = ENOSYS;
|
||||
goto exit;
|
||||
}
|
||||
exit:
|
||||
|
||||
ERRPRINT("fs_write_attrib - EXIT, result is %s\n", strerror(result));
|
||||
|
||||
UNLOCK_VOL(ns);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
Copyright 1999-2001, Be Incorporated. All Rights Reserved.
|
||||
This file may be used under the terms of the Be Sample Code License.
|
||||
*/
|
||||
/* attributes.h
|
||||
* handles mime type information for ntfs
|
||||
* gets/sets mime information in vnode
|
||||
*/
|
||||
|
||||
#ifndef _fs_ATTR_H_
|
||||
#define _fs_ATTR_H_
|
||||
|
||||
#include <fs_attr.h>
|
||||
|
||||
status_t set_mime(vnode *node, const char *filename);
|
||||
|
||||
status_t fs_open_attrib_dir(void *_vol, void *_node, void **_cookie);
|
||||
status_t fs_close_attrib_dir(void *_vol, void *_node, void *_cookie);
|
||||
status_t fs_free_attrib_dir_cookie(void *_vol, void *_node, void *_cookie);
|
||||
status_t fs_rewind_attrib_dir(void *_vol, void *_node, void *_cookie);
|
||||
status_t fs_read_attrib_dir(void *_vol, void *_node, void *_cookie, struct dirent *buf, size_t bufsize, uint32 *num);
|
||||
status_t fs_open_attrib(void *_vol, void *_node, const char *name, int openMode,fs_cookie *_cookie);
|
||||
status_t fs_close_attrib(void *_vol, void *_node, fs_cookie cookie);
|
||||
status_t fs_free_attrib_cookie(void *_vol, void *_node, fs_cookie cookie);
|
||||
status_t fs_read_attrib_stat(void *_vol, void *_node, fs_cookie _cookie, struct stat *stat);
|
||||
status_t fs_read_attrib(void *_vol, void *_node, fs_cookie _cookie, off_t pos,void *buffer, size_t *_length);
|
||||
status_t fs_write_attrib(void *_vol, void *_node, fs_cookie _cookie, off_t pos, const void *buffer, size_t *_length);
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,270 @@
|
||||
/* kernel_interface - file system interface to Haiku's vnode layer
|
||||
*
|
||||
* Copyright (c) 2006 Troeglazov Gerasim (3dEyes**)
|
||||
*
|
||||
* 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 Linux-NTFS
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
#include <lock.h>
|
||||
#include <malloc.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <ByteOrder.h>
|
||||
#include <fs_info.h>
|
||||
#include <KernelExport.h>
|
||||
|
||||
#include <NodeMonitor.h>
|
||||
|
||||
#include <fs_interface.h>
|
||||
#include <fs_cache.h>
|
||||
#include <fs_attr.h>
|
||||
#include <fs_info.h>
|
||||
#include <fs_index.h>
|
||||
#include <fs_query.h>
|
||||
#include <fs_volume.h>
|
||||
|
||||
#include <util/kernel_cpp.h>
|
||||
|
||||
|
||||
extern "C"{
|
||||
|
||||
status_t fs_mount( mount_id nsid, const char *device, ulong flags, const char *args, void **data, vnode_id *vnid );
|
||||
status_t fs_unmount(void *_ns);
|
||||
status_t fs_rfsstat(void *_ns, struct fs_info *);
|
||||
status_t fs_wfsstat(void *_vol, const struct fs_info *fss, uint32 mask);
|
||||
status_t fs_sync(void *_ns);
|
||||
status_t fs_walk(void *_ns, void *_base, const char *file, vnode_id *vnid,int *_type);
|
||||
status_t fs_get_vnode_name(void *_ns, void *_node, char *buffer, size_t bufferSize);
|
||||
status_t fs_read_vnode(void *_ns, vnode_id vnid, void **_node, bool reenter);
|
||||
status_t fs_write_vnode(void *_ns, void *_node, bool reenter);
|
||||
status_t fs_remove_vnode( void *_ns, void *_node, bool reenter );
|
||||
status_t fs_access( void *ns, void *node, int mode );
|
||||
status_t fs_rstat(void *_ns, void *_node, struct stat *st);
|
||||
status_t fs_wstat(void *_vol, void *_node, const struct stat *st, uint32 mask);
|
||||
status_t fs_create(void *_ns, void *_dir, const char *name, int omode, int perms, void **_cookie, vnode_id *_vnid);
|
||||
status_t fs_open(void *_ns, void *_node, int omode, void **cookie);
|
||||
status_t fs_close(void *ns, void *node, void *cookie);
|
||||
status_t fs_free_cookie(void *ns, void *node, void *cookie);
|
||||
status_t fs_read(void *_ns, void *_node, void *cookie, off_t pos, void *buf, size_t *len);
|
||||
status_t fs_write(void *ns, void *node, void *cookie, off_t pos, const void *buf, size_t *len);
|
||||
status_t fs_mkdir(void *_ns, void *_node, const char *name, int perms, vnode_id *_vnid);
|
||||
status_t fs_rmdir(void *_ns, void *dir, const char *name);
|
||||
status_t fs_opendir(void* _ns, void* _node, void** cookie);
|
||||
status_t fs_readdir( void *_ns, void *_node, void *_cookie, struct dirent *buf, size_t bufsize, uint32 *num );
|
||||
status_t fs_rewinddir(void *_ns, void *_node, void *cookie);
|
||||
status_t fs_closedir(void *_ns, void *_node, void *cookie);
|
||||
status_t fs_free_dircookie(void *_ns, void *_node, void *cookie);
|
||||
status_t fs_readlink(void *_ns, void *_node, char *buf, size_t *bufsize);
|
||||
status_t fs_fsync(void *_ns, void *_node);
|
||||
|
||||
status_t fs_rename(void *_ns, void *_odir, const char *oldname, void *_ndir, const char *newname);
|
||||
status_t fs_unlink(void *_ns, void *_node, const char *name);
|
||||
status_t fs_create_symlink(void *_ns, void *_dir, const char *name, const char *target, int mode);
|
||||
|
||||
status_t fs_open_attrib_dir(void *_vol, void *_node, void **_cookie);
|
||||
status_t fs_close_attrib_dir(void *_vol, void *_node, void *_cookie);
|
||||
status_t fs_free_attrib_dir_cookie(void *_vol, void *_node, void *_cookie);
|
||||
status_t fs_rewind_attrib_dir(void *_vol, void *_node, void *_cookie);
|
||||
status_t fs_read_attrib_dir(void *_vol, void *_node, void *_cookie, struct dirent *buf, size_t bufsize, uint32 *num);
|
||||
status_t fs_open_attrib(void *_vol, void *_node, const char *name, int openMode,fs_cookie *_cookie);
|
||||
status_t fs_close_attrib(void *_vol, void *_node, fs_cookie cookie);
|
||||
status_t fs_free_attrib_cookie(void *_vol, void *_node, fs_cookie cookie);
|
||||
status_t fs_read_attrib_stat(void *_vol, void *_node, fs_cookie _cookie, struct stat *stat);
|
||||
status_t fs_read_attrib(void *_vol, void *_node, fs_cookie _cookie, off_t pos,void *buffer, size_t *_length);
|
||||
status_t fs_write_attrib(void *_vol, void *_node, fs_cookie _cookie, off_t pos, const void *buffer, size_t *_length);
|
||||
|
||||
void fs_free_identify_partition_cookie(partition_data *partition, void *_cookie);
|
||||
status_t fs_scan_partition(int fd, partition_data *partition, void *_cookie);
|
||||
float fs_identify_partition(int fd, partition_data *partition, void **_cookie);
|
||||
};
|
||||
|
||||
|
||||
static status_t
|
||||
ntfs_std_ops(int32 op, ...)
|
||||
{
|
||||
switch (op) {
|
||||
case B_MODULE_INIT:
|
||||
return B_OK;
|
||||
case B_MODULE_UNINIT:
|
||||
return B_OK;
|
||||
default:
|
||||
return B_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static file_system_module_info sNTFSFileSystem = {
|
||||
{
|
||||
"file_systems/ntfs" B_CURRENT_FS_API_VERSION,
|
||||
0,
|
||||
ntfs_std_ops,
|
||||
},
|
||||
|
||||
"ntfs File System",
|
||||
|
||||
// scanning
|
||||
fs_identify_partition,
|
||||
fs_scan_partition,
|
||||
fs_free_identify_partition_cookie,
|
||||
NULL, // free_partition_content_cookie()
|
||||
|
||||
&fs_mount,
|
||||
&fs_unmount,
|
||||
&fs_rfsstat,
|
||||
#ifdef _READ_ONLY_
|
||||
NULL,
|
||||
#else
|
||||
&fs_wfsstat,
|
||||
#endif
|
||||
NULL,
|
||||
|
||||
/* vnode operations */
|
||||
&fs_walk,
|
||||
&fs_get_vnode_name,
|
||||
&fs_read_vnode,
|
||||
&fs_write_vnode,
|
||||
#ifdef _READ_ONLY_
|
||||
NULL,
|
||||
#else
|
||||
&fs_remove_vnode,
|
||||
#endif
|
||||
|
||||
/* VM file access */
|
||||
NULL, // &fs_can_page
|
||||
NULL, // &fs_read_pages
|
||||
NULL, // &fs_write_pages
|
||||
|
||||
NULL, // &fs_get_file_map
|
||||
|
||||
NULL, // &fs_ioctl
|
||||
NULL, // &fs_set_flags
|
||||
NULL, // &fs_select
|
||||
NULL, // &fs_deselect
|
||||
|
||||
#ifdef _READ_ONLY_
|
||||
NULL,
|
||||
#else
|
||||
&fs_fsync,
|
||||
#endif
|
||||
|
||||
&fs_readlink,
|
||||
|
||||
#ifdef _READ_ONLY_
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
#else
|
||||
NULL, // write link
|
||||
&fs_create_symlink,
|
||||
NULL, // &fs_link,
|
||||
&fs_unlink,
|
||||
&fs_rename,
|
||||
#endif
|
||||
|
||||
&fs_access,
|
||||
&fs_rstat,
|
||||
#ifdef _READ_ONLY_
|
||||
NULL,
|
||||
#else
|
||||
&fs_wstat,
|
||||
#endif
|
||||
|
||||
/* file operations */
|
||||
#ifdef _READ_ONLY_
|
||||
NULL,
|
||||
#else
|
||||
&fs_create,
|
||||
#endif
|
||||
&fs_open,
|
||||
&fs_close,
|
||||
&fs_free_cookie,
|
||||
&fs_read,
|
||||
#ifdef _READ_ONLY_
|
||||
NULL,
|
||||
#else
|
||||
&fs_write,
|
||||
#endif
|
||||
|
||||
/* directory operations */
|
||||
#ifdef _READ_ONLY_
|
||||
NULL,
|
||||
NULL,
|
||||
#else
|
||||
&fs_mkdir,
|
||||
&fs_rmdir,
|
||||
#endif
|
||||
&fs_opendir,
|
||||
&fs_closedir,
|
||||
&fs_free_dircookie,
|
||||
&fs_readdir,
|
||||
&fs_rewinddir,
|
||||
|
||||
/* attribute directory operations */
|
||||
&fs_open_attrib_dir,
|
||||
&fs_close_attrib_dir,
|
||||
&fs_free_attrib_dir_cookie,
|
||||
&fs_read_attrib_dir,
|
||||
&fs_rewind_attrib_dir,
|
||||
|
||||
/* attribute operations */
|
||||
NULL, //&fs_create_attr,
|
||||
&fs_open_attrib,
|
||||
&fs_close_attrib,
|
||||
&fs_free_attrib_cookie,
|
||||
&fs_read_attrib,
|
||||
&fs_write_attrib,
|
||||
|
||||
&fs_read_attrib_stat,
|
||||
NULL, //&fs_write_attr_stat,
|
||||
NULL, //&fs_rename_attr,
|
||||
NULL, //&fs_remove_attr,
|
||||
|
||||
/* index directory & index operations */
|
||||
NULL, // &fs_open_index_dir
|
||||
NULL, // &fs_close_index_dir
|
||||
NULL, // &fs_free_index_dir_cookie
|
||||
NULL, // &fs_read_index_dir
|
||||
NULL, // &fs_rewind_index_dir
|
||||
|
||||
NULL, // &fs_create_index
|
||||
NULL, // &fs_remove_index
|
||||
NULL, // &fs_stat_index
|
||||
|
||||
/* query operations */
|
||||
NULL, // &fs_open_query
|
||||
NULL, // &fs_close_query
|
||||
NULL, // &fs_free_query_cookie
|
||||
NULL, // &fs_read_query
|
||||
NULL, // &fs_rewind_query
|
||||
};
|
||||
|
||||
module_info *modules[] = {
|
||||
(module_info *)&sNTFSFileSystem,
|
||||
NULL,
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
SubDir HAIKU_TOP src add-ons kernel file_systems ntfs libntfs ;
|
||||
|
||||
SubDirCcFlags -Wall -Wno-multichar -DHAVE_CONFIG_H=1 -D_BEOS_=1 ;
|
||||
|
||||
StaticLibrary libntfs.a :
|
||||
attrib.c
|
||||
attrlist.c
|
||||
bitmap.c
|
||||
bootsect.c
|
||||
collate.c
|
||||
compat.c
|
||||
compress.c
|
||||
debug.c
|
||||
device.c
|
||||
device_io.c
|
||||
dir.c
|
||||
index.c
|
||||
inode.c
|
||||
lcnalloc.c
|
||||
logfile.c
|
||||
logging.c
|
||||
mft.c
|
||||
misc.c
|
||||
mst.c
|
||||
runlist.c
|
||||
security.c
|
||||
unistr.c
|
||||
version.c
|
||||
volume.c
|
||||
;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,360 @@
|
||||
/*
|
||||
* attrib.h - Exports for attribute handling. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2000-2004 Anton Altaparmakov
|
||||
* Copyright (c) 2004-2005 Yura Pakhuchiy
|
||||
* Copyright (c) 2006 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
|
||||
* 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_ATTRIB_H
|
||||
#define _NTFS_ATTRIB_H
|
||||
|
||||
/* Forward declarations */
|
||||
typedef struct _ntfs_attr ntfs_attr;
|
||||
typedef struct _ntfs_attr_search_ctx ntfs_attr_search_ctx;
|
||||
|
||||
#include "types.h"
|
||||
#include "inode.h"
|
||||
#include "unistr.h"
|
||||
#include "runlist.h"
|
||||
#include "volume.h"
|
||||
#include "debug.h"
|
||||
#include "logging.h"
|
||||
|
||||
extern ntfschar AT_UNNAMED[];
|
||||
|
||||
/**
|
||||
* enum ntfs_lcn_special_values - special return values for ntfs_*_vcn_to_lcn()
|
||||
*
|
||||
* Special return values for ntfs_rl_vcn_to_lcn() and ntfs_attr_vcn_to_lcn().
|
||||
*
|
||||
* TODO: Describe them.
|
||||
*/
|
||||
typedef enum {
|
||||
LCN_HOLE = -1, /* Keep this as highest value or die! */
|
||||
LCN_RL_NOT_MAPPED = -2,
|
||||
LCN_ENOENT = -3,
|
||||
LCN_EINVAL = -4,
|
||||
LCN_EIO = -5,
|
||||
} ntfs_lcn_special_values;
|
||||
|
||||
/**
|
||||
* struct ntfs_attr_search_ctx - search context used in attribute search functions
|
||||
* @mrec: buffer containing mft record to search
|
||||
* @attr: attribute record in @mrec where to begin/continue search
|
||||
* @is_first: if true lookup_attr() begins search with @attr, else after @attr
|
||||
*
|
||||
* Structure must be initialized to zero before the first call to one of the
|
||||
* attribute search functions. Initialize @mrec to point to the mft record to
|
||||
* search, and @attr to point to the first attribute within @mrec (not necessary
|
||||
* if calling the _first() functions), and set @is_first to TRUE (not necessary
|
||||
* if calling the _first() functions).
|
||||
*
|
||||
* If @is_first is TRUE, the search begins with @attr. If @is_first is FALSE,
|
||||
* the search begins after @attr. This is so that, after the first call to one
|
||||
* of the search attribute functions, we can call the function again, without
|
||||
* any modification of the search context, to automagically get the next
|
||||
* matching attribute.
|
||||
*/
|
||||
struct _ntfs_attr_search_ctx {
|
||||
MFT_RECORD *mrec;
|
||||
ATTR_RECORD *attr;
|
||||
BOOL is_first;
|
||||
ntfs_inode *ntfs_ino;
|
||||
ATTR_LIST_ENTRY *al_entry;
|
||||
ntfs_inode *base_ntfs_ino;
|
||||
MFT_RECORD *base_mrec;
|
||||
ATTR_RECORD *base_attr;
|
||||
};
|
||||
|
||||
extern void ntfs_attr_reinit_search_ctx(ntfs_attr_search_ctx *ctx);
|
||||
extern ntfs_attr_search_ctx *ntfs_attr_get_search_ctx(ntfs_inode *ni,
|
||||
MFT_RECORD *mrec);
|
||||
extern void ntfs_attr_put_search_ctx(ntfs_attr_search_ctx *ctx);
|
||||
|
||||
extern int ntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name,
|
||||
const u32 name_len, const IGNORE_CASE_BOOL ic,
|
||||
const VCN lowest_vcn, const u8 *val, const u32 val_len,
|
||||
ntfs_attr_search_ctx *ctx);
|
||||
|
||||
extern ATTR_DEF *ntfs_attr_find_in_attrdef(const ntfs_volume *vol,
|
||||
const ATTR_TYPES type);
|
||||
|
||||
/**
|
||||
* ntfs_attrs_walk - syntactic sugar for walking all attributes in an inode
|
||||
* @ctx: initialised attribute search context
|
||||
*
|
||||
* Syntactic sugar for walking attributes in an inode.
|
||||
*
|
||||
* Return 0 on success and -1 on error with errno set to the error code from
|
||||
* ntfs_attr_lookup().
|
||||
*
|
||||
* Example: When you want to enumerate all attributes in an open ntfs inode
|
||||
* @ni, you can simply do:
|
||||
*
|
||||
* int err;
|
||||
* ntfs_attr_search_ctx *ctx = ntfs_attr_get_search_ctx(ni, NULL);
|
||||
* if (!ctx)
|
||||
* // Error code is in errno. Handle this case.
|
||||
* while (!(err = ntfs_attrs_walk(ctx))) {
|
||||
* ATTR_RECORD *attr = ctx->attr;
|
||||
* // attr now contains the next attribute. Do whatever you want
|
||||
* // with it and then just continue with the while loop.
|
||||
* }
|
||||
* if (err && errno != ENOENT)
|
||||
* // Ooops. An error occurred! You should handle this case.
|
||||
* // Now finished with all attributes in the inode.
|
||||
*/
|
||||
static __inline__ int ntfs_attrs_walk(ntfs_attr_search_ctx *ctx)
|
||||
{
|
||||
return ntfs_attr_lookup(AT_UNUSED, NULL, 0, CASE_SENSITIVE, 0,
|
||||
NULL, 0, ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* struct ntfs_attr - ntfs in memory non-resident attribute structure
|
||||
* @rl: if not NULL, the decompressed runlist
|
||||
* @ni: base ntfs inode to which this attribute belongs
|
||||
* @type: attribute type
|
||||
* @name: Unicode name of the attribute
|
||||
* @name_len: length of @name in Unicode characters
|
||||
* @state: NTFS attribute specific flags describing this attribute
|
||||
* @allocated_size: copy from the attribute record
|
||||
* @data_size: copy from the attribute record
|
||||
* @initialized_size: copy from the attribute record
|
||||
* @compressed_size: copy from the attribute record
|
||||
* @compression_block_size: size of a compression block (cb)
|
||||
* @compression_block_size_bits: log2 of the size of a cb
|
||||
* @compression_block_clusters: number of clusters per cb
|
||||
*
|
||||
* This structure exists purely to provide a mechanism of caching the runlist
|
||||
* of an attribute. If you want to operate on a particular attribute extent,
|
||||
* you should not be using this structure at all. If you want to work with a
|
||||
* resident attribute, you should not be using this structure at all. As a
|
||||
* fail-safe check make sure to test NAttrNonResident() and if it is false, you
|
||||
* know you shouldn't be using this structure.
|
||||
*
|
||||
* If you want to work on a resident attribute or on a specific attribute
|
||||
* extent, you should use ntfs_lookup_attr() to retrieve the attribute (extent)
|
||||
* record, edit that, and then write back the mft record (or set the
|
||||
* corresponding ntfs inode dirty for delayed write back).
|
||||
*
|
||||
* @rl is the decompressed runlist of the attribute described by this
|
||||
* structure. Obviously this only makes sense if the attribute is not resident,
|
||||
* i.e. NAttrNonResident() is true. If the runlist hasn't been decompressed yet
|
||||
* @rl is NULL, so be prepared to cope with @rl == NULL.
|
||||
*
|
||||
* @ni is the base ntfs inode of the attribute described by this structure.
|
||||
*
|
||||
* @type is the attribute type (see layout.h for the definition of ATTR_TYPES),
|
||||
* @name and @name_len are the little endian Unicode name and the name length
|
||||
* in Unicode characters of the attribute, respectively.
|
||||
*
|
||||
* @state contains NTFS attribute specific flags describing this attribute
|
||||
* structure. See ntfs_attr_state_bits above.
|
||||
*/
|
||||
struct _ntfs_attr {
|
||||
runlist_element *rl;
|
||||
ntfs_inode *ni;
|
||||
ATTR_TYPES type;
|
||||
ntfschar *name;
|
||||
u32 name_len;
|
||||
unsigned long state;
|
||||
s64 allocated_size;
|
||||
s64 data_size;
|
||||
s64 initialized_size;
|
||||
s64 compressed_size;
|
||||
u32 compression_block_size;
|
||||
u8 compression_block_size_bits;
|
||||
u8 compression_block_clusters;
|
||||
};
|
||||
|
||||
/**
|
||||
* enum ntfs_attr_state_bits - bits for the state field in the ntfs_attr structure
|
||||
*/
|
||||
typedef enum {
|
||||
NA_Initialized, /* 1: structure is initialized. */
|
||||
NA_NonResident, /* 1: Attribute is not resident. */
|
||||
} ntfs_attr_state_bits;
|
||||
|
||||
#define test_nattr_flag(na, flag) test_bit(NA_##flag, (na)->state)
|
||||
#define set_nattr_flag(na, flag) set_bit(NA_##flag, (na)->state)
|
||||
#define clear_nattr_flag(na, flag) clear_bit(NA_##flag, (na)->state)
|
||||
|
||||
#define NAttrInitialized(na) test_nattr_flag(na, Initialized)
|
||||
#define NAttrSetInitialized(na) set_nattr_flag(na, Initialized)
|
||||
#define NAttrClearInitialized(na) clear_nattr_flag(na, Initialized)
|
||||
|
||||
#define NAttrNonResident(na) test_nattr_flag(na, NonResident)
|
||||
#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; \
|
||||
}
|
||||
|
||||
GenNAttrIno(Compressed, COMPRESSED)
|
||||
GenNAttrIno(Encrypted, ENCRYPTED)
|
||||
GenNAttrIno(Sparse, SPARSE_FILE)
|
||||
|
||||
/**
|
||||
* union attr_val - Union of all known attribute values
|
||||
*
|
||||
* For convenience. Used in the attr structure.
|
||||
*/
|
||||
typedef union {
|
||||
u8 _default; /* Unnamed u8 to serve as default when just using
|
||||
a_val without specifying any of the below. */
|
||||
STANDARD_INFORMATION std_inf;
|
||||
ATTR_LIST_ENTRY al_entry;
|
||||
FILE_NAME_ATTR filename;
|
||||
OBJECT_ID_ATTR obj_id;
|
||||
SECURITY_DESCRIPTOR_ATTR sec_desc;
|
||||
VOLUME_NAME vol_name;
|
||||
VOLUME_INFORMATION vol_inf;
|
||||
DATA_ATTR data;
|
||||
INDEX_ROOT index_root;
|
||||
INDEX_BLOCK index_blk;
|
||||
BITMAP_ATTR bmp;
|
||||
REPARSE_POINT reparse;
|
||||
EA_INFORMATION ea_inf;
|
||||
EA_ATTR ea;
|
||||
PROPERTY_SET property_set;
|
||||
LOGGED_UTILITY_STREAM logged_util_stream;
|
||||
EFS_ATTR_HEADER efs;
|
||||
} attr_val;
|
||||
|
||||
extern void ntfs_attr_init(ntfs_attr *na, const BOOL non_resident,
|
||||
const BOOL compressed, const BOOL encrypted, const BOOL sparse,
|
||||
const s64 allocated_size, const s64 data_size,
|
||||
const s64 initialized_size, const s64 compressed_size,
|
||||
const u8 compression_unit);
|
||||
|
||||
extern ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type,
|
||||
ntfschar *name, u32 name_len);
|
||||
extern void ntfs_attr_close(ntfs_attr *na);
|
||||
|
||||
extern s64 ntfs_attr_pread(ntfs_attr *na, const s64 pos, s64 count,
|
||||
void *b);
|
||||
extern s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count,
|
||||
const void *b);
|
||||
|
||||
extern void *ntfs_attr_readall(ntfs_inode *ni, const ATTR_TYPES type,
|
||||
ntfschar *name, u32 name_len, s64 *data_size);
|
||||
|
||||
extern s64 ntfs_attr_mst_pread(ntfs_attr *na, const s64 pos,
|
||||
const s64 bk_cnt, const u32 bk_size, void *dst);
|
||||
extern s64 ntfs_attr_mst_pwrite(ntfs_attr *na, const s64 pos,
|
||||
s64 bk_cnt, const u32 bk_size, void *src);
|
||||
|
||||
extern int ntfs_attr_map_runlist(ntfs_attr *na, VCN vcn);
|
||||
extern int ntfs_attr_map_whole_runlist(ntfs_attr *na);
|
||||
|
||||
extern LCN ntfs_attr_vcn_to_lcn(ntfs_attr *na, const VCN vcn);
|
||||
extern runlist_element *ntfs_attr_find_vcn(ntfs_attr *na, const VCN vcn);
|
||||
|
||||
extern int ntfs_attr_size_bounds_check(const ntfs_volume *vol,
|
||||
const ATTR_TYPES type, const s64 size);
|
||||
extern int ntfs_attr_can_be_non_resident(const ntfs_volume *vol,
|
||||
const ATTR_TYPES type);
|
||||
extern int ntfs_attr_can_be_resident(const ntfs_volume *vol,
|
||||
const ATTR_TYPES type);
|
||||
|
||||
extern int ntfs_make_room_for_attr(MFT_RECORD *m, u8 *pos, u32 size);
|
||||
|
||||
extern int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type,
|
||||
ntfschar *name, u8 name_len, u8 *val, u32 size,
|
||||
ATTR_FLAGS flags);
|
||||
extern int ntfs_non_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type,
|
||||
ntfschar *name, u8 name_len, VCN lowest_vcn, int dataruns_size,
|
||||
ATTR_FLAGS flags);
|
||||
extern int ntfs_attr_record_rm(ntfs_attr_search_ctx *ctx);
|
||||
|
||||
extern int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type,
|
||||
ntfschar *name, u8 name_len, u8 *val, s64 size);
|
||||
extern int ntfs_attr_rm(ntfs_attr *na);
|
||||
|
||||
extern int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size);
|
||||
|
||||
extern int ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a,
|
||||
const u32 new_size);
|
||||
|
||||
extern int ntfs_attr_record_move_to(ntfs_attr_search_ctx *ctx, ntfs_inode *ni);
|
||||
extern int ntfs_attr_record_move_away(ntfs_attr_search_ctx *ctx, int extra);
|
||||
|
||||
extern int ntfs_attr_update_mapping_pairs(ntfs_attr *na, VCN from_vcn);
|
||||
|
||||
extern int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize);
|
||||
|
||||
// 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
|
||||
*
|
||||
* Return the byte size of the attribute value of the attribute @a (as it
|
||||
* would be after eventual decompression and filling in of holes if sparse).
|
||||
* If we return 0, check errno. If errno is 0 the actual length was 0,
|
||||
* otherwise errno describes the error.
|
||||
*
|
||||
* FIXME: Describe possible errnos.
|
||||
*/
|
||||
extern s64 ntfs_get_attribute_value_length(const ATTR_RECORD *a);
|
||||
|
||||
/**
|
||||
* get_attribute_value - return the attribute value of an attribute
|
||||
* @vol: volume on which the attribute is present
|
||||
* @a: attribute to get the value of
|
||||
* @b: destination buffer for the attribute value
|
||||
*
|
||||
* Make a copy of the attribute value of the attribute @a into the destination
|
||||
* buffer @b. Note, that the size of @b has to be at least equal to the value
|
||||
* returned by get_attribute_value_length(@a).
|
||||
*
|
||||
* Return number of bytes copied. If this is zero check errno. If errno is 0
|
||||
* then nothing was read due to a zero-length attribute value, otherwise
|
||||
* errno describes the error.
|
||||
*/
|
||||
extern s64 ntfs_get_attribute_value(const ntfs_volume *vol,
|
||||
const ATTR_RECORD *a, u8 *b);
|
||||
|
||||
extern void ntfs_attr_name_free(char **name);
|
||||
extern char *ntfs_attr_name_get(const ntfschar *uname, const int uname_len);
|
||||
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);
|
||||
|
||||
#endif /* defined _NTFS_ATTRIB_H */
|
||||
|
||||
@@ -0,0 +1,315 @@
|
||||
/**
|
||||
* attrlist.c - Attribute list attribute handling code. Originated from the Linux-NTFS
|
||||
* project.
|
||||
*
|
||||
* Copyright (c) 2004-2005 Anton Altaparmakov
|
||||
* Copyright (c) 2004-2005 Yura Pakhuchiy
|
||||
* Copyright (c) 2006 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
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file is distributed in the hope that it will be
|
||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
#include "layout.h"
|
||||
#include "attrib.h"
|
||||
#include "attrlist.h"
|
||||
#include "debug.h"
|
||||
#include "unistr.h"
|
||||
#include "logging.h"
|
||||
#include "misc.h"
|
||||
|
||||
/**
|
||||
* ntfs_attrlist_need - check whether inode need attribute list
|
||||
* @ni: opened ntfs inode for which perform check
|
||||
*
|
||||
* Check whether all are attributes belong to one MFT record, in that case
|
||||
* attribute list is not needed.
|
||||
*
|
||||
* Return 1 if inode need attribute list, 0 if not, -1 on error with errno set
|
||||
* to the error code. If function succeed errno set to 0. The following error
|
||||
* codes are defined:
|
||||
* EINVAL - Invalid arguments passed to function or attribute haven't got
|
||||
* attribute list.
|
||||
*/
|
||||
int ntfs_attrlist_need(ntfs_inode *ni)
|
||||
{
|
||||
ATTR_LIST_ENTRY *ale;
|
||||
|
||||
if (!ni) {
|
||||
ntfs_log_trace("Invalid arguments.\n");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no);
|
||||
|
||||
if (!NInoAttrList(ni)) {
|
||||
ntfs_log_trace("Inode haven't got attribute list.\n");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!ni->attr_list) {
|
||||
ntfs_log_trace("Corrupt in-memory struct.\n");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
ale = (ATTR_LIST_ENTRY *)ni->attr_list;
|
||||
while ((u8*)ale < ni->attr_list + ni->attr_list_size) {
|
||||
if (MREF_LE(ale->mft_reference) != ni->mft_no)
|
||||
return 1;
|
||||
ale = (ATTR_LIST_ENTRY *)((u8*)ale + le16_to_cpu(ale->length));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_attrlist_entry_add - add an attribute list attribute entry
|
||||
* @ni: opened ntfs inode, which contains that attribute
|
||||
* @attr: attribute record to add to attribute list
|
||||
*
|
||||
* Return 0 on success and -1 on error with errno set to the error code. The
|
||||
* following error codes are defined:
|
||||
* EINVAL - Invalid arguments passed to function.
|
||||
* ENOMEM - Not enough memory to allocate necessary buffers.
|
||||
* EIO - I/O error occurred or damaged filesystem.
|
||||
* EEXIST - Such attribute already present in attribute list.
|
||||
*/
|
||||
int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr)
|
||||
{
|
||||
ATTR_LIST_ENTRY *ale;
|
||||
MFT_REF mref;
|
||||
ntfs_attr *na = NULL;
|
||||
ntfs_attr_search_ctx *ctx;
|
||||
u8 *new_al;
|
||||
int entry_len, entry_offset, err;
|
||||
|
||||
ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n",
|
||||
(long long) ni->mft_no,
|
||||
(unsigned) le32_to_cpu(attr->type));
|
||||
|
||||
if (!ni || !attr) {
|
||||
ntfs_log_trace("Invalid arguments.\n");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
mref = MK_LE_MREF(ni->mft_no, le16_to_cpu(ni->mrec->sequence_number));
|
||||
|
||||
if (ni->nr_extents == -1)
|
||||
ni = ni->base_ni;
|
||||
|
||||
if (!NInoAttrList(ni)) {
|
||||
ntfs_log_trace("Attribute list isn't present.\n");
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Determine size and allocate memory for new attribute list. */
|
||||
entry_len = (sizeof(ATTR_LIST_ENTRY) + sizeof(ntfschar) *
|
||||
attr->name_length + 7) & ~7;
|
||||
new_al = ntfs_calloc(ni->attr_list_size + entry_len);
|
||||
if (!new_al)
|
||||
return -1;
|
||||
|
||||
/* Find place for the new entry. */
|
||||
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*)
|
||||
((u8*)attr + le16_to_cpu(attr->name_offset)) :
|
||||
AT_UNNAMED, attr->name_length, CASE_SENSITIVE,
|
||||
(attr->non_resident) ? le64_to_cpu(attr->lowest_vcn) :
|
||||
0, (attr->non_resident) ? NULL : ((u8*)attr +
|
||||
le16_to_cpu(attr->value_offset)), (attr->non_resident) ?
|
||||
0 : le32_to_cpu(attr->value_length), ctx)) {
|
||||
/* Found some extent, check it to be before new extent. */
|
||||
if (ctx->al_entry->lowest_vcn == attr->lowest_vcn) {
|
||||
err = EEXIST;
|
||||
ntfs_log_trace("Such attribute already present in the "
|
||||
"attribute list.\n");
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
goto err_out;
|
||||
}
|
||||
/* Add new entry after this extent. */
|
||||
ale = (ATTR_LIST_ENTRY*)((u8*)ctx->al_entry +
|
||||
le16_to_cpu(ctx->al_entry->length));
|
||||
} else {
|
||||
/* Check for real errors. */
|
||||
if (errno != ENOENT) {
|
||||
err = errno;
|
||||
ntfs_log_trace("Attribute lookup failed.\n");
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
goto err_out;
|
||||
}
|
||||
/* No previous extents found. */
|
||||
ale = ctx->al_entry;
|
||||
}
|
||||
/* Don't need it anymore, @ctx->al_entry points to @ni->attr_list. */
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
|
||||
/* Determine new entry offset. */
|
||||
entry_offset = ((u8 *)ale - ni->attr_list);
|
||||
/* Set pointer to new entry. */
|
||||
ale = (ATTR_LIST_ENTRY *)(new_al + entry_offset);
|
||||
/* Zero it to fix valgrind warning. */
|
||||
memset(ale, 0, entry_len);
|
||||
/* Form new entry. */
|
||||
ale->type = attr->type;
|
||||
ale->length = cpu_to_le16(entry_len);
|
||||
ale->name_length = attr->name_length;
|
||||
ale->name_offset = offsetof(ATTR_LIST_ENTRY, name);
|
||||
if (attr->non_resident)
|
||||
ale->lowest_vcn = attr->lowest_vcn;
|
||||
else
|
||||
ale->lowest_vcn = 0;
|
||||
ale->mft_reference = mref;
|
||||
ale->instance = attr->instance;
|
||||
memcpy(ale->name, (u8 *)attr + le16_to_cpu(attr->name_offset),
|
||||
attr->name_length * sizeof(ntfschar));
|
||||
|
||||
/* Resize $ATTRIBUTE_LIST to new length. */
|
||||
na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0);
|
||||
if (!na) {
|
||||
err = errno;
|
||||
ntfs_log_trace("Failed to open $ATTRIBUTE_LIST attribute.\n");
|
||||
goto err_out;
|
||||
}
|
||||
if (ntfs_attr_truncate(na, ni->attr_list_size + entry_len)) {
|
||||
err = errno;
|
||||
ntfs_log_trace("$ATTRIBUTE_LIST resize failed.\n");
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
/* Copy entries from old attribute list to new. */
|
||||
memcpy(new_al, ni->attr_list, entry_offset);
|
||||
memcpy(new_al + entry_offset + entry_len, ni->attr_list +
|
||||
entry_offset, ni->attr_list_size - entry_offset);
|
||||
|
||||
/* Set new runlist. */
|
||||
free(ni->attr_list);
|
||||
ni->attr_list = new_al;
|
||||
ni->attr_list_size = ni->attr_list_size + entry_len;
|
||||
NInoAttrListSetDirty(ni);
|
||||
/* Done! */
|
||||
ntfs_attr_close(na);
|
||||
return 0;
|
||||
err_out:
|
||||
if (na)
|
||||
ntfs_attr_close(na);
|
||||
free(new_al);
|
||||
errno = err;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_attrlist_entry_rm - remove an attribute list attribute entry
|
||||
* @ctx: attribute search context describing the attribute list entry
|
||||
*
|
||||
* Remove the attribute list entry @ctx->al_entry from the attribute list.
|
||||
*
|
||||
* Return 0 on success and -1 on error with errno set to the error code.
|
||||
*/
|
||||
int ntfs_attrlist_entry_rm(ntfs_attr_search_ctx *ctx)
|
||||
{
|
||||
u8 *new_al;
|
||||
int new_al_len;
|
||||
ntfs_inode *base_ni;
|
||||
ntfs_attr *na;
|
||||
ATTR_LIST_ENTRY *ale;
|
||||
int err;
|
||||
|
||||
if (!ctx || !ctx->ntfs_ino || !ctx->al_entry) {
|
||||
ntfs_log_trace("Invalid arguments.\n");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ctx->base_ntfs_ino)
|
||||
base_ni = ctx->base_ntfs_ino;
|
||||
else
|
||||
base_ni = ctx->ntfs_ino;
|
||||
ale = ctx->al_entry;
|
||||
|
||||
ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, lowest_vcn %lld.\n",
|
||||
(long long) ctx->ntfs_ino->mft_no,
|
||||
(unsigned) le32_to_cpu(ctx->al_entry->type),
|
||||
(long long) le64_to_cpu(ctx->al_entry->lowest_vcn));
|
||||
|
||||
if (!NInoAttrList(base_ni)) {
|
||||
ntfs_log_trace("Attribute list isn't present.\n");
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Allocate memory for new attribute list. */
|
||||
new_al_len = base_ni->attr_list_size - le16_to_cpu(ale->length);
|
||||
new_al = ntfs_calloc(new_al_len);
|
||||
if (!new_al)
|
||||
return -1;
|
||||
|
||||
/* Reisze $ATTRIBUTE_LIST to new length. */
|
||||
na = ntfs_attr_open(base_ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0);
|
||||
if (!na) {
|
||||
err = errno;
|
||||
ntfs_log_trace("Failed to open $ATTRIBUTE_LIST attribute.\n");
|
||||
goto err_out;
|
||||
}
|
||||
if (ntfs_attr_truncate(na, new_al_len)) {
|
||||
err = errno;
|
||||
ntfs_log_trace("$ATTRIBUTE_LIST resize failed.\n");
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
/* Copy entries from old attribute list to new. */
|
||||
memcpy(new_al, base_ni->attr_list, (u8*)ale - base_ni->attr_list);
|
||||
memcpy(new_al + ((u8*)ale - base_ni->attr_list), (u8*)ale + le16_to_cpu(
|
||||
ale->length), new_al_len - ((u8*)ale - base_ni->attr_list));
|
||||
|
||||
/* Set new runlist. */
|
||||
free(base_ni->attr_list);
|
||||
base_ni->attr_list = new_al;
|
||||
base_ni->attr_list_size = new_al_len;
|
||||
NInoAttrListSetDirty(base_ni);
|
||||
/* Done! */
|
||||
ntfs_attr_close(na);
|
||||
return 0;
|
||||
err_out:
|
||||
if (na)
|
||||
ntfs_attr_close(na);
|
||||
free(new_al);
|
||||
errno = err;
|
||||
return -1;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* attrlist.h - Exports for attribute list attribute handling.
|
||||
* Originated from Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2004 Anton Altaparmakov
|
||||
* Copyright (c) 2004 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
|
||||
* 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_ATTRLIST_H
|
||||
#define _NTFS_ATTRLIST_H
|
||||
|
||||
#include "attrib.h"
|
||||
|
||||
extern int ntfs_attrlist_need(ntfs_inode *ni);
|
||||
|
||||
extern int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr);
|
||||
extern int ntfs_attrlist_entry_rm(ntfs_attr_search_ctx *ctx);
|
||||
|
||||
/**
|
||||
* ntfs_attrlist_mark_dirty - set the attribute list dirty
|
||||
* @ni: ntfs inode which base inode contain dirty attribute list
|
||||
*
|
||||
* Set the attribute list dirty so it is written out later (at the latest at
|
||||
* ntfs_inode_close() time).
|
||||
*
|
||||
* This function cannot fail.
|
||||
*/
|
||||
static __inline__ void ntfs_attrlist_mark_dirty(ntfs_inode *ni)
|
||||
{
|
||||
if (ni->nr_extents == -1)
|
||||
NInoAttrListSetDirty(ni->base_ni);
|
||||
else
|
||||
NInoAttrListSetDirty(ni);
|
||||
}
|
||||
|
||||
#endif /* defined _NTFS_ATTRLIST_H */
|
||||
@@ -0,0 +1,289 @@
|
||||
/**
|
||||
* bitmap.c - Bitmap handling code. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2002-2006 Anton Altaparmakov
|
||||
* Copyright (c) 2004-2005 Richard Russon
|
||||
* Copyright (c) 2004-2006 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
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file is distributed in the hope that it will be
|
||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDIO_H
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
#include "attrib.h"
|
||||
#include "bitmap.h"
|
||||
#include "debug.h"
|
||||
#include "logging.h"
|
||||
#include "misc.h"
|
||||
|
||||
/**
|
||||
* ntfs_bit_set - set a bit in a field of bits
|
||||
* @bitmap: field of bits
|
||||
* @bit: bit to set
|
||||
* @new_value: value to set bit to (0 or 1)
|
||||
*
|
||||
* Set the bit @bit in the @bitmap to @new_value. Ignore all errors.
|
||||
*/
|
||||
void ntfs_bit_set(u8 *bitmap, const u64 bit, const u8 new_value)
|
||||
{
|
||||
if (!bitmap || new_value > 1)
|
||||
return;
|
||||
if (!new_value)
|
||||
bitmap[bit >> 3] &= ~(1 << (bit & 7));
|
||||
else
|
||||
bitmap[bit >> 3] |= (1 << (bit & 7));
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_bit_get - get value of a bit in a field of bits
|
||||
* @bitmap: field of bits
|
||||
* @bit: bit to get
|
||||
*
|
||||
* Get and return the value of the bit @bit in @bitmap (0 or 1).
|
||||
* Return -1 on error.
|
||||
*/
|
||||
char ntfs_bit_get(const u8 *bitmap, const u64 bit)
|
||||
{
|
||||
if (!bitmap)
|
||||
return -1;
|
||||
return (bitmap[bit >> 3] >> (bit & 7)) & 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_bit_get_and_set - get value of a bit in a field of bits and set it
|
||||
* @bitmap: field of bits
|
||||
* @bit: bit to get/set
|
||||
* @new_value: value to set bit to (0 or 1)
|
||||
*
|
||||
* Return the value of the bit @bit and set it to @new_value (0 or 1).
|
||||
* Return -1 on error.
|
||||
*/
|
||||
char ntfs_bit_get_and_set(u8 *bitmap, const u64 bit, const u8 new_value)
|
||||
{
|
||||
register u8 old_bit, shift;
|
||||
|
||||
if (!bitmap || new_value > 1)
|
||||
return -1;
|
||||
shift = bit & 7;
|
||||
old_bit = (bitmap[bit >> 3] >> shift) & 1;
|
||||
if (new_value != old_bit)
|
||||
bitmap[bit >> 3] ^= 1 << shift;
|
||||
return old_bit;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_bitmap_set_bits_in_run - set a run of bits in a bitmap to a value
|
||||
* @na: attribute containing the bitmap
|
||||
* @start_bit: first bit to set
|
||||
* @count: number of bits to set
|
||||
* @value: value to set the bits to (i.e. 0 or 1)
|
||||
*
|
||||
* Set @count bits starting at bit @start_bit in the bitmap described by the
|
||||
* attribute @na to @value, where @value is either 0 or 1.
|
||||
*
|
||||
* On success return 0 and on error return -1 with errno set to the error code.
|
||||
*/
|
||||
static int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit,
|
||||
s64 count, int value)
|
||||
{
|
||||
s64 bufsize, br;
|
||||
u8 *buf, *lastbyte_buf;
|
||||
int bit, firstbyte, lastbyte, lastbyte_pos, tmp, err;
|
||||
|
||||
if (!na || start_bit < 0 || count < 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
bit = start_bit & 7;
|
||||
if (bit)
|
||||
firstbyte = 1;
|
||||
else
|
||||
firstbyte = 0;
|
||||
|
||||
/* Calculate the required buffer size in bytes, capping it at 8kiB. */
|
||||
bufsize = ((count - (bit ? 8 - bit : 0) + 7) >> 3) + firstbyte;
|
||||
if (bufsize > 8192)
|
||||
bufsize = 8192;
|
||||
|
||||
buf = ntfs_malloc(bufsize);
|
||||
if (!buf)
|
||||
return -1;
|
||||
|
||||
/* Depending on @value, zero or set all bits in the allocated buffer. */
|
||||
memset(buf, value ? 0xff : 0, bufsize);
|
||||
|
||||
/* If there is a first partial byte... */
|
||||
if (bit) {
|
||||
/* read it in... */
|
||||
br = ntfs_attr_pread(na, start_bit >> 3, 1, buf);
|
||||
if (br != 1) {
|
||||
free(buf);
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
/* and set or clear the appropriate bits in it. */
|
||||
while ((bit & 7) && count--) {
|
||||
if (value)
|
||||
*buf |= 1 << bit++;
|
||||
else
|
||||
*buf &= ~(1 << bit++);
|
||||
}
|
||||
/* Update @start_bit to the new position. */
|
||||
start_bit = (start_bit + 7) & ~7;
|
||||
}
|
||||
|
||||
/* Loop until @count reaches zero. */
|
||||
lastbyte = 0;
|
||||
lastbyte_buf = NULL;
|
||||
bit = count & 7;
|
||||
do {
|
||||
/* If there is a last partial byte... */
|
||||
if (count > 0 && bit) {
|
||||
lastbyte_pos = ((count + 7) >> 3) + firstbyte;
|
||||
if (!lastbyte_pos) {
|
||||
// FIXME: Eeek! BUG!
|
||||
ntfs_log_trace("Eeek! lastbyte is zero. Leaving "
|
||||
"inconsistent metadata.\n");
|
||||
err = EIO;
|
||||
goto free_err_out;
|
||||
}
|
||||
/* and it is in the currently loaded bitmap window... */
|
||||
if (lastbyte_pos <= bufsize) {
|
||||
lastbyte_buf = buf + lastbyte_pos - 1;
|
||||
|
||||
/* read the byte in... */
|
||||
br = ntfs_attr_pread(na, (start_bit + count) >>
|
||||
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;
|
||||
goto free_err_out;
|
||||
}
|
||||
/* and set/clear the appropriate bits in it. */
|
||||
while (bit && count--) {
|
||||
if (value)
|
||||
*lastbyte_buf |= 1 << --bit;
|
||||
else
|
||||
*lastbyte_buf &= ~(1 << --bit);
|
||||
}
|
||||
/* We don't want to come back here... */
|
||||
bit = 0;
|
||||
/* We have a last byte that we have handled. */
|
||||
lastbyte = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Write the prepared buffer to disk. */
|
||||
tmp = (start_bit >> 3) - firstbyte;
|
||||
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;
|
||||
goto free_err_out;
|
||||
}
|
||||
|
||||
/* Update counters. */
|
||||
tmp = (bufsize - firstbyte - lastbyte) << 3;
|
||||
if (firstbyte) {
|
||||
firstbyte = 0;
|
||||
/*
|
||||
* Re-set the partial first byte so a subsequent write
|
||||
* of the buffer does not have stale, incorrect bits.
|
||||
*/
|
||||
*buf = value ? 0xff : 0;
|
||||
}
|
||||
start_bit += tmp;
|
||||
count -= tmp;
|
||||
if (bufsize > (tmp = (count + 7) >> 3))
|
||||
bufsize = tmp;
|
||||
|
||||
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;
|
||||
goto free_err_out;
|
||||
}
|
||||
} while (count > 0);
|
||||
|
||||
/* Done! */
|
||||
free(buf);
|
||||
return 0;
|
||||
|
||||
free_err_out:
|
||||
free(buf);
|
||||
errno = err;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_bitmap_set_run - set a run of bits in a bitmap
|
||||
* @na: attribute containing the bitmap
|
||||
* @start_bit: first bit to set
|
||||
* @count: number of bits to set
|
||||
*
|
||||
* Set @count bits starting at bit @start_bit in the bitmap described by the
|
||||
* attribute @na.
|
||||
*
|
||||
* On success return 0 and on error return -1 with errno set to the error code.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_bitmap_clear_run - clear a run of bits in a bitmap
|
||||
* @na: attribute containing the bitmap
|
||||
* @start_bit: first bit to clear
|
||||
* @count: number of bits to clear
|
||||
*
|
||||
* Clear @count bits starting at bit @start_bit in the bitmap described by the
|
||||
* attribute @na.
|
||||
*
|
||||
* On success return 0 and on error return -1 with errno set to the error code.
|
||||
*/
|
||||
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",
|
||||
(long long)start_bit, (long long)count);
|
||||
|
||||
return ntfs_bitmap_set_bits_in_run(na, start_bit, count, 0);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* bitmap.h - Exports for bitmap handling. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2000-2004 Anton Altaparmakov
|
||||
* Copyright (c) 2004-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_BITMAP_H
|
||||
#define _NTFS_BITMAP_H
|
||||
|
||||
#include "types.h"
|
||||
#include "attrib.h"
|
||||
|
||||
/*
|
||||
* NOTES:
|
||||
*
|
||||
* - Operations are 8-bit only to ensure the functions work both on little
|
||||
* and big endian machines! So don't make them 32-bit ops!
|
||||
* - bitmap starts at bit = 0 and ends at bit = bitmap size - 1.
|
||||
* - _Caller_ has to make sure that the bit to operate on is less than the
|
||||
* size of the bitmap.
|
||||
*/
|
||||
|
||||
extern void ntfs_bit_set(u8 *bitmap, const u64 bit, const u8 new_value);
|
||||
extern char ntfs_bit_get(const u8 *bitmap, const u64 bit);
|
||||
extern char ntfs_bit_get_and_set(u8 *bitmap, const u64 bit, const u8 new_value);
|
||||
extern int ntfs_bitmap_set_run(ntfs_attr *na, s64 start_bit, s64 count);
|
||||
extern int ntfs_bitmap_clear_run(ntfs_attr *na, s64 start_bit, s64 count);
|
||||
|
||||
/**
|
||||
* ntfs_bitmap_set_bit - set a bit in a bitmap
|
||||
* @na: attribute containing the bitmap
|
||||
* @bit: bit to set
|
||||
*
|
||||
* Set the @bit in the bitmap described by the attribute @na.
|
||||
*
|
||||
* On success return 0 and on error return -1 with errno set to the error code.
|
||||
*/
|
||||
static __inline__ int ntfs_bitmap_set_bit(ntfs_attr *na, s64 bit)
|
||||
{
|
||||
return ntfs_bitmap_set_run(na, bit, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_bitmap_clear_bit - clear a bit in a bitmap
|
||||
* @na: attribute containing the bitmap
|
||||
* @bit: bit to clear
|
||||
*
|
||||
* Clear @bit in the bitmap described by the attribute @na.
|
||||
*
|
||||
* On success return 0 and on error return -1 with errno set to the error code.
|
||||
*/
|
||||
static __inline__ int ntfs_bitmap_clear_bit(ntfs_attr *na, s64 bit)
|
||||
{
|
||||
return ntfs_bitmap_clear_run(na, bit, 1);
|
||||
}
|
||||
|
||||
#endif /* defined _NTFS_BITMAP_H */
|
||||
|
||||
@@ -0,0 +1,297 @@
|
||||
/**
|
||||
* 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) 2005 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
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file is distributed in the hope that it will be
|
||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDIO_H
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "compat.h"
|
||||
#include "bootsect.h"
|
||||
#include "debug.h"
|
||||
#include "logging.h"
|
||||
|
||||
/**
|
||||
* ntfs_boot_sector_is_ntfs - check if buffer contains a valid ntfs boot sector
|
||||
* @b: buffer containing putative boot sector to analyze
|
||||
* @silent: if zero, output progress messages to stderr
|
||||
*
|
||||
* Check if the buffer @b contains a valid ntfs boot sector. The buffer @b
|
||||
* must be at least 512 bytes in size.
|
||||
*
|
||||
* If @silent is zero, output progress messages to stderr. Otherwise, do not
|
||||
* output any messages (except when configured with --enable-debug in which
|
||||
* case warning/debug messages may be displayed).
|
||||
*
|
||||
* Return TRUE if @b contains a valid ntfs boot sector and FALSE if not.
|
||||
*/
|
||||
BOOL ntfs_boot_sector_is_ntfs(NTFS_BOOT_SECTOR *b)
|
||||
{
|
||||
u32 i;
|
||||
BOOL ret = FALSE;
|
||||
|
||||
ntfs_log_debug("Beginning bootsector check.\n");
|
||||
|
||||
/* Calculate the checksum. Note, this is just a simple addition of
|
||||
all u32 values in the bootsector starting at the beginning and
|
||||
finishing at the offset of the checksum itself (i.e. not including
|
||||
the checksum...). */
|
||||
if ((void*)b < (void*)&b->checksum) {
|
||||
u32 *u = (u32 *)b;
|
||||
u32 *bi = (u32 *)(&b->checksum);
|
||||
|
||||
ntfs_log_debug("Calculating bootsector checksum.\n");
|
||||
|
||||
for (i = 0; u < bi; ++u)
|
||||
i += le32_to_cpup(u);
|
||||
|
||||
if (le32_to_cpu(b->checksum) && le32_to_cpu(b->checksum) != i) {
|
||||
ntfs_log_error("Bootsector checksum failed.\n");
|
||||
goto not_ntfs;
|
||||
}
|
||||
}
|
||||
|
||||
ntfs_log_debug("Checking OEMid, NTFS signature.\n");
|
||||
if (b->oem_id != cpu_to_le64(0x202020205346544eULL)) { /* "NTFS " */
|
||||
ntfs_log_error("NTFS signature is missing.\n");
|
||||
goto not_ntfs;
|
||||
}
|
||||
|
||||
ntfs_log_debug("Checking bytes per sector.\n");
|
||||
if (le16_to_cpu(b->bpb.bytes_per_sector) < 256 ||
|
||||
le16_to_cpu(b->bpb.bytes_per_sector) > 4096) {
|
||||
ntfs_log_error("Unexpected bytes per sector value (%d).\n",
|
||||
le16_to_cpu(b->bpb.bytes_per_sector));
|
||||
goto not_ntfs;
|
||||
}
|
||||
|
||||
ntfs_log_debug("Checking sectors per cluster.\n");
|
||||
switch (b->bpb.sectors_per_cluster) {
|
||||
case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128:
|
||||
break;
|
||||
default:
|
||||
ntfs_log_error("Unexpected sectors per cluster value (%d).\n",
|
||||
b->bpb.sectors_per_cluster);
|
||||
goto not_ntfs;
|
||||
}
|
||||
|
||||
ntfs_log_debug("Checking cluster size.\n");
|
||||
i = (u32)le16_to_cpu(b->bpb.bytes_per_sector) *
|
||||
b->bpb.sectors_per_cluster;
|
||||
if (i > 65536) {
|
||||
ntfs_log_error("Unexpected cluster size (%d).\n", i);
|
||||
goto not_ntfs;
|
||||
}
|
||||
|
||||
ntfs_log_debug("Checking reserved fields are zero.\n");
|
||||
if (le16_to_cpu(b->bpb.reserved_sectors) ||
|
||||
le16_to_cpu(b->bpb.root_entries) ||
|
||||
le16_to_cpu(b->bpb.sectors) ||
|
||||
le16_to_cpu(b->bpb.sectors_per_fat) ||
|
||||
le32_to_cpu(b->bpb.large_sectors) ||
|
||||
b->bpb.fats) {
|
||||
ntfs_log_error("Reserved fields aren't zero "
|
||||
"(%d, %d, %d, %d, %d, %d).\n",
|
||||
le16_to_cpu(b->bpb.reserved_sectors),
|
||||
le16_to_cpu(b->bpb.root_entries),
|
||||
le16_to_cpu(b->bpb.sectors),
|
||||
le16_to_cpu(b->bpb.sectors_per_fat),
|
||||
le32_to_cpu(b->bpb.large_sectors),
|
||||
b->bpb.fats);
|
||||
goto not_ntfs;
|
||||
}
|
||||
|
||||
ntfs_log_debug("Checking clusters per mft record.\n");
|
||||
if ((u8)b->clusters_per_mft_record < 0xe1 ||
|
||||
(u8)b->clusters_per_mft_record > 0xf7) {
|
||||
switch (b->clusters_per_mft_record) {
|
||||
case 1: case 2: case 4: case 8: case 0x10: case 0x20: case 0x40:
|
||||
break;
|
||||
default:
|
||||
ntfs_log_error("Unexpected clusters per mft record "
|
||||
"(%d).\n", b->clusters_per_mft_record);
|
||||
goto not_ntfs;
|
||||
}
|
||||
}
|
||||
|
||||
ntfs_log_debug("Checking clusters per index block.\n");
|
||||
if ((u8)b->clusters_per_index_record < 0xe1 ||
|
||||
(u8)b->clusters_per_index_record > 0xf7) {
|
||||
switch (b->clusters_per_index_record) {
|
||||
case 1: case 2: case 4: case 8: case 0x10: case 0x20: case 0x40:
|
||||
break;
|
||||
default:
|
||||
ntfs_log_error("Unexpected clusters per index record "
|
||||
"(%d).\n", b->clusters_per_index_record);
|
||||
goto not_ntfs;
|
||||
}
|
||||
}
|
||||
|
||||
if (b->end_of_sector_marker != cpu_to_le16(0xaa55))
|
||||
ntfs_log_debug("Warning: Bootsector has invalid end of sector "
|
||||
"marker.\n");
|
||||
|
||||
ntfs_log_debug("Bootsector check completed successfully.\n");
|
||||
|
||||
ret = TRUE;
|
||||
not_ntfs:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_boot_sector_parse - setup an ntfs volume from an ntfs boot sector
|
||||
* @vol: ntfs_volume to setup
|
||||
* @bs: buffer containing ntfs boot sector to parse
|
||||
*
|
||||
* Parse the ntfs bootsector @bs and setup the ntfs volume @vol with the
|
||||
* obtained values.
|
||||
*
|
||||
* Return 0 on success or -1 on error with errno set to the error code EINVAL.
|
||||
*/
|
||||
int ntfs_boot_sector_parse(ntfs_volume *vol, const NTFS_BOOT_SECTOR *bs)
|
||||
{
|
||||
s64 sectors;
|
||||
u8 sectors_per_cluster;
|
||||
s8 c;
|
||||
|
||||
/* We return -1 with errno = EINVAL on error. */
|
||||
errno = EINVAL;
|
||||
|
||||
vol->sector_size = le16_to_cpu(bs->bpb.bytes_per_sector);
|
||||
vol->sector_size_bits = ffs(vol->sector_size) - 1;
|
||||
ntfs_log_debug("SectorSize = 0x%x\n", vol->sector_size);
|
||||
ntfs_log_debug("SectorSizeBits = %u\n", vol->sector_size_bits);
|
||||
/*
|
||||
* The bounds checks on mft_lcn and mft_mirr_lcn (i.e. them being
|
||||
* below or equal the number_of_clusters) really belong in the
|
||||
* ntfs_boot_sector_is_ntfs but in this way we can just do this once.
|
||||
*/
|
||||
sectors_per_cluster = bs->bpb.sectors_per_cluster;
|
||||
ntfs_log_debug("SectorsPerCluster = 0x%x\n", sectors_per_cluster);
|
||||
if (sectors_per_cluster & (sectors_per_cluster - 1)) {
|
||||
ntfs_log_error("sectors_per_cluster (%d) is not a power of 2."
|
||||
"\n", sectors_per_cluster);
|
||||
return -1;
|
||||
}
|
||||
|
||||
sectors = sle64_to_cpu(bs->number_of_sectors);
|
||||
ntfs_log_debug("NumberOfSectors = %lld\n", sectors);
|
||||
if (!sectors) {
|
||||
ntfs_log_error("Volume size is set to zero.\n");
|
||||
return -1;
|
||||
}
|
||||
if (vol->dev->d_ops->seek(vol->dev,
|
||||
(sectors - 1) << vol->sector_size_bits,
|
||||
SEEK_SET) == -1) {
|
||||
ntfs_log_perror("Failed to read last sector (%lld)", 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" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
vol->nr_clusters = sectors >> (ffs(sectors_per_cluster) - 1);
|
||||
|
||||
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);
|
||||
if (vol->mft_lcn > vol->nr_clusters ||
|
||||
vol->mftmirr_lcn > vol->nr_clusters) {
|
||||
ntfs_log_error("$MFT LCN (%lld) or $MFTMirr LCN (%lld) is "
|
||||
"greater than the number of clusters (%lld).\n",
|
||||
vol->mft_lcn, vol->mftmirr_lcn, vol->nr_clusters);
|
||||
return -1;
|
||||
}
|
||||
|
||||
vol->cluster_size = sectors_per_cluster * vol->sector_size;
|
||||
if (vol->cluster_size & (vol->cluster_size - 1)) {
|
||||
ntfs_log_error("cluster_size (%d) is not a power of 2.\n",
|
||||
vol->cluster_size);
|
||||
return -1;
|
||||
}
|
||||
vol->cluster_size_bits = ffs(vol->cluster_size) - 1;
|
||||
/*
|
||||
* Need to get the clusters per mft record and handle it if it is
|
||||
* negative. Then calculate the mft_record_size. A value of 0x80 is
|
||||
* illegal, thus signed char is actually ok!
|
||||
*/
|
||||
c = bs->clusters_per_mft_record;
|
||||
ntfs_log_debug("ClusterSize = 0x%x\n", (unsigned)vol->cluster_size);
|
||||
ntfs_log_debug("ClusterSizeBits = %u\n", vol->cluster_size_bits);
|
||||
ntfs_log_debug("ClustersPerMftRecord = 0x%x\n", c);
|
||||
/*
|
||||
* When clusters_per_mft_record is negative, it means that it is to
|
||||
* be taken to be the negative base 2 logarithm of the mft_record_size
|
||||
* min bytes. Then:
|
||||
* mft_record_size = 2^(-clusters_per_mft_record) bytes.
|
||||
*/
|
||||
if (c < 0)
|
||||
vol->mft_record_size = 1 << -c;
|
||||
else
|
||||
vol->mft_record_size = c << vol->cluster_size_bits;
|
||||
if (vol->mft_record_size & (vol->mft_record_size - 1)) {
|
||||
ntfs_log_error("mft_record_size (%d) is not a power of 2.\n",
|
||||
vol->mft_record_size);
|
||||
return -1;
|
||||
}
|
||||
vol->mft_record_size_bits = ffs(vol->mft_record_size) - 1;
|
||||
ntfs_log_debug("MftRecordSize = 0x%x\n", (unsigned)vol->mft_record_size);
|
||||
ntfs_log_debug("MftRecordSizeBits = %u\n", vol->mft_record_size_bits);
|
||||
/* Same as above for INDX record. */
|
||||
c = bs->clusters_per_index_record;
|
||||
ntfs_log_debug("ClustersPerINDXRecord = 0x%x\n", c);
|
||||
if (c < 0)
|
||||
vol->indx_record_size = 1 << -c;
|
||||
else
|
||||
vol->indx_record_size = c << vol->cluster_size_bits;
|
||||
vol->indx_record_size_bits = ffs(vol->indx_record_size) - 1;
|
||||
ntfs_log_debug("INDXRecordSize = 0x%x\n", (unsigned)vol->indx_record_size);
|
||||
ntfs_log_debug("INDXRecordSizeBits = %u\n", vol->indx_record_size_bits);
|
||||
/*
|
||||
* Work out the size of the MFT mirror in number of mft records. If the
|
||||
* cluster size is less than or equal to the size taken by four mft
|
||||
* records, the mft mirror stores the first four mft records. If the
|
||||
* cluster size is bigger than the size taken by four mft records, the
|
||||
* mft mirror contains as many mft records as will fit into one
|
||||
* cluster.
|
||||
*/
|
||||
if (vol->cluster_size <= 4 * vol->mft_record_size)
|
||||
vol->mftmirr_size = 4;
|
||||
else
|
||||
vol->mftmirr_size = vol->cluster_size / vol->mft_record_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* bootsect.h - Exports for bootsector record handling.
|
||||
* Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2000-2002 Anton Altaparmakov
|
||||
* Copyright (c) 2006 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
|
||||
* 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_BOOTSECT_H
|
||||
#define _NTFS_BOOTSECT_H
|
||||
|
||||
#include "types.h"
|
||||
#include "volume.h"
|
||||
#include "layout.h"
|
||||
|
||||
/**
|
||||
* ntfs_boot_sector_is_ntfs - check a boot sector for describing an ntfs volume
|
||||
* @b: buffer containing the boot sector
|
||||
*
|
||||
* This function checks the boot sector in @b for describing a valid ntfs
|
||||
* volume. Return TRUE if @b is a valid NTFS boot sector or FALSE otherwise.
|
||||
*/
|
||||
extern BOOL ntfs_boot_sector_is_ntfs(NTFS_BOOT_SECTOR *b);
|
||||
extern int ntfs_boot_sector_parse(ntfs_volume *vol, const NTFS_BOOT_SECTOR *bs);
|
||||
|
||||
#endif /* defined _NTFS_BOOTSECT_H */
|
||||
|
||||
@@ -0,0 +1,221 @@
|
||||
/**
|
||||
* collate.c - NTFS collation handling. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2004 Anton Altaparmakov
|
||||
* Copyright (c) 2005 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
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file is distributed in the hope that it will be
|
||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include "collate.h"
|
||||
#include "debug.h"
|
||||
#include "unistr.h"
|
||||
#include "logging.h"
|
||||
|
||||
BOOL ntfs_is_collation_rule_supported(COLLATION_RULES cr)
|
||||
{
|
||||
int i;
|
||||
|
||||
/*
|
||||
* FIXME: At the moment we only support COLLATION_BINARY,
|
||||
* COLLATION_NTOFS_ULONG and COLLATION_FILE_NAME so we return false
|
||||
* for everything else.
|
||||
*/
|
||||
if (cr != COLLATION_BINARY && cr != COLLATION_NTOFS_ULONG &&
|
||||
cr != COLLATION_FILE_NAME)
|
||||
return FALSE;
|
||||
i = le32_to_cpu(cr);
|
||||
if (((i >= 0) && (i <= 0x02)) ||
|
||||
((i >= 0x10) && (i <= 0x13)))
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_collate_binary - Which of two binary objects should be listed first
|
||||
* @vol: unused
|
||||
* @data1:
|
||||
* @data1_len:
|
||||
* @data2:
|
||||
* @data2_len:
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
static int ntfs_collate_binary(ntfs_volume *vol __attribute__((unused)),
|
||||
const void *data1, const int data1_len,
|
||||
const void *data2, const int data2_len)
|
||||
{
|
||||
int rc;
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
rc = memcmp(data1, data2, min(data1_len, data2_len));
|
||||
if (!rc && (data1_len != data2_len)) {
|
||||
if (data1_len < data2_len)
|
||||
rc = -1;
|
||||
else
|
||||
rc = 1;
|
||||
}
|
||||
ntfs_log_trace("Done, returning %i.\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_collate_ntofs_ulong - Which of two long ints should be listed first
|
||||
* @vol: unused
|
||||
* @data1:
|
||||
* @data1_len:
|
||||
* @data2:
|
||||
* @data2_len:
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
static int ntfs_collate_ntofs_ulong(ntfs_volume *vol __attribute__((unused)),
|
||||
const void *data1, const int data1_len,
|
||||
const void *data2, const int data2_len)
|
||||
{
|
||||
int rc;
|
||||
u32 d1, d2;
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
if (data1_len != data2_len || data1_len != 4) {
|
||||
ntfs_log_error("data1_len or/and data2_len not equal to 4.\n");
|
||||
return NTFS_COLLATION_ERROR;
|
||||
}
|
||||
d1 = le32_to_cpup(data1);
|
||||
d2 = le32_to_cpup(data2);
|
||||
if (d1 < d2)
|
||||
rc = -1;
|
||||
else {
|
||||
if (d1 == d2)
|
||||
rc = 0;
|
||||
else
|
||||
rc = 1;
|
||||
}
|
||||
ntfs_log_trace("Done, returning %i.\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_collate_file_name - Which of two filenames should be listed first
|
||||
* @vol:
|
||||
* @data1:
|
||||
* @data1_len: unused
|
||||
* @data2:
|
||||
* @data2_len: unused
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
static int ntfs_collate_file_name(ntfs_volume *vol,
|
||||
const void *data1, const int data1_len __attribute__((unused)),
|
||||
const void *data2, const int data2_len __attribute__((unused)))
|
||||
{
|
||||
int rc;
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
rc = ntfs_file_values_compare(data1, data2, NTFS_COLLATION_ERROR,
|
||||
IGNORE_CASE, vol->upcase, vol->upcase_len);
|
||||
if (!rc)
|
||||
rc = ntfs_file_values_compare(data1, data2,
|
||||
NTFS_COLLATION_ERROR, CASE_SENSITIVE,
|
||||
vol->upcase, vol->upcase_len);
|
||||
ntfs_log_trace("Done, returning %i.\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
typedef int (*ntfs_collate_func_t)(ntfs_volume *, const void *, const int,
|
||||
const void *, const int);
|
||||
|
||||
static ntfs_collate_func_t ntfs_do_collate0x0[3] = {
|
||||
ntfs_collate_binary,
|
||||
ntfs_collate_file_name,
|
||||
NULL/*ntfs_collate_unicode_string*/,
|
||||
};
|
||||
|
||||
static ntfs_collate_func_t ntfs_do_collate0x1[4] = {
|
||||
ntfs_collate_ntofs_ulong,
|
||||
NULL/*ntfs_collate_ntofs_sid*/,
|
||||
NULL/*ntfs_collate_ntofs_security_hash*/,
|
||||
NULL/*ntfs_collate_ntofs_ulongs*/,
|
||||
};
|
||||
|
||||
/**
|
||||
* ntfs_collate - collate two data items using a specified collation rule
|
||||
* @vol: ntfs volume to which the data items belong
|
||||
* @cr: collation rule to use when comparing the items
|
||||
* @data1: first data item to collate
|
||||
* @data1_len: length in bytes of @data1
|
||||
* @data2: second data item to collate
|
||||
* @data2_len: length in bytes of @data2
|
||||
*
|
||||
* Collate the two data items @data1 and @data2 using the collation rule @cr
|
||||
* and return -1, 0, or 1 if @data1 is found, respectively, to collate before,
|
||||
* to match, or to collate after @data2.
|
||||
*
|
||||
* For speed we use the collation rule @cr as an index into two tables of
|
||||
* function pointers to call the appropriate collation function.
|
||||
*
|
||||
* Return NTFS_COLLATION_ERROR if error occurred.
|
||||
*/
|
||||
int ntfs_collate(ntfs_volume *vol, COLLATION_RULES cr,
|
||||
const void *data1, const int data1_len,
|
||||
const void *data2, const int data2_len)
|
||||
{
|
||||
int i;
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
if (!vol || !data1 || !data2 || data1_len < 0 || data2_len < 0) {
|
||||
ntfs_log_error("Invalid arguments passed.\n");
|
||||
return NTFS_COLLATION_ERROR;
|
||||
}
|
||||
/*
|
||||
* FIXME: At the moment we only support COLLATION_BINARY,
|
||||
* COLLATION_NTOFS_ULONG and COLLATION_FILE_NAME so we return error
|
||||
* for everything else.
|
||||
*/
|
||||
if (cr != COLLATION_BINARY && cr != COLLATION_NTOFS_ULONG &&
|
||||
cr != COLLATION_FILE_NAME)
|
||||
goto err;
|
||||
i = le32_to_cpu(cr);
|
||||
if (i < 0)
|
||||
goto err;
|
||||
if (i <= 0x02)
|
||||
return ntfs_do_collate0x0[i](vol, data1, data1_len,
|
||||
data2, data2_len);
|
||||
if (i < 0x10)
|
||||
goto err;
|
||||
i -= 0x10;
|
||||
if (i <= 3)
|
||||
return ntfs_do_collate0x1[i](vol, data1, data1_len,
|
||||
data2, data2_len);
|
||||
err:
|
||||
ntfs_log_debug("Unknown collation rule.\n");
|
||||
return NTFS_COLLATION_ERROR;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* collate.h - Defines for NTFS collation handling. Originated from the Linux-NTFS
|
||||
* project.
|
||||
*
|
||||
* Copyright (c) 2004 Anton Altaparmakov
|
||||
* Copyright (c) 2005 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
|
||||
* 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_COLLATE_H
|
||||
#define _NTFS_COLLATE_H
|
||||
|
||||
#include "types.h"
|
||||
#include "volume.h"
|
||||
|
||||
#define NTFS_COLLATION_ERROR -2
|
||||
|
||||
extern BOOL ntfs_is_collation_rule_supported(COLLATION_RULES cr);
|
||||
extern int ntfs_collate(ntfs_volume *vol, COLLATION_RULES cr,
|
||||
const void *data1, const int data1_len,
|
||||
const void *data2, const int data2_len);
|
||||
|
||||
#endif /* _NTFS_COLLATE_H */
|
||||
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* compat.c - Tweaks for Windows compatibility
|
||||
*
|
||||
* Copyright (c) 2002 Richard Russon
|
||||
* Copyright (c) 2002-2004 Anton Altaparmakov
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file is distributed in the hope that it will be
|
||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
//#ifdef WINDOWS
|
||||
|
||||
#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
|
||||
* @x:
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
int ffs(int x)
|
||||
{
|
||||
int r = 1;
|
||||
|
||||
if (!x)
|
||||
return 0;
|
||||
if (!(x & 0xffff)) {
|
||||
x >>= 16;
|
||||
r += 16;
|
||||
}
|
||||
if (!(x & 0xff)) {
|
||||
x >>= 8;
|
||||
r += 8;
|
||||
}
|
||||
if (!(x & 0xf)) {
|
||||
x >>= 4;
|
||||
r += 4;
|
||||
}
|
||||
if (!(x & 3)) {
|
||||
x >>= 2;
|
||||
r += 2;
|
||||
}
|
||||
if (!(x & 1)) {
|
||||
x >>= 1;
|
||||
r += 1;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
#endif /* HAVE_FFS */
|
||||
|
||||
//#endif /* WINDOWS */
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* compat.h - Tweaks for Windows compatibility.
|
||||
*
|
||||
* Copyright (c) 2002 Richard Russon
|
||||
* Copyright (c) 2002-2004 Anton Altaparmakov
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file is distributed in the hope that it will be
|
||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_COMPAT_H
|
||||
#define _NTFS_COMPAT_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WINDOWS
|
||||
|
||||
#ifndef HAVE_FFS
|
||||
#define HAVE_FFS
|
||||
extern int ffs(int i);
|
||||
#endif /* HAVE_FFS */
|
||||
|
||||
#define HAVE_STDIO_H /* mimic config.h */
|
||||
#define HAVE_STDARG_H
|
||||
|
||||
#define atoll _atoi64
|
||||
#define fdatasync commit
|
||||
#define __inline__ inline
|
||||
#define __attribute__(X) /*nothing*/
|
||||
|
||||
#else /* !defined WINDOWS */
|
||||
|
||||
#ifndef O_BINARY
|
||||
#define O_BINARY 0 /* unix is binary by default */
|
||||
#endif
|
||||
|
||||
#endif /* defined WINDOWS */
|
||||
|
||||
#endif /* defined _NTFS_COMPAT_H */
|
||||
|
||||
@@ -0,0 +1,552 @@
|
||||
/**
|
||||
* compress.c - Compressed attribute handling code. Originated from the Linux-NTFS
|
||||
* project.
|
||||
*
|
||||
* Copyright (c) 2004-2005 Anton Altaparmakov
|
||||
* Copyright (c) 2004-2006 Szabolcs Szakacsits
|
||||
* Copyright (c) 2005 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
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file is distributed in the hope that it will be
|
||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDIO_H
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "attrib.h"
|
||||
#include "debug.h"
|
||||
#include "volume.h"
|
||||
#include "types.h"
|
||||
#include "layout.h"
|
||||
#include "runlist.h"
|
||||
#include "compress.h"
|
||||
#include "logging.h"
|
||||
#include "misc.h"
|
||||
|
||||
/**
|
||||
* enum ntfs_compression_constants - constants used in the compression code
|
||||
*/
|
||||
typedef enum {
|
||||
/* Token types and access mask. */
|
||||
NTFS_SYMBOL_TOKEN = 0,
|
||||
NTFS_PHRASE_TOKEN = 1,
|
||||
NTFS_TOKEN_MASK = 1,
|
||||
|
||||
/* Compression sub-block constants. */
|
||||
NTFS_SB_SIZE_MASK = 0x0fff,
|
||||
NTFS_SB_SIZE = 0x1000,
|
||||
NTFS_SB_IS_COMPRESSED = 0x8000,
|
||||
} ntfs_compression_constants;
|
||||
|
||||
/**
|
||||
* ntfs_decompress - decompress a compression block into an array of pages
|
||||
* @dest: buffer to which to write the decompressed data
|
||||
* @dest_size: size of buffer @dest in bytes
|
||||
* @cb_start: compression block to decompress
|
||||
* @cb_size: size of compression block @cb_start in bytes
|
||||
*
|
||||
* This decompresses the compression block @cb_start into the destination
|
||||
* buffer @dest.
|
||||
*
|
||||
* @cb_start is a pointer to the compression block which needs decompressing
|
||||
* and @cb_size is the size of @cb_start in bytes (8-64kiB).
|
||||
*
|
||||
* Return 0 if success or -EOVERFLOW on error in the compressed stream.
|
||||
*/
|
||||
static int ntfs_decompress(u8 *dest, const u32 dest_size,
|
||||
u8 *const cb_start, const u32 cb_size)
|
||||
{
|
||||
/*
|
||||
* Pointers into the compressed data, i.e. the compression block (cb),
|
||||
* and the therein contained sub-blocks (sb).
|
||||
*/
|
||||
u8 *cb_end = cb_start + cb_size; /* End of cb. */
|
||||
u8 *cb = cb_start; /* Current position in cb. */
|
||||
u8 *cb_sb_start = cb; /* Beginning of the current sb in the cb. */
|
||||
u8 *cb_sb_end; /* End of current sb / beginning of next sb. */
|
||||
/* Variables for uncompressed data / destination. */
|
||||
u8 *dest_end = dest + dest_size; /* End of dest buffer. */
|
||||
u8 *dest_sb_start; /* Start of current sub-block in dest. */
|
||||
u8 *dest_sb_end; /* End of current sb in dest. */
|
||||
/* Variables for tag and token parsing. */
|
||||
u8 tag; /* Current tag. */
|
||||
int token; /* Loop counter for the eight tokens in tag. */
|
||||
|
||||
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);
|
||||
/*
|
||||
* 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
|
||||
* position in the compression block is one byte before its end so the
|
||||
* first two checks do not detect it.
|
||||
*/
|
||||
if (cb == cb_end || !le16_to_cpup((u16*)cb) || dest == dest_end) {
|
||||
ntfs_log_debug("Completed. Returning success (0).\n");
|
||||
return 0;
|
||||
}
|
||||
/* Setup offset for the current sub-block destination. */
|
||||
dest_sb_start = dest;
|
||||
dest_sb_end = dest + NTFS_SB_SIZE;
|
||||
/* Check that we are still within allowed boundaries. */
|
||||
if (dest_sb_end > dest_end)
|
||||
goto return_overflow;
|
||||
/* Does the minimum size of a compressed sb overflow valid range? */
|
||||
if (cb + 6 > cb_end)
|
||||
goto return_overflow;
|
||||
/* Setup the current sub-block source pointers and validate range. */
|
||||
cb_sb_start = cb;
|
||||
cb_sb_end = cb_sb_start + (le16_to_cpup((u16*)cb) & NTFS_SB_SIZE_MASK)
|
||||
+ 3;
|
||||
if (cb_sb_end > cb_end)
|
||||
goto return_overflow;
|
||||
/* Now, we are ready to process the current sub-block (sb). */
|
||||
if (!(le16_to_cpup((u16*)cb) & NTFS_SB_IS_COMPRESSED)) {
|
||||
ntfs_log_debug("Found uncompressed sub-block.\n");
|
||||
/* This sb is not compressed, just copy it into destination. */
|
||||
/* Advance source position to first data byte. */
|
||||
cb += 2;
|
||||
/* An uncompressed sb must be full size. */
|
||||
if (cb_sb_end - cb != NTFS_SB_SIZE)
|
||||
goto return_overflow;
|
||||
/* Copy the block and advance the source position. */
|
||||
memcpy(dest, cb, NTFS_SB_SIZE);
|
||||
cb += NTFS_SB_SIZE;
|
||||
/* Advance destination position to next sub-block. */
|
||||
dest += NTFS_SB_SIZE;
|
||||
goto do_next_sb;
|
||||
}
|
||||
ntfs_log_debug("Found compressed sub-block.\n");
|
||||
/* This sb is compressed, decompress it into destination. */
|
||||
/* Forward to the first tag in the sub-block. */
|
||||
cb += 2;
|
||||
do_next_tag:
|
||||
if (cb == cb_sb_end) {
|
||||
/* Check if the decompressed sub-block was not full-length. */
|
||||
if (dest < dest_sb_end) {
|
||||
int nr_bytes = dest_sb_end - dest;
|
||||
|
||||
ntfs_log_debug("Filling incomplete sub-block with zeroes.\n");
|
||||
/* Zero remainder and update destination position. */
|
||||
memset(dest, 0, nr_bytes);
|
||||
dest += nr_bytes;
|
||||
}
|
||||
/* We have finished the current sub-block. */
|
||||
goto do_next_sb;
|
||||
}
|
||||
/* Check we are still in range. */
|
||||
if (cb > cb_sb_end || dest > dest_sb_end)
|
||||
goto return_overflow;
|
||||
/* Get the next tag and advance to first token. */
|
||||
tag = *cb++;
|
||||
/* Parse the eight tokens described by the tag. */
|
||||
for (token = 0; token < 8; token++, tag >>= 1) {
|
||||
u16 lg, pt, length, max_non_overlap;
|
||||
register u16 i;
|
||||
u8 *dest_back_addr;
|
||||
|
||||
/* Check if we are done / still in range. */
|
||||
if (cb >= cb_sb_end || dest > dest_sb_end)
|
||||
break;
|
||||
/* Determine token type and parse appropriately.*/
|
||||
if ((tag & NTFS_TOKEN_MASK) == NTFS_SYMBOL_TOKEN) {
|
||||
/*
|
||||
* We have a symbol token, copy the symbol across, and
|
||||
* advance the source and destination positions.
|
||||
*/
|
||||
*dest++ = *cb++;
|
||||
/* Continue with the next token. */
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
* We have a phrase token. Make sure it is not the first tag in
|
||||
* the sb as this is illegal and would confuse the code below.
|
||||
*/
|
||||
if (dest == dest_sb_start)
|
||||
goto return_overflow;
|
||||
/*
|
||||
* Determine the number of bytes to go back (p) and the number
|
||||
* of bytes to copy (l). We use an optimized algorithm in which
|
||||
* we first calculate log2(current destination position in sb),
|
||||
* which allows determination of l and p in O(1) rather than
|
||||
* O(n). We just need an arch-optimized log2() function now.
|
||||
*/
|
||||
lg = 0;
|
||||
for (i = dest - dest_sb_start - 1; i >= 0x10; i >>= 1)
|
||||
lg++;
|
||||
/* Get the phrase token into i. */
|
||||
pt = le16_to_cpup((u16*)cb);
|
||||
/*
|
||||
* Calculate starting position of the byte sequence in
|
||||
* the destination using the fact that p = (pt >> (12 - lg)) + 1
|
||||
* and make sure we don't go too far back.
|
||||
*/
|
||||
dest_back_addr = dest - (pt >> (12 - lg)) - 1;
|
||||
if (dest_back_addr < dest_sb_start)
|
||||
goto return_overflow;
|
||||
/* Now calculate the length of the byte sequence. */
|
||||
length = (pt & (0xfff >> lg)) + 3;
|
||||
/* Verify destination is in range. */
|
||||
if (dest + length > dest_sb_end)
|
||||
goto return_overflow;
|
||||
/* The number of non-overlapping bytes. */
|
||||
max_non_overlap = dest - dest_back_addr;
|
||||
if (length <= max_non_overlap) {
|
||||
/* The byte sequence doesn't overlap, just copy it. */
|
||||
memcpy(dest, dest_back_addr, length);
|
||||
/* Advance destination pointer. */
|
||||
dest += length;
|
||||
} else {
|
||||
/*
|
||||
* The byte sequence does overlap, copy non-overlapping
|
||||
* part and then do a slow byte by byte copy for the
|
||||
* overlapping part. Also, advance the destination
|
||||
* pointer.
|
||||
*/
|
||||
memcpy(dest, dest_back_addr, max_non_overlap);
|
||||
dest += max_non_overlap;
|
||||
dest_back_addr += max_non_overlap;
|
||||
length -= max_non_overlap;
|
||||
while (length--)
|
||||
*dest++ = *dest_back_addr++;
|
||||
}
|
||||
/* Advance source position and continue with the next token. */
|
||||
cb += 2;
|
||||
}
|
||||
/* No tokens left in the current tag. Continue with the next tag. */
|
||||
goto do_next_tag;
|
||||
return_overflow:
|
||||
errno = EOVERFLOW;
|
||||
ntfs_log_perror("Failed to decompress file");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_is_cb_compressed - internal function, do not use
|
||||
*
|
||||
* This is a very specialised function determining if a cb is compressed or
|
||||
* uncompressed. It is assumed that checking for a sparse cb has already been
|
||||
* performed and that the cb is not sparse. It makes all sorts of other
|
||||
* assumptions as well and hence it is not useful anywhere other than where it
|
||||
* is used at the moment. Please, do not make this function available for use
|
||||
* outside of compress.c as it is bound to confuse people and not do what they
|
||||
* want.
|
||||
*
|
||||
* Return TRUE on errors so that the error will be detected later on in the
|
||||
* code. Might be a bit confusing to debug but there really should never be
|
||||
* errors coming from here.
|
||||
*/
|
||||
static BOOL ntfs_is_cb_compressed(ntfs_attr *na, runlist_element *rl,
|
||||
VCN cb_start_vcn, int cb_clusters)
|
||||
{
|
||||
/*
|
||||
* The simplest case: the run starting at @cb_start_vcn contains
|
||||
* @cb_clusters clusters which are all not sparse, thus the cb is not
|
||||
* compressed.
|
||||
*/
|
||||
restart:
|
||||
cb_clusters -= rl->length - (cb_start_vcn - rl->vcn);
|
||||
while (cb_clusters > 0) {
|
||||
/* Go to the next run. */
|
||||
rl++;
|
||||
/* Map the next runlist fragment if it is not mapped. */
|
||||
if (rl->lcn < LCN_HOLE || !rl->length) {
|
||||
cb_start_vcn = rl->vcn;
|
||||
rl = ntfs_attr_find_vcn(na, rl->vcn);
|
||||
if (!rl || rl->lcn < LCN_HOLE || !rl->length)
|
||||
return TRUE;
|
||||
/*
|
||||
* If the runs were merged need to deal with the
|
||||
* resulting partial run so simply restart.
|
||||
*/
|
||||
if (rl->vcn < cb_start_vcn)
|
||||
goto restart;
|
||||
}
|
||||
/* If the current run is sparse, the cb is compressed. */
|
||||
if (rl->lcn == LCN_HOLE)
|
||||
return TRUE;
|
||||
/* If the whole cb is not sparse, it is not compressed. */
|
||||
if (rl->length >= cb_clusters)
|
||||
return FALSE;
|
||||
cb_clusters -= rl->length;
|
||||
};
|
||||
/* All cb_clusters were not sparse thus the cb is not compressed. */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_compressed_attr_pread - read from a compressed attribute
|
||||
* @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
|
||||
*
|
||||
* NOTE: You probably want to be using attrib.c::ntfs_attr_pread() instead.
|
||||
*
|
||||
* This function will read @count bytes starting at offset @pos from the
|
||||
* compressed 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_compressed_attr_pread(ntfs_attr *na, s64 pos, s64 count, void *b)
|
||||
{
|
||||
s64 br, to_read, ofs, total, total2;
|
||||
u64 cb_size_mask;
|
||||
VCN start_vcn, vcn, end_vcn;
|
||||
ntfs_volume *vol;
|
||||
runlist_element *rl;
|
||||
u8 *dest, *cb, *cb_pos, *cb_end;
|
||||
u32 cb_size;
|
||||
int err;
|
||||
unsigned int nr_cbs, cb_clusters;
|
||||
|
||||
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 || !NAttrCompressed(na) || !na->ni || !na->ni->vol || !b ||
|
||||
pos < 0 || count < 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
/*
|
||||
* Encrypted attributes are not supported. We return access denied,
|
||||
* which is what Windows NT4 does, too.
|
||||
*/
|
||||
if (NAttrEncrypted(na)) {
|
||||
errno = EACCES;
|
||||
return -1;
|
||||
}
|
||||
if (!count)
|
||||
return 0;
|
||||
/* Truncate reads beyond end of attribute. */
|
||||
if (pos + count > na->data_size) {
|
||||
if (pos >= na->data_size) {
|
||||
return 0;
|
||||
}
|
||||
count = na->data_size - pos;
|
||||
}
|
||||
/* If it is a resident attribute, simply use ntfs_attr_pread(). */
|
||||
if (!NAttrNonResident(na))
|
||||
return ntfs_attr_pread(na, pos, count, b);
|
||||
total = total2 = 0;
|
||||
/* Zero out reads beyond initialized size. */
|
||||
if (pos + count > na->initialized_size) {
|
||||
if (pos >= na->initialized_size) {
|
||||
memset(b, 0, count);
|
||||
return count;
|
||||
}
|
||||
total2 = pos + count - na->initialized_size;
|
||||
count -= total2;
|
||||
memset((u8*)b + count, 0, total2);
|
||||
}
|
||||
vol = na->ni->vol;
|
||||
cb_size = na->compression_block_size;
|
||||
cb_size_mask = cb_size - 1UL;
|
||||
cb_clusters = na->compression_block_clusters;
|
||||
|
||||
/* Need a temporary buffer for each loaded compression block. */
|
||||
cb = ntfs_malloc(cb_size);
|
||||
if (!cb)
|
||||
return -1;
|
||||
|
||||
/* Need a temporary buffer for each uncompressed block. */
|
||||
dest = ntfs_malloc(cb_size);
|
||||
if (!dest) {
|
||||
free(cb);
|
||||
return -1;
|
||||
}
|
||||
/*
|
||||
* The first vcn in the first compression block (cb) which we need to
|
||||
* decompress.
|
||||
*/
|
||||
start_vcn = (pos & ~cb_size_mask) >> vol->cluster_size_bits;
|
||||
/* Offset in the uncompressed cb at which to start reading data. */
|
||||
ofs = pos & cb_size_mask;
|
||||
/*
|
||||
* The first vcn in the cb after the last cb which we need to
|
||||
* decompress.
|
||||
*/
|
||||
end_vcn = ((pos + count + cb_size - 1) & ~cb_size_mask) >>
|
||||
vol->cluster_size_bits;
|
||||
/* Number of compression blocks (cbs) in the wanted vcn range. */
|
||||
nr_cbs = (end_vcn - start_vcn) << vol->cluster_size_bits >>
|
||||
na->compression_block_size_bits;
|
||||
cb_end = cb + cb_size;
|
||||
do_next_cb:
|
||||
nr_cbs--;
|
||||
cb_pos = cb;
|
||||
vcn = start_vcn;
|
||||
start_vcn += cb_clusters;
|
||||
|
||||
/* Check whether the compression block is sparse. */
|
||||
rl = ntfs_attr_find_vcn(na, vcn);
|
||||
if (!rl || rl->lcn < LCN_HOLE) {
|
||||
free(cb);
|
||||
free(dest);
|
||||
if (total)
|
||||
return total;
|
||||
/* FIXME: Do we want EIO or the error code? (AIA) */
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
if (rl->lcn == LCN_HOLE) {
|
||||
/* Sparse cb, zero out destination range overlapping the cb. */
|
||||
ntfs_log_debug("Found sparse compression block.\n");
|
||||
to_read = min(count, cb_size - ofs);
|
||||
memset(b, 0, to_read);
|
||||
ofs = 0;
|
||||
total += to_read;
|
||||
count -= to_read;
|
||||
b = (u8*)b + to_read;
|
||||
} else if (!ntfs_is_cb_compressed(na, rl, vcn, cb_clusters)) {
|
||||
s64 tdata_size, tinitialized_size;
|
||||
/*
|
||||
* Uncompressed cb, read it straight into the destination range
|
||||
* overlapping the cb.
|
||||
*/
|
||||
ntfs_log_debug("Found uncompressed compression block.\n");
|
||||
/*
|
||||
* Read the uncompressed data into the destination buffer.
|
||||
* NOTE: We cheat a little bit here by marking the attribute as
|
||||
* not compressed in the ntfs_attr structure so that we can
|
||||
* read the data by simply using ntfs_attr_pread(). (-8
|
||||
* NOTE: we have to modify data_size and initialized_size
|
||||
* temporarily as well...
|
||||
*/
|
||||
to_read = min(count, cb_size - ofs);
|
||||
ofs += vcn << vol->cluster_size_bits;
|
||||
NAttrClearCompressed(na);
|
||||
tdata_size = na->data_size;
|
||||
tinitialized_size = na->initialized_size;
|
||||
na->data_size = na->initialized_size = na->allocated_size;
|
||||
do {
|
||||
br = ntfs_attr_pread(na, ofs, to_read, b);
|
||||
if (br < 0) {
|
||||
err = errno;
|
||||
na->data_size = tdata_size;
|
||||
na->initialized_size = tinitialized_size;
|
||||
NAttrSetCompressed(na);
|
||||
free(cb);
|
||||
free(dest);
|
||||
if (total)
|
||||
return total;
|
||||
errno = err;
|
||||
return br;
|
||||
}
|
||||
total += br;
|
||||
count -= br;
|
||||
b = (u8*)b + br;
|
||||
to_read -= br;
|
||||
ofs += br;
|
||||
} while (to_read > 0);
|
||||
na->data_size = tdata_size;
|
||||
na->initialized_size = tinitialized_size;
|
||||
NAttrSetCompressed(na);
|
||||
ofs = 0;
|
||||
} else {
|
||||
s64 tdata_size, tinitialized_size;
|
||||
|
||||
/*
|
||||
* Compressed cb, decompress it into the temporary buffer, then
|
||||
* copy the data to the destination range overlapping the cb.
|
||||
*/
|
||||
ntfs_log_debug("Found compressed compression block.\n");
|
||||
/*
|
||||
* Read the compressed data into the temporary buffer.
|
||||
* NOTE: We cheat a little bit here by marking the attribute as
|
||||
* not compressed in the ntfs_attr structure so that we can
|
||||
* read the raw, compressed data by simply using
|
||||
* ntfs_attr_pread(). (-8
|
||||
* NOTE: We have to modify data_size and initialized_size
|
||||
* temporarily as well...
|
||||
*/
|
||||
to_read = cb_size;
|
||||
NAttrClearCompressed(na);
|
||||
tdata_size = na->data_size;
|
||||
tinitialized_size = na->initialized_size;
|
||||
na->data_size = na->initialized_size = na->allocated_size;
|
||||
do {
|
||||
br = ntfs_attr_pread(na,
|
||||
(vcn << vol->cluster_size_bits) +
|
||||
(cb_pos - cb), to_read, cb_pos);
|
||||
if (br < 0) {
|
||||
err = errno;
|
||||
na->data_size = tdata_size;
|
||||
na->initialized_size = tinitialized_size;
|
||||
NAttrSetCompressed(na);
|
||||
free(cb);
|
||||
free(dest);
|
||||
if (total)
|
||||
return total;
|
||||
errno = err;
|
||||
return br;
|
||||
}
|
||||
cb_pos += br;
|
||||
to_read -= br;
|
||||
} while (to_read > 0);
|
||||
na->data_size = tdata_size;
|
||||
na->initialized_size = tinitialized_size;
|
||||
NAttrSetCompressed(na);
|
||||
/* Just a precaution. */
|
||||
if (cb_pos + 2 <= cb_end)
|
||||
*(u16*)cb_pos = 0;
|
||||
ntfs_log_debug("Successfully read the compression block.\n");
|
||||
if (ntfs_decompress(dest, cb_size, cb, cb_size) < 0) {
|
||||
err = errno;
|
||||
free(cb);
|
||||
free(dest);
|
||||
if (total)
|
||||
return total;
|
||||
errno = err;
|
||||
return -1;
|
||||
}
|
||||
to_read = min(count, cb_size - ofs);
|
||||
memcpy(b, dest + ofs, to_read);
|
||||
total += to_read;
|
||||
count -= to_read;
|
||||
b = (u8*)b + to_read;
|
||||
ofs = 0;
|
||||
}
|
||||
/* Do we have more work to do? */
|
||||
if (nr_cbs)
|
||||
goto do_next_cb;
|
||||
/* We no longer need the buffers. */
|
||||
free(cb);
|
||||
free(dest);
|
||||
/* Return number of bytes read. */
|
||||
return total + total2;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* compress.h - Exports for compressed attribute handling.
|
||||
* Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2004 Anton Altaparmakov
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file is distributed in the hope that it will be
|
||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_COMPRESS_H
|
||||
#define _NTFS_COMPRESS_H
|
||||
|
||||
#include "types.h"
|
||||
#include "attrib.h"
|
||||
|
||||
extern s64 ntfs_compressed_attr_pread(ntfs_attr *na, s64 pos, s64 count,
|
||||
void *b);
|
||||
|
||||
#endif /* defined _NTFS_COMPRESS_H */
|
||||
|
||||
@@ -0,0 +1,317 @@
|
||||
/* config.h.in. Generated from configure.ac by autoheader. */
|
||||
|
||||
/* Define to 1 if you have the `atexit' function. */
|
||||
#undef HAVE_ATEXIT
|
||||
|
||||
/* Define to 1 if you have the `basename' function. */
|
||||
#undef HAVE_BASENAME
|
||||
|
||||
/* Define to 1 if you have the <byteswap.h> header file. */
|
||||
#undef HAVE_BYTESWAP_H
|
||||
|
||||
/* Define to 1 if you have the <ctype.h> header file. */
|
||||
#define HAVE_CTYPE_H 1
|
||||
|
||||
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||
#undef HAVE_DLFCN_H
|
||||
|
||||
/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */
|
||||
#undef HAVE_DOPRNT
|
||||
|
||||
/* Define to 1 if you have the `dup2' function. */
|
||||
#undef HAVE_DUP2
|
||||
|
||||
/* Define to 1 if you have the <endian.h> header file. */
|
||||
#define HAVE_ENDIAN_H 1
|
||||
|
||||
/* Define to 1 if you have the <errno.h> header file. */
|
||||
#define HAVE_ERRNO_H 1
|
||||
|
||||
/* Define to 1 if you have the <fcntl.h> header file. */
|
||||
#define HAVE_FCNTL_H 1
|
||||
|
||||
/* Define to 1 if you have the `fdatasync' function. */
|
||||
#undef HAVE_FDATASYNC
|
||||
|
||||
/* Define to 1 if you have the <features.h> header file. */
|
||||
#undef HAVE_FEATURES_H
|
||||
|
||||
/* Define to 1 if you have the <gcrypt.h> header file. */
|
||||
#undef HAVE_GCRYPT_H
|
||||
|
||||
/* Define to 1 if you have the `getmntent' function. */
|
||||
#undef HAVE_GETMNTENT
|
||||
|
||||
/* Define to 1 if you have the <getopt.h> header file. */
|
||||
#undef HAVE_GETOPT_H
|
||||
|
||||
/* Define to 1 if you have the `getopt_long' function. */
|
||||
#undef HAVE_GETOPT_LONG
|
||||
|
||||
/* Define to 1 if you have the <gnutls/pkcs12.h> header file. */
|
||||
#undef HAVE_GNUTLS_PKCS12_H
|
||||
|
||||
/* Define to 1 if you have the `hasmntopt' function. */
|
||||
#undef HAVE_HASMNTOPT
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#undef HAVE_INTTYPES_H
|
||||
|
||||
/* Define to 1 if you have the <libgen.h> header file. */
|
||||
#undef HAVE_LIBGEN_H
|
||||
|
||||
/* Define to 1 if you have the <libintl.h> header file. */
|
||||
#undef HAVE_LIBINTL_H
|
||||
|
||||
/* Define to 1 if you have the <limits.h> header file. */
|
||||
#undef HAVE_LIMITS_H
|
||||
|
||||
/* Define to 1 if you have the <linux/fd.h> header file. */
|
||||
#undef HAVE_LINUX_FD_H
|
||||
|
||||
/* Define to 1 if you have the <linux/hdreg.h> header file. */
|
||||
#undef HAVE_LINUX_HDREG_H
|
||||
|
||||
/* Define to 1 if you have the <linux/major.h> header file. */
|
||||
#undef HAVE_LINUX_MAJOR_H
|
||||
|
||||
/* Define to 1 if you have the <locale.h> header file. */
|
||||
#define HAVE_LOCALE_H 1
|
||||
|
||||
/* Define to 1 if you have the <machine/endian.h> header file. */
|
||||
#undef HAVE_MACHINE_ENDIAN_H
|
||||
|
||||
/* Define to 1 if mbrtowc and mbstate_t are properly declared. */
|
||||
#undef HAVE_MBRTOWC
|
||||
#define HAVE_MBRTOWC 1
|
||||
|
||||
/* Define to 1 if you have the `mbsinit' function. */
|
||||
#undef HAVE_MBSINIT
|
||||
//#define HAVE_MBSINIT 1
|
||||
|
||||
/* Define to 1 if you have the `memmove' function. */
|
||||
#undef HAVE_MEMMOVE
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#undef HAVE_MEMORY_H
|
||||
|
||||
/* Define to 1 if you have the `memset' function. */
|
||||
#undef HAVE_MEMSET
|
||||
|
||||
/* Define to 1 if you have the <mntent.h> header file. */
|
||||
#undef HAVE_MNTENT_H
|
||||
|
||||
/* Define to 1 if you have the `realpath' function. */
|
||||
#undef HAVE_REALPATH
|
||||
|
||||
/* Define to 1 if you have the `regcomp' function. */
|
||||
#undef HAVE_REGCOMP
|
||||
|
||||
/* Define to 1 if you have the `setlocale' function. */
|
||||
#undef HAVE_SETLOCALE
|
||||
|
||||
/* Define to 1 if you have the `setxattr' function. */
|
||||
#undef HAVE_SETXATTR
|
||||
|
||||
/* Define to 1 if `stat' has the bug that it succeeds when given the
|
||||
zero-length file name argument. */
|
||||
#undef HAVE_STAT_EMPTY_STRING_BUG
|
||||
|
||||
/* Define to 1 if you have the <stdarg.h> header file. */
|
||||
#define HAVE_STDARG_H 1
|
||||
|
||||
/* Define to 1 if stdbool.h conforms to C99. */
|
||||
#undef HAVE_STDBOOL_H
|
||||
|
||||
/* Define to 1 if you have the <stddef.h> header file. */
|
||||
#undef HAVE_STDDEF_H
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#define HAVE_STDINT_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdio.h> header file. */
|
||||
#define HAVE_STDIO_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#define HAVE_STDLIB_H 1
|
||||
|
||||
/* Define to 1 if you have the `strcasecmp' function. */
|
||||
#undef HAVE_STRCASECMP
|
||||
|
||||
/* Define to 1 if you have the `strchr' function. */
|
||||
#undef HAVE_STRCHR
|
||||
|
||||
/* Define to 1 if you have the `strdup' function. */
|
||||
#undef HAVE_STRDUP
|
||||
|
||||
/* Define to 1 if you have the `strerror' function. */
|
||||
#undef HAVE_STRERROR
|
||||
|
||||
/* Define to 1 if you have the `strftime' function. */
|
||||
#undef HAVE_STRFTIME
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
#undef HAVE_STRINGS_H
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#define HAVE_STRING_H 1
|
||||
|
||||
/* Define to 1 if you have the `strnlen' function. */
|
||||
#undef HAVE_STRNLEN
|
||||
|
||||
/* Define to 1 if you have the `strtol' function. */
|
||||
#undef HAVE_STRTOL
|
||||
|
||||
/* Define to 1 if you have the `strtoul' function. */
|
||||
#undef HAVE_STRTOUL
|
||||
|
||||
/* Define to 1 if `st_blocks' is member of `struct stat'. */
|
||||
#undef HAVE_STRUCT_STAT_ST_BLOCKS
|
||||
|
||||
/* Define to 1 if `st_rdev' is member of `struct stat'. */
|
||||
#undef HAVE_STRUCT_STAT_ST_RDEV
|
||||
|
||||
/* Define to 1 if your `struct stat' has `st_blocks'. Deprecated, use
|
||||
`HAVE_STRUCT_STAT_ST_BLOCKS' instead. */
|
||||
#undef HAVE_ST_BLOCKS
|
||||
|
||||
/* Define to 1 if you have the `sysconf' function. */
|
||||
#undef HAVE_SYSCONF
|
||||
|
||||
/* Define to 1 if you have the <syslog.h> header file. */
|
||||
#undef HAVE_SYSLOG_H
|
||||
|
||||
/* Define to 1 if you have the <sys/byteorder.h> header file. */
|
||||
#undef HAVE_SYS_BYTEORDER_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/endian.h> header file. */
|
||||
#undef HAVE_SYS_ENDIAN_H
|
||||
|
||||
/* Define to 1 if you have the <sys/ioctl.h> header file. */
|
||||
#undef HAVE_SYS_IOCTL_H
|
||||
|
||||
/* Define to 1 if you have the <sys/mount.h> header file. */
|
||||
#undef HAVE_SYS_MOUNT_H
|
||||
|
||||
/* Define to 1 if you have the <sys/param.h> header file. */
|
||||
#undef HAVE_SYS_PARAM_H
|
||||
|
||||
/* Define to 1 if you have the <sys/statvfs.h> header file. */
|
||||
#undef HAVE_SYS_STATVFS_H
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#define HAVE_SYS_STAT_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/sysmacros.h> header file. */
|
||||
#undef HAVE_SYS_SYSMACROS_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#define HAVE_SYS_TYPES_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/vfs.h> header file. */
|
||||
#undef HAVE_SYS_VFS_H
|
||||
|
||||
/* Define to 1 if you have the <time.h> header file. */
|
||||
#undef HAVE_TIME_H
|
||||
#define HAVE_TIME_H 1
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#undef HAVE_UNISTD_H
|
||||
#define HAVE_UNISTD_H 1
|
||||
|
||||
/* Define to 1 if you have the `utime' function. */
|
||||
#undef HAVE_UTIME
|
||||
|
||||
/* Define to 1 if you have the <utime.h> header file. */
|
||||
#undef HAVE_UTIME_H
|
||||
|
||||
/* Define to 1 if `utime(file, NULL)' sets file's timestamp to the present. */
|
||||
#undef HAVE_UTIME_NULL
|
||||
|
||||
/* Define to 1 if you have the `vprintf' function. */
|
||||
#undef HAVE_VPRINTF
|
||||
#define HAVE_VPRINTF 1
|
||||
|
||||
/* Define to 1 if you have the <wchar.h> header file. */
|
||||
#undef HAVE_WCHAR_H
|
||||
#define HAVE_WCHAR_H 1
|
||||
|
||||
/* Define to 1 if you have the <windows.h> header file. */
|
||||
#undef HAVE_WINDOWS_H
|
||||
|
||||
/* Define to 1 if the system has the type `_Bool'. */
|
||||
#undef HAVE__BOOL
|
||||
|
||||
/* Define to 1 if `lstat' dereferences a symlink specified with a trailing
|
||||
slash. */
|
||||
#undef LSTAT_FOLLOWS_SLASHED_SYMLINK
|
||||
|
||||
/* Name of package */
|
||||
#undef PACKAGE
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#undef PACKAGE_BUGREPORT
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#undef PACKAGE_NAME
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#undef PACKAGE_STRING
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#undef PACKAGE_TARNAME
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#undef PACKAGE_VERSION
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#undef STDC_HEADERS
|
||||
|
||||
/* Version number of package */
|
||||
#undef VERSION
|
||||
|
||||
/* Define to 1 if your processor stores words with the most significant byte
|
||||
first (like Motorola and SPARC, unlike Intel and VAX). */
|
||||
#undef WORDS_BIGENDIAN
|
||||
|
||||
/* Define to 1 if your processor stores words with the least significant byte
|
||||
first (like Intel and VAX, unlike Motorola and SPARC). */
|
||||
#undef WORDS_LITTLEENDIAN
|
||||
|
||||
/* Number of bits in a file offset, on hosts where this is settable. */
|
||||
#undef _FILE_OFFSET_BITS
|
||||
|
||||
/* Enable GNU extensions on systems that have them. */
|
||||
#ifndef _GNU_SOURCE
|
||||
# undef _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
/* Define for large files, on AIX-style hosts. */
|
||||
#undef _LARGE_FILES
|
||||
|
||||
/* Define to empty if `const' does not conform to ANSI C. */
|
||||
#undef const
|
||||
|
||||
/* Define to `__inline__' or `__inline' if that's what the C compiler
|
||||
calls it, or to nothing if 'inline' is not supported under any name. */
|
||||
#ifndef __cplusplus
|
||||
#undef inline
|
||||
#endif
|
||||
|
||||
/* Define to `long' if <sys/types.h> does not define. */
|
||||
#undef off_t
|
||||
|
||||
/* Define to `unsigned' if <sys/types.h> does not define. */
|
||||
#undef size_t
|
||||
|
||||
#define LOGGING_DISABLE 1
|
||||
|
||||
/* major part of a device */
|
||||
#define major(x) (int)((unsigned)((x)>>8)&0x7F)
|
||||
#define bmajor(x) (int)((unsigned)((x)>>8)&0x7F)
|
||||
|
||||
/* minor part of a device */
|
||||
#define minor(x) (int)((x)&0xFF)
|
||||
|
||||
/* make a device number */
|
||||
#define makedev(x,y) (dev_t)(((x)<<8) | (y))
|
||||
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* debug.c - Debugging output functions. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2002-2004 Anton Altaparmakov
|
||||
* Copyright (c) 2004-2006 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
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file is distributed in the hope that it will be
|
||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
#include "runlist.h"
|
||||
#include "debug.h"
|
||||
#include "logging.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
/**
|
||||
* ntfs_debug_runlist_dump - Dump a runlist.
|
||||
* @rl:
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
void ntfs_debug_runlist_dump(const runlist_element *rl)
|
||||
{
|
||||
int i = 0;
|
||||
const char *lcn_str[5] = { "LCN_HOLE ", "LCN_RL_NOT_MAPPED",
|
||||
"LCN_ENOENT ", "LCN_EINVAL ",
|
||||
"LCN_unknown " };
|
||||
|
||||
ntfs_log_debug("NTFS-fs DEBUG: Dumping runlist (values in hex):\n");
|
||||
if (!rl) {
|
||||
ntfs_log_debug("Run list not present.\n");
|
||||
return;
|
||||
}
|
||||
ntfs_log_debug("VCN LCN Run length\n");
|
||||
do {
|
||||
LCN lcn = (rl + i)->lcn;
|
||||
|
||||
if (lcn < (LCN)0) {
|
||||
int idx = -lcn - 1;
|
||||
|
||||
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)");
|
||||
} else
|
||||
ntfs_log_debug("%-16llx %-16llx %-16llx%s\n", rl[i].vcn, rl[i].lcn, rl[i].length, rl[i].length ? "" : " (runlist end)");
|
||||
} while (rl[i++].length);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* debug.h - Debugging output functions. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2002-2004 Anton Altaparmakov
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file is distributed in the hope that it will be
|
||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_DEBUG_H
|
||||
#define _NTFS_DEBUG_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "logging.h"
|
||||
|
||||
struct _runlist_element;
|
||||
|
||||
#ifdef DEBUG
|
||||
extern void ntfs_debug_runlist_dump(const struct _runlist_element *rl);
|
||||
#else
|
||||
static __inline__ void ntfs_debug_runlist_dump(const struct _runlist_element *rl __attribute__((unused))) {}
|
||||
#endif
|
||||
|
||||
#define NTFS_BUG(msg) \
|
||||
{ \
|
||||
int ___i; \
|
||||
ntfs_log_critical("Bug in %s(): %s\n", __FUNCTION__, msg); \
|
||||
ntfs_log_debug("Forcing segmentation fault!"); \
|
||||
___i = ((int*)NULL)[1]; \
|
||||
}
|
||||
|
||||
#endif /* defined _NTFS_DEBUG_H */
|
||||
@@ -0,0 +1,727 @@
|
||||
/**
|
||||
* device.c - Low level device io functions. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2004-2006 Anton Altaparmakov
|
||||
* Copyright (c) 2004-2006 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
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file is distributed in the hope that it will be
|
||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDIO_H
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_IOCTL_H
|
||||
#include <sys/ioctl.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_PARAM_H
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_MOUNT_H
|
||||
#include <sys/mount.h>
|
||||
#endif
|
||||
#ifdef HAVE_LINUX_FD_H
|
||||
#include <linux/fd.h>
|
||||
#endif
|
||||
#ifdef HAVE_LINUX_HDREG_H
|
||||
#include <linux/hdreg.h>
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
#include "mst.h"
|
||||
#include "debug.h"
|
||||
#include "device.h"
|
||||
#include "logging.h"
|
||||
#include "misc.h"
|
||||
|
||||
#if defined(linux) && defined(_IO) && !defined(BLKGETSIZE)
|
||||
#define BLKGETSIZE _IO(0x12,96) /* Get device size in 512-byte blocks. */
|
||||
#endif
|
||||
#if defined(linux) && defined(_IOR) && !defined(BLKGETSIZE64)
|
||||
#define BLKGETSIZE64 _IOR(0x12,114,size_t) /* Get device size in bytes. */
|
||||
#endif
|
||||
#if defined(linux) && !defined(HDIO_GETGEO)
|
||||
#define HDIO_GETGEO 0x0301 /* Get device geometry. */
|
||||
#endif
|
||||
#if defined(linux) && defined(_IO) && !defined(BLKSSZGET)
|
||||
# define BLKSSZGET _IO(0x12,104) /* Get device sector size in bytes. */
|
||||
#endif
|
||||
#if defined(linux) && defined(_IO) && !defined(BLKBSZSET)
|
||||
# define BLKBSZSET _IOW(0x12,113,size_t) /* Set device block size in bytes. */
|
||||
#endif
|
||||
|
||||
/**
|
||||
* ntfs_device_alloc - allocate an ntfs device structure and pre-initialize it
|
||||
* @name: name of the device (must be present)
|
||||
* @state: initial device state (usually zero)
|
||||
* @dops: ntfs device operations to use with the device (must be present)
|
||||
* @priv_data: pointer to private data (optional)
|
||||
*
|
||||
* Allocate an ntfs device structure and pre-initialize it with the user-
|
||||
* specified device operations @dops, device state @state, device name @name,
|
||||
* and optional private data @priv_data.
|
||||
*
|
||||
* Note, @name is copied and can hence be freed after this functions returns.
|
||||
*
|
||||
* On success return a pointer to the allocated ntfs device structure and on
|
||||
* error return NULL with errno set to the error code returned by ntfs_malloc().
|
||||
*/
|
||||
struct ntfs_device *ntfs_device_alloc(const char *name, const long state,
|
||||
struct ntfs_device_operations *dops, void *priv_data)
|
||||
{
|
||||
struct ntfs_device *dev;
|
||||
|
||||
if (!name) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dev = ntfs_malloc(sizeof(struct ntfs_device));
|
||||
if (dev) {
|
||||
if (!(dev->d_name = strdup(name))) {
|
||||
int eo = errno;
|
||||
free(dev);
|
||||
errno = eo;
|
||||
return NULL;
|
||||
}
|
||||
dev->d_ops = dops;
|
||||
dev->d_state = state;
|
||||
dev->d_private = priv_data;
|
||||
}
|
||||
return dev;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_free - free an ntfs device structure
|
||||
* @dev: ntfs device structure to free
|
||||
*
|
||||
* Free the ntfs device structure @dev.
|
||||
*
|
||||
* Return 0 on success or -1 on error with errno set to the error code. The
|
||||
* following error codes are defined:
|
||||
* EINVAL Invalid pointer @dev.
|
||||
* EBUSY Device is still open. Close it before freeing it!
|
||||
*/
|
||||
int ntfs_device_free(struct ntfs_device *dev)
|
||||
{
|
||||
if (!dev) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (NDevOpen(dev)) {
|
||||
errno = EBUSY;
|
||||
return -1;
|
||||
}
|
||||
free(dev->d_name);
|
||||
free(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_pread - positioned read from disk
|
||||
* @dev: device to read from
|
||||
* @pos: position in device to read from
|
||||
* @count: number of bytes to read
|
||||
* @b: output data buffer
|
||||
*
|
||||
* This function will read @count bytes from device @dev at position @pos into
|
||||
* the data buffer @b.
|
||||
*
|
||||
* On success, return the number of successfully read bytes. If this number is
|
||||
* lower than @count this means that we have either reached end of file or
|
||||
* encountered an error during the read so that the read is partial. 0 means
|
||||
* end of file or nothing to read (@count is 0).
|
||||
*
|
||||
* On error and nothing has been read, return -1 with errno set appropriately
|
||||
* to the return code of either seek, read, or set to EINVAL in case of
|
||||
* invalid arguments.
|
||||
*/
|
||||
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);
|
||||
|
||||
if (!b || count < 0 || pos < 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (!count)
|
||||
return 0;
|
||||
|
||||
dops = dev->d_ops;
|
||||
|
||||
for (total = 0; count; count -= br, total += br) {
|
||||
br = dops->pread(dev, (char*)b + total, count, pos + total);
|
||||
/* If everything ok, continue. */
|
||||
if (br > 0)
|
||||
continue;
|
||||
/* If EOF or error return number of bytes read. */
|
||||
if (!br || total)
|
||||
return total;
|
||||
/* Nothing read and error, return error status. */
|
||||
return br;
|
||||
}
|
||||
/* Finally, return the number of bytes read. */
|
||||
return total;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_pwrite - positioned write to disk
|
||||
* @dev: device to write to
|
||||
* @pos: position in file descriptor to write to
|
||||
* @count: number of bytes to write
|
||||
* @b: data buffer to write to disk
|
||||
*
|
||||
* This function will write @count bytes from data buffer @b to the device @dev
|
||||
* at position @pos.
|
||||
*
|
||||
* On success, return the number of successfully written bytes. If this number
|
||||
* is lower than @count this means that the write has been interrupted in
|
||||
* flight or that an error was encountered during the write so that the write
|
||||
* is partial. 0 means nothing was written (also return 0 when @count is 0).
|
||||
*
|
||||
* On error and nothing has been written, return -1 with errno set
|
||||
* appropriately to the return code of either seek, write, or set
|
||||
* to EINVAL in case of invalid arguments.
|
||||
*/
|
||||
s64 ntfs_pwrite(struct ntfs_device *dev, const s64 pos, s64 count,
|
||||
const void *b)
|
||||
{
|
||||
s64 written, total, ret = -1;
|
||||
struct ntfs_device_operations *dops;
|
||||
|
||||
ntfs_log_trace("Entering for pos 0x%llx, count 0x%llx.\n", pos, count);
|
||||
if (!b || count < 0 || pos < 0) {
|
||||
errno = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (!count)
|
||||
return 0;
|
||||
if (NDevReadOnly(dev)) {
|
||||
errno = EROFS;
|
||||
goto out;
|
||||
}
|
||||
|
||||
dops = dev->d_ops;
|
||||
|
||||
NDevSetDirty(dev);
|
||||
for (total = 0; count; count -= written, total += written) {
|
||||
written = dops->pwrite(dev, (const char*)b + total, count,
|
||||
pos + total);
|
||||
/* If everything ok, continue. */
|
||||
if (written > 0)
|
||||
continue;
|
||||
/*
|
||||
* If nothing written or error return number of bytes written.
|
||||
*/
|
||||
if (!written || total)
|
||||
break;
|
||||
/* Nothing written and error, return error status. */
|
||||
total = written;
|
||||
break;
|
||||
}
|
||||
ret = total;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_mst_pread - multi sector transfer (mst) positioned read
|
||||
* @dev: device to read from
|
||||
* @pos: position in file descriptor to read from
|
||||
* @count: number of blocks to read
|
||||
* @bksize: size of each block that needs mst deprotecting
|
||||
* @b: output data buffer
|
||||
*
|
||||
* Multi sector transfer (mst) positioned read. This function will read @count
|
||||
* blocks of size @bksize bytes each from device @dev at position @pos into the
|
||||
* the data buffer @b.
|
||||
*
|
||||
* On success, return the number of successfully read blocks. If this number is
|
||||
* lower than @count this means that we have reached end of file, that the read
|
||||
* was interrupted, 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 or @bksize are 0).
|
||||
*
|
||||
* On error and nothing was read, return -1 with errno set appropriately to the
|
||||
* return code of either seek, read, or set to EINVAL in case of invalid
|
||||
* arguments.
|
||||
*
|
||||
* NOTE: If an incomplete multi sector transfer has been detected the magic
|
||||
* will have been changed to magic_BAAD but no error will be returned. Thus it
|
||||
* is possible that we return count blocks as being read but that any number
|
||||
* (between zero and count!) of these blocks is actually subject to a multi
|
||||
* sector transfer error. This should be detected by the caller by checking for
|
||||
* the magic being "BAAD".
|
||||
*/
|
||||
s64 ntfs_mst_pread(struct ntfs_device *dev, const s64 pos, s64 count,
|
||||
const u32 bksize, void *b)
|
||||
{
|
||||
s64 br, i;
|
||||
|
||||
if (bksize & (bksize - 1) || bksize % NTFS_BLOCK_SIZE) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
/* Do the read. */
|
||||
br = ntfs_pread(dev, pos, count * bksize, b);
|
||||
if (br < 0)
|
||||
return br;
|
||||
/*
|
||||
* Apply fixups to successfully read data, disregarding any errors
|
||||
* returned from the MST fixup function. This is because we want to
|
||||
* fixup everything possible and we rely on the fact that the "BAAD"
|
||||
* magic will be detected later on.
|
||||
*/
|
||||
count = br / bksize;
|
||||
for (i = 0; i < count; ++i)
|
||||
ntfs_mst_post_read_fixup((NTFS_RECORD*)
|
||||
((u8*)b + i * bksize), bksize);
|
||||
/* Finally, return the number of complete blocks read. */
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_mst_pwrite - multi sector transfer (mst) positioned write
|
||||
* @dev: device to write to
|
||||
* @pos: position in file descriptor to write to
|
||||
* @count: number of blocks to write
|
||||
* @bksize: size of each block that needs mst protecting
|
||||
* @b: data buffer to write to disk
|
||||
*
|
||||
* Multi sector transfer (mst) positioned write. This function will write
|
||||
* @count blocks of size @bksize bytes each from data buffer @b to the device
|
||||
* @dev at position @pos.
|
||||
*
|
||||
* On success, return the number of successfully written blocks. If this number
|
||||
* is lower than @count this means that the write has been interrupted or that
|
||||
* an error was encountered during the write so that the write is partial. 0
|
||||
* means nothing was written (also return 0 when @count or @bksize are 0).
|
||||
*
|
||||
* On error and nothing has been written, return -1 with errno set
|
||||
* appropriately to the return code of either seek, write, or set
|
||||
* to EINVAL in case of invalid arguments.
|
||||
*
|
||||
* NOTE: We mst protect the data, write it, then mst deprotect it using a quick
|
||||
* deprotect algorithm (no checking). This saves us from making a copy before
|
||||
* the write and at the same time causes the usn to be incremented in the
|
||||
* buffer. This conceptually fits in better with the idea that cached data is
|
||||
* always deprotected and protection is performed when the data is actually
|
||||
* going to hit the disk and the cache is immediately deprotected again
|
||||
* simulating an mst read on the written data. This way cache coherency is
|
||||
* achieved.
|
||||
*/
|
||||
s64 ntfs_mst_pwrite(struct ntfs_device *dev, const s64 pos, s64 count,
|
||||
const u32 bksize, void *b)
|
||||
{
|
||||
s64 written, i;
|
||||
|
||||
if (count < 0 || bksize % NTFS_BLOCK_SIZE) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (!count)
|
||||
return 0;
|
||||
/* Prepare data for writing. */
|
||||
for (i = 0; i < count; ++i) {
|
||||
int err;
|
||||
|
||||
err = ntfs_mst_pre_write_fixup((NTFS_RECORD*)
|
||||
((u8*)b + i * bksize), bksize);
|
||||
if (err < 0) {
|
||||
/* Abort write at this position. */
|
||||
if (!i)
|
||||
return err;
|
||||
count = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Write the prepared data. */
|
||||
written = ntfs_pwrite(dev, pos, count * bksize, b);
|
||||
/* Quickly deprotect the data again. */
|
||||
for (i = 0; i < count; ++i)
|
||||
ntfs_mst_post_write_fixup((NTFS_RECORD*)((u8*)b + i * bksize));
|
||||
if (written <= 0)
|
||||
return written;
|
||||
/* Finally, return the number of complete blocks written. */
|
||||
return written / bksize;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_cluster_read - read ntfs clusters
|
||||
* @vol: volume to read from
|
||||
* @lcn: starting logical cluster number
|
||||
* @count: number of clusters to read
|
||||
* @b: output data buffer
|
||||
*
|
||||
* Read @count ntfs clusters starting at logical cluster number @lcn from
|
||||
* volume @vol into buffer @b. Return number of clusters read or -1 on error,
|
||||
* with errno set to the error code.
|
||||
*/
|
||||
s64 ntfs_cluster_read(const ntfs_volume *vol, const s64 lcn, const s64 count,
|
||||
void *b)
|
||||
{
|
||||
s64 br;
|
||||
|
||||
if (!vol || lcn < 0 || count < 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (vol->nr_clusters < lcn + count) {
|
||||
errno = ESPIPE;
|
||||
ntfs_log_perror("Trying to read outside of volume "
|
||||
"(%lld < %lld)", vol->nr_clusters, lcn + count);
|
||||
return -1;
|
||||
}
|
||||
br = ntfs_pread(vol->dev, lcn << vol->cluster_size_bits,
|
||||
count << vol->cluster_size_bits, b);
|
||||
if (br < 0) {
|
||||
ntfs_log_perror("Error reading cluster(s)");
|
||||
return br;
|
||||
}
|
||||
return br >> vol->cluster_size_bits;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_cluster_write - write ntfs clusters
|
||||
* @vol: volume to write to
|
||||
* @lcn: starting logical cluster number
|
||||
* @count: number of clusters to write
|
||||
* @b: data buffer to write to disk
|
||||
*
|
||||
* Write @count ntfs clusters starting at logical cluster number @lcn from
|
||||
* buffer @b to volume @vol. Return the number of clusters written or -1 on
|
||||
* error, with errno set to the error code.
|
||||
*/
|
||||
s64 ntfs_cluster_write(const ntfs_volume *vol, const s64 lcn,
|
||||
const s64 count, const void *b)
|
||||
{
|
||||
s64 bw;
|
||||
|
||||
if (!vol || lcn < 0 || count < 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (vol->nr_clusters < lcn + count) {
|
||||
errno = ESPIPE;
|
||||
ntfs_log_perror("Trying to write outside of volume "
|
||||
"(%lld < %lld)", vol->nr_clusters, lcn + count);
|
||||
return -1;
|
||||
}
|
||||
if (!NVolReadOnly(vol))
|
||||
bw = ntfs_pwrite(vol->dev, lcn << vol->cluster_size_bits,
|
||||
count << vol->cluster_size_bits, b);
|
||||
else
|
||||
bw = count << vol->cluster_size_bits;
|
||||
if (bw < 0) {
|
||||
ntfs_log_perror("Error writing cluster(s)");
|
||||
return bw;
|
||||
}
|
||||
return bw >> vol->cluster_size_bits;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_offset_valid - test if a device offset is valid
|
||||
* @dev: open device
|
||||
* @ofs: offset to test for validity
|
||||
*
|
||||
* Test if the offset @ofs is an existing location on the device described
|
||||
* by the open device structure @dev.
|
||||
*
|
||||
* Return 0 if it is valid and -1 if it is not valid.
|
||||
*/
|
||||
static int ntfs_device_offset_valid(struct ntfs_device *dev, s64 ofs)
|
||||
{
|
||||
char ch;
|
||||
|
||||
if (dev->d_ops->seek(dev, ofs, SEEK_SET) >= 0 &&
|
||||
dev->d_ops->read(dev, &ch, 1) == 1)
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_size_get - return the size of a device in blocks
|
||||
* @dev: open device
|
||||
* @block_size: block size in bytes in which to return the result
|
||||
*
|
||||
* Return the number of @block_size sized blocks in the device described by the
|
||||
* open device @dev.
|
||||
*
|
||||
* Adapted from e2fsutils-1.19, Copyright (C) 1995 Theodore Ts'o.
|
||||
*
|
||||
* On error return -1 with errno set to the error code.
|
||||
*/
|
||||
s64 ntfs_device_size_get(struct ntfs_device *dev, int block_size)
|
||||
{
|
||||
s64 high, low;
|
||||
|
||||
if (!dev || block_size <= 0 || (block_size - 1) & block_size) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
#ifdef BLKGETSIZE64
|
||||
{ u64 size;
|
||||
|
||||
if (dev->d_ops->ioctl(dev, BLKGETSIZE64, &size) >= 0) {
|
||||
ntfs_log_debug("BLKGETSIZE64 nr bytes = %llu (0x%llx)\n",
|
||||
(unsigned long long)size,
|
||||
(unsigned long long)size);
|
||||
return (s64)size / block_size;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef BLKGETSIZE
|
||||
{ unsigned long size;
|
||||
|
||||
if (dev->d_ops->ioctl(dev, BLKGETSIZE, &size) >= 0) {
|
||||
ntfs_log_debug("BLKGETSIZE nr 512 byte blocks = %lu (0x%lx)\n",
|
||||
size, size);
|
||||
return (s64)size * 512 / block_size;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef FDGETPRM
|
||||
{ struct floppy_struct this_floppy;
|
||||
|
||||
if (dev->d_ops->ioctl(dev, FDGETPRM, &this_floppy) >= 0) {
|
||||
ntfs_log_debug("FDGETPRM nr 512 byte blocks = %lu (0x%lx)\n",
|
||||
(unsigned long)this_floppy.size,
|
||||
(unsigned long)this_floppy.size);
|
||||
return (s64)this_floppy.size * 512 / block_size;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
* We couldn't figure it out by using a specialized ioctl,
|
||||
* so do binary search to find the size of the device.
|
||||
*/
|
||||
low = 0LL;
|
||||
for (high = 1024LL; !ntfs_device_offset_valid(dev, high); high <<= 1)
|
||||
low = high;
|
||||
while (low < high - 1LL) {
|
||||
const s64 mid = (low + high) / 2;
|
||||
|
||||
if (!ntfs_device_offset_valid(dev, mid))
|
||||
low = mid;
|
||||
else
|
||||
high = mid;
|
||||
}
|
||||
dev->d_ops->seek(dev, 0LL, SEEK_SET);
|
||||
return (low + 1LL) / block_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_partition_start_sector_get - get starting sector of a partition
|
||||
* @dev: open device
|
||||
*
|
||||
* On success, return the starting sector of the partition @dev in the parent
|
||||
* block device of @dev. On error return -1 with errno set to the error code.
|
||||
*
|
||||
* The following error codes are defined:
|
||||
* EINVAL Input parameter error
|
||||
* EOPNOTSUPP System does not support HDIO_GETGEO ioctl
|
||||
* ENOTTY @dev is a file or a device not supporting HDIO_GETGEO
|
||||
*/
|
||||
s64 ntfs_device_partition_start_sector_get(struct ntfs_device *dev)
|
||||
{
|
||||
if (!dev) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
#ifdef HDIO_GETGEO
|
||||
{ struct hd_geometry geo;
|
||||
|
||||
if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) {
|
||||
ntfs_log_debug("HDIO_GETGEO start_sect = %lu (0x%lx)\n",
|
||||
geo.start, geo.start);
|
||||
return geo.start;
|
||||
}
|
||||
}
|
||||
#else
|
||||
errno = EOPNOTSUPP;
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_heads_get - get number of heads of device
|
||||
* @dev: open device
|
||||
*
|
||||
* On success, return the number of heads on the device @dev. On error return
|
||||
* -1 with errno set to the error code.
|
||||
*
|
||||
* The following error codes are defined:
|
||||
* EINVAL Input parameter error
|
||||
* EOPNOTSUPP System does not support HDIO_GETGEO ioctl
|
||||
* ENOTTY @dev is a file or a device not supporting HDIO_GETGEO
|
||||
*/
|
||||
int ntfs_device_heads_get(struct ntfs_device *dev)
|
||||
{
|
||||
if (!dev) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
#ifdef HDIO_GETGEO
|
||||
{ struct hd_geometry geo;
|
||||
|
||||
if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) {
|
||||
ntfs_log_debug("HDIO_GETGEO heads = %u (0x%x)\n",
|
||||
(unsigned)geo.heads,
|
||||
(unsigned)geo.heads);
|
||||
return geo.heads;
|
||||
}
|
||||
}
|
||||
#else
|
||||
errno = EOPNOTSUPP;
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_sectors_per_track_get - get number of sectors per track of device
|
||||
* @dev: open device
|
||||
*
|
||||
* On success, return the number of sectors per track on the device @dev. On
|
||||
* error return -1 with errno set to the error code.
|
||||
*
|
||||
* The following error codes are defined:
|
||||
* EINVAL Input parameter error
|
||||
* EOPNOTSUPP System does not support HDIO_GETGEO ioctl
|
||||
* ENOTTY @dev is a file or a device not supporting HDIO_GETGEO
|
||||
*/
|
||||
int ntfs_device_sectors_per_track_get(struct ntfs_device *dev)
|
||||
{
|
||||
if (!dev) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
#ifdef HDIO_GETGEO
|
||||
{ struct hd_geometry geo;
|
||||
|
||||
if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) {
|
||||
ntfs_log_debug("HDIO_GETGEO sectors_per_track = %u (0x%x)\n",
|
||||
(unsigned)geo.sectors,
|
||||
(unsigned)geo.sectors);
|
||||
return geo.sectors;
|
||||
}
|
||||
}
|
||||
#else
|
||||
errno = EOPNOTSUPP;
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_sector_size_get - get sector size of a device
|
||||
* @dev: open device
|
||||
*
|
||||
* On success, return the sector size in bytes of the device @dev.
|
||||
* On error return -1 with errno set to the error code.
|
||||
*
|
||||
* The following error codes are defined:
|
||||
* EINVAL Input parameter error
|
||||
* EOPNOTSUPP System does not support BLKSSZGET ioctl
|
||||
* ENOTTY @dev is a file or a device not supporting BLKSSZGET
|
||||
*/
|
||||
int ntfs_device_sector_size_get(struct ntfs_device *dev)
|
||||
{
|
||||
if (!dev) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
#ifdef BLKSSZGET
|
||||
{
|
||||
int sect_size = 0;
|
||||
|
||||
if (!dev->d_ops->ioctl(dev, BLKSSZGET, §_size)) {
|
||||
ntfs_log_debug("BLKSSZGET sector size = %d bytes\n",
|
||||
sect_size);
|
||||
return sect_size;
|
||||
}
|
||||
}
|
||||
#else
|
||||
errno = EOPNOTSUPP;
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_block_size_set - set block size of a device
|
||||
* @dev: open device
|
||||
* @block_size: block size to set @dev to
|
||||
*
|
||||
* On success, return 0.
|
||||
* On error return -1 with errno set to the error code.
|
||||
*
|
||||
* The following error codes are defined:
|
||||
* EINVAL Input parameter error
|
||||
* EOPNOTSUPP System does not support BLKBSZSET ioctl
|
||||
* ENOTTY @dev is a file or a device not supporting BLKBSZSET
|
||||
*/
|
||||
int ntfs_device_block_size_set(struct ntfs_device *dev,
|
||||
int block_size __attribute__((unused)))
|
||||
{
|
||||
if (!dev) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
#ifdef BLKBSZSET
|
||||
{
|
||||
size_t s_block_size = block_size;
|
||||
if (!dev->d_ops->ioctl(dev, BLKBSZSET, &s_block_size)) {
|
||||
ntfs_log_debug("Used BLKBSZSET to set block size to "
|
||||
"%d bytes.\n", block_size);
|
||||
return 0;
|
||||
}
|
||||
/* If not a block device, pretend it was successful. */
|
||||
if (!NDevBlock(dev))
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
/* If not a block device, pretend it was successful. */
|
||||
if (!NDevBlock(dev))
|
||||
return 0;
|
||||
errno = EOPNOTSUPP;
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* device.h - Exports for low level device io. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2000-2006 Anton Altaparmakov
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file is distributed in the hope that it will be
|
||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_DEVICE_H
|
||||
#define _NTFS_DEVICE_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "device_io.h"
|
||||
#include "types.h"
|
||||
#include "support.h"
|
||||
#include "volume.h"
|
||||
|
||||
/**
|
||||
* enum ntfs_device_state_bits -
|
||||
*
|
||||
* Defined bits for the state field in the ntfs_device structure.
|
||||
*/
|
||||
typedef enum {
|
||||
ND_Open, /* 1: Device is open. */
|
||||
ND_ReadOnly, /* 1: Device is read-only. */
|
||||
ND_Dirty, /* 1: Device is dirty, needs sync. */
|
||||
ND_Block, /* 1: Device is a block device. */
|
||||
} ntfs_device_state_bits;
|
||||
|
||||
#define test_ndev_flag(nd, flag) test_bit(ND_##flag, (nd)->d_state)
|
||||
#define set_ndev_flag(nd, flag) set_bit(ND_##flag, (nd)->d_state)
|
||||
#define clear_ndev_flag(nd, flag) clear_bit(ND_##flag, (nd)->d_state)
|
||||
|
||||
#define NDevOpen(nd) test_ndev_flag(nd, Open)
|
||||
#define NDevSetOpen(nd) set_ndev_flag(nd, Open)
|
||||
#define NDevClearOpen(nd) clear_ndev_flag(nd, Open)
|
||||
|
||||
#define NDevReadOnly(nd) test_ndev_flag(nd, ReadOnly)
|
||||
#define NDevSetReadOnly(nd) set_ndev_flag(nd, ReadOnly)
|
||||
#define NDevClearReadOnly(nd) clear_ndev_flag(nd, ReadOnly)
|
||||
|
||||
#define NDevDirty(nd) test_ndev_flag(nd, Dirty)
|
||||
#define NDevSetDirty(nd) set_ndev_flag(nd, Dirty)
|
||||
#define NDevClearDirty(nd) clear_ndev_flag(nd, Dirty)
|
||||
|
||||
#define NDevBlock(nd) test_ndev_flag(nd, Block)
|
||||
#define NDevSetBlock(nd) set_ndev_flag(nd, Block)
|
||||
#define NDevClearBlock(nd) clear_ndev_flag(nd, Block)
|
||||
|
||||
/**
|
||||
* struct ntfs_device -
|
||||
*
|
||||
* The ntfs device structure defining all operations needed to access the low
|
||||
* level device underlying the ntfs volume.
|
||||
*/
|
||||
struct ntfs_device {
|
||||
struct ntfs_device_operations *d_ops; /* Device operations. */
|
||||
unsigned long d_state; /* State of the device. */
|
||||
char *d_name; /* Name of device. */
|
||||
void *d_private; /* Private data used by the
|
||||
device operations. */
|
||||
};
|
||||
|
||||
struct stat;
|
||||
|
||||
/**
|
||||
* struct ntfs_device_operations -
|
||||
*
|
||||
* The ntfs device operations defining all operations that can be performed on
|
||||
* the low level device described by an ntfs device structure.
|
||||
*/
|
||||
struct ntfs_device_operations {
|
||||
int (*open)(struct ntfs_device *dev, int flags);
|
||||
int (*close)(struct ntfs_device *dev);
|
||||
s64 (*seek)(struct ntfs_device *dev, s64 offset, int whence);
|
||||
s64 (*read)(struct ntfs_device *dev, void *buf, s64 count);
|
||||
s64 (*write)(struct ntfs_device *dev, const void *buf, s64 count);
|
||||
s64 (*pread)(struct ntfs_device *dev, void *buf, s64 count, s64 offset);
|
||||
s64 (*pwrite)(struct ntfs_device *dev, const void *buf, s64 count,
|
||||
s64 offset);
|
||||
int (*sync)(struct ntfs_device *dev);
|
||||
int (*stat)(struct ntfs_device *dev, struct stat *buf);
|
||||
int (*ioctl)(struct ntfs_device *dev, int request, void *argp);
|
||||
};
|
||||
|
||||
extern struct ntfs_device *ntfs_device_alloc(const char *name, const long state,
|
||||
struct ntfs_device_operations *dops, void *priv_data);
|
||||
extern int ntfs_device_free(struct ntfs_device *dev);
|
||||
|
||||
extern s64 ntfs_pread(struct ntfs_device *dev, const s64 pos, s64 count,
|
||||
void *b);
|
||||
extern s64 ntfs_pwrite(struct ntfs_device *dev, const s64 pos, s64 count,
|
||||
const void *b);
|
||||
|
||||
extern s64 ntfs_mst_pread(struct ntfs_device *dev, const s64 pos, s64 count,
|
||||
const u32 bksize, void *b);
|
||||
extern s64 ntfs_mst_pwrite(struct ntfs_device *dev, const s64 pos, s64 count,
|
||||
const u32 bksize, void *b);
|
||||
|
||||
extern s64 ntfs_cluster_read(const ntfs_volume *vol, const s64 lcn,
|
||||
const s64 count, void *b);
|
||||
extern s64 ntfs_cluster_write(const ntfs_volume *vol, const s64 lcn,
|
||||
const s64 count, const void *b);
|
||||
|
||||
extern s64 ntfs_device_size_get(struct ntfs_device *dev, int block_size);
|
||||
extern s64 ntfs_device_partition_start_sector_get(struct ntfs_device *dev);
|
||||
extern int ntfs_device_heads_get(struct ntfs_device *dev);
|
||||
extern int ntfs_device_sectors_per_track_get(struct ntfs_device *dev);
|
||||
extern int ntfs_device_sector_size_get(struct ntfs_device *dev);
|
||||
extern int ntfs_device_block_size_set(struct ntfs_device *dev, int block_size);
|
||||
|
||||
#endif /* defined _NTFS_DEVICE_H */
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* device_io.c - Default device io operations. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2003 Anton Altaparmakov
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file is distributed in the hope that it will be
|
||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifndef NO_NTFS_DEVICE_DEFAULT_IO_OPS
|
||||
|
||||
#ifndef __CYGWIN32__
|
||||
|
||||
/* Not on Cygwin; use standard Unix style low level device operations. */
|
||||
#include "unix_io.c"
|
||||
|
||||
#else /* __CYGWIN32__ */
|
||||
|
||||
/* On Cygwin; use Win32 low level device operations. */
|
||||
#include "win32_io.c"
|
||||
|
||||
#endif /* __CYGWIN32__ */
|
||||
|
||||
#endif /* NO_NTFS_DEVICE_DEFAULT_IO_OPS */
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* device_io.h - Exports for default device io. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2000-2006 Anton Altaparmakov
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file is distributed in the hope that it will be
|
||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_DEVICE_IO_H
|
||||
#define _NTFS_DEVICE_IO_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifndef NO_NTFS_DEVICE_DEFAULT_IO_OPS
|
||||
|
||||
#ifndef __CYGWIN32__
|
||||
|
||||
/* Not on Cygwin; use standard Unix style low level device operations. */
|
||||
#define ntfs_device_default_io_ops ntfs_device_unix_io_ops
|
||||
|
||||
#else /* __CYGWIN32__ */
|
||||
|
||||
#ifndef HDIO_GETGEO
|
||||
# define HDIO_GETGEO 0x301
|
||||
/**
|
||||
* struct hd_geometry -
|
||||
*/
|
||||
struct hd_geometry {
|
||||
unsigned char heads;
|
||||
unsigned char sectors;
|
||||
unsigned short cylinders;
|
||||
unsigned long start;
|
||||
};
|
||||
#endif
|
||||
#ifndef BLKGETSIZE
|
||||
# define BLKGETSIZE 0x1260
|
||||
#endif
|
||||
#ifndef BLKSSZGET
|
||||
# define BLKSSZGET 0x1268
|
||||
#endif
|
||||
#ifndef BLKGETSIZE64
|
||||
# define BLKGETSIZE64 0x80041272
|
||||
#endif
|
||||
#ifndef BLKBSZSET
|
||||
# define BLKBSZSET 0x40041271
|
||||
#endif
|
||||
|
||||
/* On Cygwin; use Win32 low level device operations. */
|
||||
#define ntfs_device_default_io_ops ntfs_device_win32_io_ops
|
||||
|
||||
#endif /* __CYGWIN32__ */
|
||||
|
||||
|
||||
/* Forward declaration. */
|
||||
struct ntfs_device_operations;
|
||||
|
||||
extern struct ntfs_device_operations ntfs_device_default_io_ops;
|
||||
|
||||
#endif /* NO_NTFS_DEVICE_DEFAULT_IO_OPS */
|
||||
|
||||
#endif /* defined _NTFS_DEVICE_IO_H */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* dir.h - Exports for directory handling. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2002 Anton Altaparmakov
|
||||
* Copyright (c) 2005-2006 Yura Pakhuchiy
|
||||
* Copyright (c) 2004-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_DIR_H
|
||||
#define _NTFS_DIR_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define PATH_SEP '/'
|
||||
|
||||
#ifndef MAX_PATH
|
||||
#define MAX_PATH 1024
|
||||
#endif
|
||||
|
||||
/*
|
||||
* We do not have these under DJGPP, so define our version that do not conflict
|
||||
* with other S_IFs defined under DJGPP.
|
||||
*/
|
||||
#ifdef DJGPP
|
||||
#ifndef S_IFLNK
|
||||
#define S_IFLNK 0120000
|
||||
#endif
|
||||
#ifndef S_ISLNK
|
||||
#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
|
||||
#endif
|
||||
#ifndef S_IFSOCK
|
||||
#define S_IFSOCK 0140000
|
||||
#endif
|
||||
#ifndef S_ISSOCK
|
||||
#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The little endian Unicode strings $I30, $SII, $SDH, $O, $Q, $R
|
||||
* as a global constant.
|
||||
*/
|
||||
extern ntfschar NTFS_INDEX_I30[5];
|
||||
extern ntfschar NTFS_INDEX_SII[5];
|
||||
extern ntfschar NTFS_INDEX_SDH[5];
|
||||
extern ntfschar NTFS_INDEX_O[3];
|
||||
extern ntfschar NTFS_INDEX_Q[3];
|
||||
extern ntfschar NTFS_INDEX_R[3];
|
||||
|
||||
extern u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni,
|
||||
const ntfschar *uname, const int uname_len);
|
||||
|
||||
extern ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent,
|
||||
const char *pathname);
|
||||
|
||||
extern ntfs_inode *ntfs_create(ntfs_inode *dir_ni, ntfschar *name, u8 name_len,
|
||||
dev_t type);
|
||||
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);
|
||||
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);
|
||||
|
||||
extern int ntfs_link(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name,
|
||||
u8 name_len);
|
||||
|
||||
/*
|
||||
* File types (adapted from include <linux/fs.h>)
|
||||
*/
|
||||
#define NTFS_DT_UNKNOWN 0
|
||||
#define NTFS_DT_FIFO 1
|
||||
#define NTFS_DT_CHR 2
|
||||
#define NTFS_DT_DIR 4
|
||||
#define NTFS_DT_BLK 6
|
||||
#define NTFS_DT_REG 8
|
||||
#define NTFS_DT_LNK 10
|
||||
#define NTFS_DT_SOCK 12
|
||||
#define NTFS_DT_WHT 14
|
||||
|
||||
/*
|
||||
* This is the "ntfs_filldir" function type, used by ntfs_readdir() to let
|
||||
* the caller specify what kind of dirent layout it wants to have.
|
||||
* This allows the caller to read directories into their application or
|
||||
* to have different dirent layouts depending on the binary type.
|
||||
*/
|
||||
typedef int (*ntfs_filldir_t)(void *dirent, const ntfschar *name,
|
||||
const int name_len, const int name_type, const s64 pos,
|
||||
const MFT_REF mref, const unsigned dt_type);
|
||||
|
||||
extern int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos,
|
||||
void *dirent, ntfs_filldir_t filldir);
|
||||
|
||||
#endif /* defined _NTFS_DIR_H */
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* endians.h - Definitions related to handling of byte ordering. Part of the
|
||||
* Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2000-2005 Anton Altaparmakov
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file is distributed in the hope that it will be
|
||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the Linux-NTFS
|
||||
* 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_ENDIANS_H
|
||||
#define _NTFS_ENDIANS_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Notes:
|
||||
* We define the conversion functions including typecasts since the
|
||||
* defaults don't necessarily perform appropriate typecasts.
|
||||
* Also, using our own functions means that we can change them if it
|
||||
* turns out that we do need to use the unaligned access macros on
|
||||
* architectures requiring aligned memory accesses...
|
||||
*/
|
||||
|
||||
#ifdef HAVE_ENDIAN_H
|
||||
#include <endian.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_ENDIAN_H
|
||||
#include <sys/endian.h>
|
||||
#endif
|
||||
#ifdef HAVE_MACHINE_ENDIAN_H
|
||||
#include <machine/endian.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_BYTEORDER_H
|
||||
#include <sys/byteorder.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_PARAM_H
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
|
||||
#ifndef __BYTE_ORDER
|
||||
# if defined(_BYTE_ORDER)
|
||||
# define __BYTE_ORDER _BYTE_ORDER
|
||||
# define __LITTLE_ENDIAN _LITTLE_ENDIAN
|
||||
# define __BIG_ENDIAN _BIG_ENDIAN
|
||||
# elif defined(BYTE_ORDER)
|
||||
# define __BYTE_ORDER BYTE_ORDER
|
||||
# define __LITTLE_ENDIAN LITTLE_ENDIAN
|
||||
# define __BIG_ENDIAN BIG_ENDIAN
|
||||
# elif defined(__BYTE_ORDER__)
|
||||
# define __BYTE_ORDER __BYTE_ORDER__
|
||||
# define __LITTLE_ENDIAN __LITTLE_ENDIAN__
|
||||
# define __BIG_ENDIAN __BIG_ENDIAN__
|
||||
# elif (defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)) || \
|
||||
defined(WORDS_LITTLEENDIAN)
|
||||
# define __BYTE_ORDER 1
|
||||
# define __LITTLE_ENDIAN 1
|
||||
# define __BIG_ENDIAN 0
|
||||
# elif (!defined(_LITTLE_ENDIAN) && defined(_BIG_ENDIAN)) || \
|
||||
defined(WORDS_BIGENDIAN)
|
||||
# define __BYTE_ORDER 0
|
||||
# define __LITTLE_ENDIAN 1
|
||||
# define __BIG_ENDIAN 0
|
||||
# else
|
||||
# error "__BYTE_ORDER is not defined."
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#define __ntfs_bswap_constant_16(x) \
|
||||
(u16)((((u16)(x) & 0xff00) >> 8) | \
|
||||
(((u16)(x) & 0x00ff) << 8))
|
||||
|
||||
#define __ntfs_bswap_constant_32(x) \
|
||||
(u32)((((u32)(x) & 0xff000000u) >> 24) | \
|
||||
(((u32)(x) & 0x00ff0000u) >> 8) | \
|
||||
(((u32)(x) & 0x0000ff00u) << 8) | \
|
||||
(((u32)(x) & 0x000000ffu) << 24))
|
||||
|
||||
#define __ntfs_bswap_constant_64(x) \
|
||||
(u64)((((u64)(x) & 0xff00000000000000ull) >> 56) | \
|
||||
(((u64)(x) & 0x00ff000000000000ull) >> 40) | \
|
||||
(((u64)(x) & 0x0000ff0000000000ull) >> 24) | \
|
||||
(((u64)(x) & 0x000000ff00000000ull) >> 8) | \
|
||||
(((u64)(x) & 0x00000000ff000000ull) << 8) | \
|
||||
(((u64)(x) & 0x0000000000ff0000ull) << 24) | \
|
||||
(((u64)(x) & 0x000000000000ff00ull) << 40) | \
|
||||
(((u64)(x) & 0x00000000000000ffull) << 56))
|
||||
|
||||
#ifdef HAVE_BYTESWAP_H
|
||||
# include <byteswap.h>
|
||||
#else
|
||||
# define bswap_16(x) __ntfs_bswap_constant_16(x)
|
||||
# define bswap_32(x) __ntfs_bswap_constant_32(x)
|
||||
# define bswap_64(x) __ntfs_bswap_constant_64(x)
|
||||
#endif
|
||||
|
||||
#if defined(__LITTLE_ENDIAN) && (__BYTE_ORDER == __LITTLE_ENDIAN)
|
||||
|
||||
#define __le16_to_cpu(x) (x)
|
||||
#define __le32_to_cpu(x) (x)
|
||||
#define __le64_to_cpu(x) (x)
|
||||
|
||||
#define __cpu_to_le16(x) (x)
|
||||
#define __cpu_to_le32(x) (x)
|
||||
#define __cpu_to_le64(x) (x)
|
||||
|
||||
#define __constant_le16_to_cpu(x) (x)
|
||||
#define __constant_le32_to_cpu(x) (x)
|
||||
#define __constant_le64_to_cpu(x) (x)
|
||||
|
||||
#define __constant_cpu_to_le16(x) (x)
|
||||
#define __constant_cpu_to_le32(x) (x)
|
||||
#define __constant_cpu_to_le64(x) (x)
|
||||
|
||||
#elif defined(__BIG_ENDIAN) && (__BYTE_ORDER == __BIG_ENDIAN)
|
||||
|
||||
#define __le16_to_cpu(x) bswap_16(x)
|
||||
#define __le32_to_cpu(x) bswap_32(x)
|
||||
#define __le64_to_cpu(x) bswap_64(x)
|
||||
|
||||
#define __cpu_to_le16(x) bswap_16(x)
|
||||
#define __cpu_to_le32(x) bswap_32(x)
|
||||
#define __cpu_to_le64(x) bswap_64(x)
|
||||
|
||||
#define __constant_le16_to_cpu(x) __ntfs_bswap_constant_16((u16)(x))
|
||||
#define __constant_le32_to_cpu(x) __ntfs_bswap_constant_32((u32)(x))
|
||||
#define __constant_le64_to_cpu(x) __ntfs_bswap_constant_64((u64)(x))
|
||||
|
||||
#define __constant_cpu_to_le16(x) __ntfs_bswap_constant_16((u16)(x))
|
||||
#define __constant_cpu_to_le32(x) __ntfs_bswap_constant_32((u32)(x))
|
||||
#define __constant_cpu_to_le64(x) __ntfs_bswap_constant_64((u64)(x))
|
||||
|
||||
#else
|
||||
|
||||
#error "You must define __BYTE_ORDER to be __LITTLE_ENDIAN or __BIG_ENDIAN."
|
||||
|
||||
#endif
|
||||
|
||||
/* Unsigned from LE to CPU conversion. */
|
||||
|
||||
#define le16_to_cpu(x) (u16)__le16_to_cpu((u16)(x))
|
||||
#define le32_to_cpu(x) (u32)__le32_to_cpu((u32)(x))
|
||||
#define le64_to_cpu(x) (u64)__le64_to_cpu((u64)(x))
|
||||
|
||||
#define le16_to_cpup(x) (u16)__le16_to_cpu(*(const u16*)(x))
|
||||
#define le32_to_cpup(x) (u32)__le32_to_cpu(*(const u32*)(x))
|
||||
#define le64_to_cpup(x) (u64)__le64_to_cpu(*(const u64*)(x))
|
||||
|
||||
/* Signed from LE to CPU conversion. */
|
||||
|
||||
#define sle16_to_cpu(x) (s16)__le16_to_cpu((s16)(x))
|
||||
#define sle32_to_cpu(x) (s32)__le32_to_cpu((s32)(x))
|
||||
#define sle64_to_cpu(x) (s64)__le64_to_cpu((s64)(x))
|
||||
|
||||
#define sle16_to_cpup(x) (s16)__le16_to_cpu(*(s16*)(x))
|
||||
#define sle32_to_cpup(x) (s32)__le32_to_cpu(*(s32*)(x))
|
||||
#define sle64_to_cpup(x) (s64)__le64_to_cpu(*(s64*)(x))
|
||||
|
||||
/* Unsigned from CPU to LE conversion. */
|
||||
|
||||
#define cpu_to_le16(x) (u16)__cpu_to_le16((u16)(x))
|
||||
#define cpu_to_le32(x) (u32)__cpu_to_le32((u32)(x))
|
||||
#define cpu_to_le64(x) (u64)__cpu_to_le64((u64)(x))
|
||||
|
||||
#define cpu_to_le16p(x) (u16)__cpu_to_le16(*(u16*)(x))
|
||||
#define cpu_to_le32p(x) (u32)__cpu_to_le32(*(u32*)(x))
|
||||
#define cpu_to_le64p(x) (u64)__cpu_to_le64(*(u64*)(x))
|
||||
|
||||
/* Signed from CPU to LE conversion. */
|
||||
|
||||
#define cpu_to_sle16(x) (s16)__cpu_to_le16((s16)(x))
|
||||
#define cpu_to_sle32(x) (s32)__cpu_to_le32((s32)(x))
|
||||
#define cpu_to_sle64(x) (s64)__cpu_to_le64((s64)(x))
|
||||
|
||||
#define cpu_to_sle16p(x) (s16)__cpu_to_le16(*(s16*)(x))
|
||||
#define cpu_to_sle32p(x) (s32)__cpu_to_le32(*(s32*)(x))
|
||||
#define cpu_to_sle64p(x) (s64)__cpu_to_le64(*(s64*)(x))
|
||||
|
||||
/* Constant endianness conversion defines. */
|
||||
|
||||
#define const_le16_to_cpu(x) __constant_le16_to_cpu(x)
|
||||
#define const_le32_to_cpu(x) __constant_le32_to_cpu(x)
|
||||
#define const_le64_to_cpu(x) __constant_le64_to_cpu(x)
|
||||
|
||||
#define const_cpu_to_le16(x) __constant_cpu_to_le16(x)
|
||||
#define const_cpu_to_le32(x) __constant_cpu_to_le32(x)
|
||||
#define const_cpu_to_le64(x) __constant_cpu_to_le64(x)
|
||||
|
||||
#endif /* defined _NTFS_ENDIANS_H */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* index.h - Defines for NTFS index handling. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2004 Anton Altaparmakov
|
||||
* Copyright (c) 2004-2005 Richard Russon
|
||||
* Copyright (c) 2005 Yura Pakhuchiy
|
||||
* Copyright (c) 2006 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
|
||||
* 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_INDEX_H
|
||||
#define _NTFS_INDEX_H
|
||||
|
||||
#include "attrib.h"
|
||||
#include "types.h"
|
||||
#include "layout.h"
|
||||
#include "inode.h"
|
||||
#include "mft.h"
|
||||
|
||||
#define VCN_INDEX_ROOT_PARENT ((VCN)-2)
|
||||
|
||||
#define MAX_PARENT_VCN 32
|
||||
|
||||
/**
|
||||
* struct ntfs_index_context -
|
||||
* @ni: inode containing the @entry described by this context
|
||||
* @name: name of the index described by this context
|
||||
* @name_len: length of the index name
|
||||
* @entry: index entry (points into @ir or @ia)
|
||||
* @data: index entry data (points into @entry)
|
||||
* @data_len: length in bytes of @data
|
||||
* @is_in_root: TRUE if @entry is in @ir or FALSE if it is in @ia
|
||||
* @ir: index root if @is_in_root or NULL otherwise
|
||||
* @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
|
||||
* @median: move to the parent if splitting index blocks
|
||||
* @ib_dirty: TRUE if index block was changed
|
||||
* @block_size: index block size
|
||||
* @vcn_size_bits: VCN size bits for this index block
|
||||
*
|
||||
* @ni is the inode this context belongs to.
|
||||
*
|
||||
* @entry is the index entry described by this context. @data and @data_len
|
||||
* are the index entry data and its length in bytes, respectively. @data
|
||||
* 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
|
||||
* @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,
|
||||
* 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.
|
||||
*
|
||||
* To obtain a context call ntfs_index_ctx_get().
|
||||
*
|
||||
* When finished with the @entry and its @data, call ntfs_index_ctx_put() to
|
||||
* free the context and other associated resources.
|
||||
*
|
||||
* If the index entry was modified, call ntfs_index_entry_mark_dirty() before
|
||||
* the call to ntfs_index_ctx_put() to ensure that the changes are written
|
||||
* to disk.
|
||||
*/
|
||||
typedef struct {
|
||||
ntfs_inode *ni;
|
||||
ntfschar *name;
|
||||
u32 name_len;
|
||||
INDEX_ENTRY *entry;
|
||||
void *data;
|
||||
u16 data_len;
|
||||
COLLATION_RULES cr;
|
||||
BOOL is_in_root;
|
||||
INDEX_ROOT *ir;
|
||||
ntfs_attr_search_ctx *actx;
|
||||
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;
|
||||
u8 vcn_size_bits;
|
||||
} ntfs_index_context;
|
||||
|
||||
extern ntfs_index_context *ntfs_index_ctx_get(ntfs_inode *ni,
|
||||
ntfschar *name, u32 name_len);
|
||||
extern void ntfs_index_ctx_put(ntfs_index_context *ictx);
|
||||
extern void ntfs_index_ctx_reinit(ntfs_index_context *ictx);
|
||||
|
||||
extern int ntfs_index_lookup(const void *key, const int key_len,
|
||||
ntfs_index_context *ictx);
|
||||
|
||||
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 INDEX_ROOT *ntfs_index_root_get(ntfs_inode *ni, ATTR_RECORD *attr);
|
||||
|
||||
extern VCN ntfs_ie_get_vcn(INDEX_ENTRY *ie);
|
||||
|
||||
extern void ntfs_index_entry_mark_dirty(ntfs_index_context *ictx);
|
||||
|
||||
extern char *ntfs_ie_filename_get(INDEX_ENTRY *ie);
|
||||
extern void ntfs_ie_filename_dump(INDEX_ENTRY *ie);
|
||||
extern void ntfs_ih_filename_dump(INDEX_HEADER *ih);
|
||||
|
||||
#endif /* _NTFS_INDEX_H */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* 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) 2004-2005 Richard Russon
|
||||
* Copyright (c) 2006 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
|
||||
* 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_INODE_H
|
||||
#define _NTFS_INODE_H
|
||||
|
||||
/* Forward declaration */
|
||||
typedef struct _ntfs_inode ntfs_inode;
|
||||
|
||||
#include "types.h"
|
||||
#include "layout.h"
|
||||
#include "support.h"
|
||||
#include "volume.h"
|
||||
|
||||
/**
|
||||
* enum ntfs_inode_state_bits -
|
||||
*
|
||||
* Defined bits for the state field in the ntfs_inode structure.
|
||||
* (f) = files only, (d) = directories only
|
||||
*/
|
||||
typedef enum {
|
||||
NI_Dirty, /* 1: Mft record needs to be written to disk. */
|
||||
|
||||
/* The NI_AttrList* tests only make sense for base inodes. */
|
||||
NI_AttrList, /* 1: Mft record contains an attribute list. */
|
||||
NI_AttrListDirty, /* 1: Attribute list needs to be written to the
|
||||
mft record and then to disk. */
|
||||
NI_FileNameDirty, /* 1: FILE_NAME attributes need to be updated
|
||||
in the index. */
|
||||
} ntfs_inode_state_bits;
|
||||
|
||||
#define test_nino_flag(ni, flag) test_bit(NI_##flag, (ni)->state)
|
||||
#define set_nino_flag(ni, flag) set_bit(NI_##flag, (ni)->state)
|
||||
#define clear_nino_flag(ni, flag) clear_bit(NI_##flag, (ni)->state)
|
||||
|
||||
#define test_and_set_nino_flag(ni, flag) \
|
||||
test_and_set_bit(NI_##flag, (ni)->state)
|
||||
#define test_and_clear_nino_flag(ni, flag) \
|
||||
test_and_clear_bit(NI_##flag, (ni)->state)
|
||||
|
||||
#define NInoDirty(ni) test_nino_flag(ni, Dirty)
|
||||
#define NInoSetDirty(ni) set_nino_flag(ni, Dirty)
|
||||
#define NInoClearDirty(ni) clear_nino_flag(ni, Dirty)
|
||||
#define NInoTestAndSetDirty(ni) test_and_set_nino_flag(ni, Dirty)
|
||||
#define NInoTestAndClearDirty(ni) test_and_clear_nino_flag(ni, Dirty)
|
||||
|
||||
#define NInoAttrList(ni) test_nino_flag(ni, AttrList)
|
||||
#define NInoSetAttrList(ni) set_nino_flag(ni, AttrList)
|
||||
#define NInoClearAttrList(ni) clear_nino_flag(ni, AttrList)
|
||||
|
||||
|
||||
#define test_nino_al_flag(ni, flag) test_nino_flag(ni, AttrList##flag)
|
||||
#define set_nino_al_flag(ni, flag) set_nino_flag(ni, AttrList##flag)
|
||||
#define clear_nino_al_flag(ni, flag) clear_nino_flag(ni, AttrList##flag)
|
||||
|
||||
#define test_and_set_nino_al_flag(ni, flag) \
|
||||
test_and_set_nino_flag(ni, AttrList##flag)
|
||||
#define test_and_clear_nino_al_flag(ni, flag) \
|
||||
test_and_clear_nino_flag(ni, AttrList##flag)
|
||||
|
||||
#define NInoAttrListDirty(ni) test_nino_al_flag(ni, Dirty)
|
||||
#define NInoAttrListSetDirty(ni) set_nino_al_flag(ni, Dirty)
|
||||
#define NInoAttrListClearDirty(ni) clear_nino_al_flag(ni, Dirty)
|
||||
#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 NInoFileNameTestAndSetDirty(ni) \
|
||||
test_and_set_nino_flag(ni, FileNameDirty)
|
||||
#define NInoFileNameTestAndClearDirty(ni) \
|
||||
test_and_clear_nino_flag(ni, FileNameDirty)
|
||||
|
||||
/**
|
||||
* struct _ntfs_inode - The NTFS in-memory inode structure.
|
||||
*
|
||||
* It is just used as an extension to the fields already provided in the VFS
|
||||
* inode.
|
||||
*/
|
||||
struct _ntfs_inode {
|
||||
u64 mft_no; /* Inode / mft record number. */
|
||||
MFT_RECORD *mrec; /* The actual mft record of the inode. */
|
||||
ntfs_volume *vol; /* Pointer to the ntfs volume of this inode. */
|
||||
unsigned long state; /* NTFS specific flags describing this inode.
|
||||
See ntfs_inode_state_bits above. */
|
||||
FILE_ATTR_FLAGS flags; /* Flags describing the file.
|
||||
(Copy from STANDARD_INFORMATION) */
|
||||
/*
|
||||
* Attribute list support (for use by the attribute lookup functions).
|
||||
* Setup during ntfs_open_inode() for all inodes with attribute lists.
|
||||
* Only valid if NI_AttrList is set in state.
|
||||
*/
|
||||
u32 attr_list_size; /* Length of attribute list value in bytes. */
|
||||
u8 *attr_list; /* Attribute list value itself. */
|
||||
/* Below fields are always valid. */
|
||||
s32 nr_extents; /* For a base mft record, the number of
|
||||
attached extent inodes (0 if none), for
|
||||
extent records this is -1. */
|
||||
union { /* This union is only used if nr_extents != 0. */
|
||||
ntfs_inode **extent_nis;/* For nr_extents > 0, array of the
|
||||
ntfs inodes of the extent mft
|
||||
records belonging to this base
|
||||
inode which have been loaded. */
|
||||
ntfs_inode *base_ni; /* For nr_extents == -1, the ntfs
|
||||
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. */
|
||||
s64 allocated_size; /* Allocated size stored in the filename
|
||||
index. (NOTE: Equal to allocated size of
|
||||
the unnamed data attribute for normal or
|
||||
encrypted files and to compressed size
|
||||
of the unnamed data attribute for sparse or
|
||||
compressed files.) */
|
||||
|
||||
time_t creation_time;
|
||||
time_t last_data_change_time;
|
||||
time_t last_mft_change_time;
|
||||
time_t last_access_time;
|
||||
};
|
||||
|
||||
extern ntfs_inode *ntfs_inode_allocate(ntfs_volume *vol);
|
||||
|
||||
extern ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref);
|
||||
|
||||
extern int ntfs_inode_close(ntfs_inode *ni);
|
||||
|
||||
extern ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni,
|
||||
const MFT_REF mref);
|
||||
|
||||
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 int ntfs_inode_sync(ntfs_inode *ni);
|
||||
|
||||
extern int ntfs_inode_add_attrlist(ntfs_inode *ni);
|
||||
|
||||
extern int ntfs_inode_free_space(ntfs_inode *ni, int size);
|
||||
|
||||
extern int ntfs_inode_badclus_bad(u64 mft_no, ATTR_RECORD *a);
|
||||
|
||||
#endif /* defined _NTFS_INODE_H */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,818 @@
|
||||
/**
|
||||
* lcnalloc.c - Cluster (de)allocation code. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2002-2004 Anton Altaparmakov
|
||||
* Copyright (c) 2004-2007 Szabolcs Szakacsits
|
||||
* Copyright (c) 2004 Yura Pakhuchiy
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file is distributed in the hope that it will be
|
||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDIO_H
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
#include "attrib.h"
|
||||
#include "bitmap.h"
|
||||
#include "debug.h"
|
||||
#include "runlist.h"
|
||||
#include "volume.h"
|
||||
#include "lcnalloc.h"
|
||||
#include "logging.h"
|
||||
#include "misc.h"
|
||||
|
||||
#define NTFS_LCNALLOC_BSIZE 512
|
||||
|
||||
#define NTFS_LCNALLOC_SKIP 4096
|
||||
|
||||
void ntfs_cluster_set_zone_pos(u8 zone, LCN zone_start, LCN zone_end,
|
||||
LCN *zone_pos, LCN tc, LCN bmp_initial_pos)
|
||||
{
|
||||
ntfs_log_trace("Before: zone %d = %lld\n", zone, (long long)*zone_pos);
|
||||
|
||||
if (tc >= zone_end) {
|
||||
*zone_pos = zone_start;
|
||||
// FIXME: seems to be bogus and only MFT zone used it
|
||||
if (!zone_end)
|
||||
*zone_pos = 0;
|
||||
} else if ((bmp_initial_pos >= *zone_pos || tc > *zone_pos) &&
|
||||
tc >= zone_start)
|
||||
*zone_pos = tc;
|
||||
|
||||
ntfs_log_trace("After: zone %d = %lld\n", zone, (long long)*zone_pos);
|
||||
}
|
||||
|
||||
int ntfs_cluster_update_zone_pos(ntfs_volume *vol, u8 zone, LCN tc,
|
||||
LCN bmp_initial_pos)
|
||||
{
|
||||
ntfs_log_trace("tc = %lld, zone = %d\n", (long long)tc, zone);
|
||||
|
||||
switch (zone) {
|
||||
case 1:
|
||||
ntfs_cluster_set_zone_pos(zone, vol->mft_lcn,
|
||||
vol->mft_zone_end,
|
||||
&vol->mft_zone_pos,
|
||||
tc, bmp_initial_pos);
|
||||
break;
|
||||
case 2:
|
||||
ntfs_cluster_set_zone_pos(zone, vol->mft_zone_end,
|
||||
vol->nr_clusters,
|
||||
&vol->data1_zone_pos,
|
||||
tc, bmp_initial_pos);
|
||||
break;
|
||||
case 4:
|
||||
ntfs_cluster_set_zone_pos(zone, 0,
|
||||
vol->mft_zone_start,
|
||||
&vol->data2_zone_pos,
|
||||
tc, bmp_initial_pos);
|
||||
break;
|
||||
default:
|
||||
ntfs_log_error("Invalid zone: %d\n", zone);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_cluster_alloc - allocate clusters on an ntfs volume
|
||||
* @vol: mounted ntfs volume on which to allocate the clusters
|
||||
* @start_vcn: vcn to use for the first allocated cluster
|
||||
* @count: number of clusters to allocate
|
||||
* @start_lcn: starting lcn at which to allocate the clusters (or -1 if none)
|
||||
* @zone: zone from which to allocate the clusters
|
||||
*
|
||||
* Allocate @count clusters preferably starting at cluster @start_lcn or at the
|
||||
* current allocator position if @start_lcn is -1, on the mounted ntfs volume
|
||||
* @vol. @zone is either DATA_ZONE for allocation of normal clusters and
|
||||
* MFT_ZONE for allocation of clusters for the master file table, i.e. the
|
||||
* $MFT/$DATA attribute.
|
||||
*
|
||||
* On success return a runlist describing the allocated cluster(s).
|
||||
*
|
||||
* On error return NULL with errno set to the error code.
|
||||
*
|
||||
* Notes on the allocation algorithm
|
||||
* =================================
|
||||
*
|
||||
* There are two data zones. First is the area between the end of the mft zone
|
||||
* and the end of the volume, and second is the area between the start of the
|
||||
* volume and the start of the mft zone. On unmodified/standard NTFS 1.x
|
||||
* volumes, the second data zone doesn't exist due to the mft zone being
|
||||
* expanded to cover the start of the volume in order to reserve space for the
|
||||
* mft bitmap attribute.
|
||||
*
|
||||
* This is not the prettiest function but the complexity stems from the need of
|
||||
* implementing the mft vs data zoned approach and from the fact that we have
|
||||
* access to the lcn bitmap via up to NTFS_LCNALLOC_BSIZE bytes at a time, so we
|
||||
* need to cope with crossing over boundaries of two buffers. Further, the fact
|
||||
* that the allocator allows for caller supplied hints as to the location of
|
||||
* where allocation should begin and the fact that the allocator keeps track of
|
||||
* where in the data zones the next natural allocation should occur, contribute
|
||||
* to the complexity of the function. But it should all be worthwhile, because
|
||||
* this allocator should: 1) be a full implementation of the MFT zone approach
|
||||
* used by Windows, 2) cause reduction in fragmentation as much as possible,
|
||||
* and 3) be speedy in allocations (the code is not optimized for speed, but
|
||||
* the algorithm is, so further speed improvements are probably possible).
|
||||
*
|
||||
* FIXME: We should be monitoring cluster allocation and increment the MFT zone
|
||||
* size dynamically but this is something for the future. We will just cause
|
||||
* heavier fragmentation by not doing it and I am not even sure Windows would
|
||||
* grow the MFT zone dynamically, so it might even be correct not to do this.
|
||||
* The overhead in doing dynamic MFT zone expansion would be very large and
|
||||
* unlikely worth the effort. (AIA)
|
||||
*
|
||||
* TODO: I have added in double the required zone position pointer wrap around
|
||||
* logic which can be optimized to having only one of the two logic sets.
|
||||
* However, having the double logic will work fine, but if we have only one of
|
||||
* the sets and we get it wrong somewhere, then we get into trouble, so
|
||||
* removing the duplicate logic requires _very_ careful consideration of _all_
|
||||
* possible code paths. So at least for now, I am leaving the double logic -
|
||||
* better safe than sorry... (AIA)
|
||||
*/
|
||||
runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count,
|
||||
LCN start_lcn, const NTFS_CLUSTER_ALLOCATION_ZONES zone)
|
||||
{
|
||||
LCN zone_start, zone_end, bmp_pos, bmp_initial_pos, last_read_pos, lcn;
|
||||
LCN prev_lcn = 0, prev_run_len = 0, mft_zone_size;
|
||||
s64 clusters, br;
|
||||
runlist *rl = NULL, *trl;
|
||||
u8 *buf, *byte;
|
||||
int err = 0, rlpos, rlsize, buf_size;
|
||||
u8 pass, done_zones, search_zone, need_writeback, bit;
|
||||
u8 first_try = 1;
|
||||
|
||||
ntfs_log_trace("Entering with count = 0x%llx, start_lcn = 0x%llx, "
|
||||
"zone = %s_ZONE.\n", (long long)count, (long long)
|
||||
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;
|
||||
}
|
||||
|
||||
/* Return empty runlist if @count == 0 */
|
||||
if (!count) {
|
||||
rl = ntfs_malloc(0x1000);
|
||||
if (!rl)
|
||||
return NULL;
|
||||
rl[0].vcn = start_vcn;
|
||||
rl[0].lcn = LCN_RL_NOT_MAPPED;
|
||||
rl[0].length = 0;
|
||||
return rl;
|
||||
}
|
||||
|
||||
/* Allocate memory. */
|
||||
buf = ntfs_malloc(NTFS_LCNALLOC_BSIZE);
|
||||
if (!buf)
|
||||
return NULL;
|
||||
/*
|
||||
* If no specific @start_lcn was requested, use the current data zone
|
||||
* position, otherwise use the requested @start_lcn but make sure it
|
||||
* lies outside the mft zone. Also set done_zones to 0 (no zones done)
|
||||
* and pass depending on whether we are starting inside a zone (1) or
|
||||
* at the beginning of a zone (2). If requesting from the MFT_ZONE,
|
||||
* we either start at the current position within the mft zone or at
|
||||
* the specified position. If the latter is out of bounds then we start
|
||||
* at the beginning of the MFT_ZONE.
|
||||
*/
|
||||
done_zones = 0;
|
||||
pass = 1;
|
||||
/*
|
||||
* zone_start and zone_end are the current search range. search_zone
|
||||
* is 1 for mft zone, 2 for data zone 1 (end of mft zone till end of
|
||||
* volume) and 4 for data zone 2 (start of volume till start of mft
|
||||
* zone).
|
||||
*/
|
||||
zone_start = start_lcn;
|
||||
if (zone_start < 0) {
|
||||
if (zone == DATA_ZONE)
|
||||
zone_start = vol->data1_zone_pos;
|
||||
else
|
||||
zone_start = vol->mft_zone_pos;
|
||||
if (!zone_start) {
|
||||
/*
|
||||
* Zone starts at beginning of volume which means a
|
||||
* single pass is sufficient.
|
||||
*/
|
||||
pass = 2;
|
||||
}
|
||||
} else if (zone == DATA_ZONE && zone_start >= vol->mft_zone_start &&
|
||||
zone_start < vol->mft_zone_end) {
|
||||
zone_start = vol->mft_zone_end;
|
||||
/*
|
||||
* Starting at beginning of data1_zone which means a single
|
||||
* pass in this zone is sufficient.
|
||||
*/
|
||||
pass = 2;
|
||||
} else if (zone == MFT_ZONE && (zone_start < vol->mft_zone_start ||
|
||||
zone_start >= vol->mft_zone_end)) {
|
||||
zone_start = vol->mft_lcn;
|
||||
if (!vol->mft_zone_end)
|
||||
zone_start = 0;
|
||||
/*
|
||||
* Starting at beginning of volume which means a single pass
|
||||
* is sufficient.
|
||||
*/
|
||||
pass = 2;
|
||||
}
|
||||
if (zone == MFT_ZONE) {
|
||||
zone_end = vol->mft_zone_end;
|
||||
search_zone = 1;
|
||||
} else /* if (zone == DATA_ZONE) */ {
|
||||
/* Skip searching the mft zone. */
|
||||
done_zones |= 1;
|
||||
if (zone_start >= vol->mft_zone_end) {
|
||||
zone_end = vol->nr_clusters;
|
||||
search_zone = 2;
|
||||
} else {
|
||||
zone_end = vol->mft_zone_start;
|
||||
search_zone = 4;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* bmp_pos is the current bit position inside the bitmap. We use
|
||||
* bmp_initial_pos to determine whether or not to do a zone switch.
|
||||
*/
|
||||
bmp_pos = bmp_initial_pos = zone_start;
|
||||
|
||||
/* Loop until all clusters are allocated, i.e. clusters == 0. */
|
||||
clusters = count;
|
||||
rlpos = rlsize = 0;
|
||||
while (1) {
|
||||
ntfs_log_trace("Start of outer while loop: done_zones = 0x%x, "
|
||||
"search_zone = %i, pass = %i, zone_start = "
|
||||
"0x%llx, zone_end = 0x%llx, bmp_initial_pos = "
|
||||
"0x%llx, bmp_pos = 0x%llx, rlpos = %i, rlsize = "
|
||||
"%i.\n", done_zones, search_zone, pass,
|
||||
(long long)zone_start, (long long)zone_end,
|
||||
(long long)bmp_initial_pos, (long long)bmp_pos,
|
||||
rlpos, rlsize);
|
||||
/* Loop until we run out of free clusters. */
|
||||
last_read_pos = bmp_pos >> 3;
|
||||
ntfs_log_trace("last_read_pos = 0x%llx.\n", (long long)last_read_pos);
|
||||
br = ntfs_attr_pread(vol->lcnbmp_na, last_read_pos, NTFS_LCNALLOC_BSIZE, buf);
|
||||
if (br <= 0) {
|
||||
if (!br) {
|
||||
/* Reached end of attribute. */
|
||||
ntfs_log_trace("End of attribute reached. Skipping "
|
||||
"to zone_pass_done.\n");
|
||||
goto zone_pass_done;
|
||||
}
|
||||
err = errno;
|
||||
ntfs_log_perror("ntfs_attr_pread() failed");
|
||||
goto err_ret;
|
||||
}
|
||||
/*
|
||||
* We might have read less than NTFS_LCNALLOC_BSIZE bytes if we are close to
|
||||
* the end of the attribute.
|
||||
*/
|
||||
buf_size = (int)br << 3;
|
||||
lcn = bmp_pos & 7;
|
||||
bmp_pos &= ~7;
|
||||
need_writeback = 0;
|
||||
ntfs_log_trace("Before inner while loop: buf_size = %i, lcn = "
|
||||
"0x%llx, bmp_pos = 0x%llx, need_writeback = %i.\n",
|
||||
buf_size, (long long)lcn, (long long)bmp_pos,
|
||||
need_writeback);
|
||||
while (lcn < buf_size && lcn + bmp_pos < zone_end) {
|
||||
byte = buf + (lcn >> 3);
|
||||
ntfs_log_trace("In inner while loop: buf_size = %i, lcn = "
|
||||
"0x%llx, bmp_pos = 0x%llx, "
|
||||
"need_writeback = %i, byte ofs = 0x%x, "
|
||||
"*byte = 0x%x.\n", buf_size,
|
||||
(long long)lcn, (long long)bmp_pos,
|
||||
need_writeback, (unsigned int)(lcn >> 3),
|
||||
(unsigned int)*byte);
|
||||
/* Skip full bytes. */
|
||||
if (*byte == 0xff) {
|
||||
lcn = (lcn + 8) & ~7;
|
||||
ntfs_log_trace("continuing while loop 1.\n");
|
||||
if (first_try) {
|
||||
first_try = 0;
|
||||
lcn += NTFS_LCNALLOC_SKIP;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
bit = 1 << (lcn & 7);
|
||||
ntfs_log_trace("bit = %i.\n", bit);
|
||||
/* If the bit is already set, go onto the next one. */
|
||||
if (*byte & bit) {
|
||||
lcn++;
|
||||
ntfs_log_trace("continuing while loop 2.\n");
|
||||
if (first_try) {
|
||||
first_try = 0;
|
||||
lcn += NTFS_LCNALLOC_SKIP;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
/* Reallocate memory if necessary. */
|
||||
if ((rlpos + 2) * (int)sizeof(runlist) >= rlsize) {
|
||||
ntfs_log_trace("Reallocating space.\n");
|
||||
if (!rl)
|
||||
ntfs_log_trace("First free bit is at LCN = "
|
||||
"0x%llx.\n", (long long)(lcn + bmp_pos));
|
||||
rlsize += 4096;
|
||||
trl = (runlist*)realloc(rl, rlsize);
|
||||
if (!trl) {
|
||||
err = ENOMEM;
|
||||
ntfs_log_perror("Failed to allocate memory");
|
||||
goto wb_err_ret;
|
||||
}
|
||||
rl = trl;
|
||||
ntfs_log_trace("Reallocated memory, rlsize = "
|
||||
"0x%x.\n", rlsize);
|
||||
}
|
||||
/* Allocate the bitmap bit. */
|
||||
*byte |= bit;
|
||||
/* We need to write this bitmap buffer back to disk! */
|
||||
need_writeback = 1;
|
||||
ntfs_log_trace("*byte = 0x%x, need_writeback is set.\n",
|
||||
(unsigned int)*byte);
|
||||
/*
|
||||
* Coalesce with previous run if adjacent LCNs.
|
||||
* Otherwise, append a new run.
|
||||
*/
|
||||
ntfs_log_trace("Adding run (lcn 0x%llx, len 0x%llx), "
|
||||
"prev_lcn = 0x%llx, lcn = 0x%llx, "
|
||||
"bmp_pos = 0x%llx, prev_run_len = "
|
||||
"0x%llx, rlpos = %i.\n",
|
||||
(long long)(lcn + bmp_pos), 1LL,
|
||||
(long long)prev_lcn, (long long)lcn,
|
||||
(long long)bmp_pos,
|
||||
(long long)prev_run_len, rlpos);
|
||||
if (prev_lcn == lcn + bmp_pos - prev_run_len && rlpos) {
|
||||
ntfs_log_trace("Coalescing to run (lcn 0x%llx, len "
|
||||
"0x%llx).\n",
|
||||
(long long)rl[rlpos - 1].lcn,
|
||||
(long long) rl[rlpos - 1].length);
|
||||
rl[rlpos - 1].length = ++prev_run_len;
|
||||
ntfs_log_trace("Run now (lcn 0x%llx, len 0x%llx), "
|
||||
"prev_run_len = 0x%llx.\n",
|
||||
(long long)rl[rlpos - 1].lcn,
|
||||
(long long)rl[rlpos - 1].length,
|
||||
(long long)prev_run_len);
|
||||
} else {
|
||||
if (rlpos) {
|
||||
ntfs_log_trace("Adding new run, (previous "
|
||||
"run lcn 0x%llx, len 0x%llx).\n",
|
||||
(long long) rl[rlpos - 1].lcn,
|
||||
(long long) rl[rlpos - 1].length);
|
||||
rl[rlpos].vcn = rl[rlpos - 1].vcn +
|
||||
prev_run_len;
|
||||
} else {
|
||||
ntfs_log_trace("Adding new run, is first run.\n");
|
||||
rl[rlpos].vcn = start_vcn;
|
||||
}
|
||||
rl[rlpos].lcn = prev_lcn = lcn + bmp_pos;
|
||||
rl[rlpos].length = prev_run_len = 1;
|
||||
rlpos++;
|
||||
}
|
||||
/* Done? */
|
||||
if (!--clusters) {
|
||||
if (ntfs_cluster_update_zone_pos(vol,
|
||||
search_zone, lcn + bmp_pos + 1
|
||||
+ NTFS_LCNALLOC_SKIP,
|
||||
bmp_initial_pos)) {
|
||||
free(rl);
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
goto done_ret;
|
||||
}
|
||||
lcn++;
|
||||
}
|
||||
bmp_pos += buf_size;
|
||||
ntfs_log_trace("After inner while loop: buf_size = 0x%x, lcn = "
|
||||
"0x%llx, bmp_pos = 0x%llx, need_writeback = %i.\n",
|
||||
buf_size, (long long)lcn,
|
||||
(long long)bmp_pos, need_writeback);
|
||||
if (need_writeback) {
|
||||
s64 bw;
|
||||
ntfs_log_trace("Writing back.\n");
|
||||
need_writeback = 0;
|
||||
bw = ntfs_attr_pwrite(vol->lcnbmp_na, last_read_pos,
|
||||
br, buf);
|
||||
if (bw != br) {
|
||||
if (bw == -1)
|
||||
err = errno;
|
||||
else
|
||||
err = EIO;
|
||||
ntfs_log_perror("Bitmap writeback failed in "
|
||||
"read next buffer code path");
|
||||
goto err_ret;
|
||||
}
|
||||
}
|
||||
if (bmp_pos < zone_end) {
|
||||
ntfs_log_trace("Continuing outer while loop, bmp_pos = "
|
||||
"0x%llx, zone_end = 0x%llx.\n",
|
||||
(long long)bmp_pos,
|
||||
(long long)zone_end);
|
||||
continue;
|
||||
}
|
||||
zone_pass_done: /* Finished with the current zone pass. */
|
||||
ntfs_log_trace("At zone_pass_done, pass = %i.\n", pass);
|
||||
if (pass == 1) {
|
||||
/*
|
||||
* Now do pass 2, scanning the first part of the zone
|
||||
* we omitted in pass 1.
|
||||
*/
|
||||
pass = 2;
|
||||
zone_end = zone_start;
|
||||
switch (search_zone) {
|
||||
case 1: /* mft_zone */
|
||||
zone_start = vol->mft_zone_start;
|
||||
break;
|
||||
case 2: /* data1_zone */
|
||||
zone_start = vol->mft_zone_end;
|
||||
break;
|
||||
case 4: /* data2_zone */
|
||||
zone_start = 0;
|
||||
break;
|
||||
default:
|
||||
NTFS_BUG("switch (search_zone) 2");
|
||||
}
|
||||
/* Sanity check. */
|
||||
if (zone_end < zone_start)
|
||||
zone_end = zone_start;
|
||||
bmp_pos = zone_start;
|
||||
ntfs_log_trace("Continuing outer while loop, pass = 2, "
|
||||
"zone_start = 0x%llx, zone_end = "
|
||||
"0x%llx, bmp_pos = 0x%llx.\n",
|
||||
zone_start, zone_end, bmp_pos);
|
||||
continue;
|
||||
} /* pass == 2 */
|
||||
done_zones_check:
|
||||
ntfs_log_trace("At done_zones_check, search_zone = %i, done_zones "
|
||||
"before = 0x%x, done_zones after = 0x%x.\n",
|
||||
search_zone, done_zones, done_zones | search_zone);
|
||||
done_zones |= search_zone;
|
||||
if (done_zones < 7) {
|
||||
ntfs_log_trace("Switching zone.\n");
|
||||
/* Now switch to the next zone we haven't done yet. */
|
||||
pass = 1;
|
||||
if (rlpos) {
|
||||
LCN tc;
|
||||
|
||||
tc = rl[rlpos - 1].lcn + rl[rlpos - 1].length
|
||||
+ NTFS_LCNALLOC_SKIP;
|
||||
|
||||
if (ntfs_cluster_update_zone_pos(vol,
|
||||
search_zone, tc, bmp_initial_pos))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (search_zone) {
|
||||
case 1:
|
||||
ntfs_log_trace("Zone switch: mft -> data1\n");
|
||||
switch_to_data1_zone: search_zone = 2;
|
||||
zone_start = bmp_initial_pos =
|
||||
vol->data1_zone_pos;
|
||||
zone_end = vol->nr_clusters;
|
||||
if (zone_start == vol->mft_zone_end)
|
||||
pass = 2;
|
||||
if (zone_start >= zone_end) {
|
||||
vol->data1_zone_pos = zone_start =
|
||||
vol->mft_zone_end;
|
||||
pass = 2;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
ntfs_log_trace("Zone switch: data1 -> data2\n");
|
||||
search_zone = 4;
|
||||
zone_start = bmp_initial_pos =
|
||||
vol->data2_zone_pos;
|
||||
zone_end = vol->mft_zone_start;
|
||||
if (!zone_start)
|
||||
pass = 2;
|
||||
if (zone_start >= zone_end) {
|
||||
vol->data2_zone_pos = zone_start =
|
||||
bmp_initial_pos = 0;
|
||||
pass = 2;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
ntfs_log_trace("Zone switch: data2 -> data1\n");
|
||||
goto switch_to_data1_zone; /* See above. */
|
||||
default:
|
||||
NTFS_BUG("switch (search_zone) 3");
|
||||
}
|
||||
ntfs_log_trace("After zone switch, search_zone = %i, pass = "
|
||||
"%i, bmp_initial_pos = 0x%llx, "
|
||||
"zone_start = 0x%llx, zone_end = "
|
||||
"0x%llx.\n", search_zone, pass,
|
||||
(long long)bmp_initial_pos,
|
||||
(long long)zone_start,
|
||||
(long long)zone_end);
|
||||
bmp_pos = zone_start;
|
||||
if (zone_start == zone_end) {
|
||||
ntfs_log_trace("Empty zone, going to "
|
||||
"done_zones_check.\n");
|
||||
/* Empty zone. Don't bother searching it. */
|
||||
goto done_zones_check;
|
||||
}
|
||||
ntfs_log_trace("Continuing outer while loop.\n");
|
||||
continue;
|
||||
} /* done_zones == 7 */
|
||||
ntfs_log_trace("All zones are finished.\n");
|
||||
/*
|
||||
* All zones are finished! If DATA_ZONE, shrink mft zone. If
|
||||
* MFT_ZONE, we have really run out of space.
|
||||
*/
|
||||
mft_zone_size = vol->mft_zone_end - vol->mft_zone_start;
|
||||
ntfs_log_trace("vol->mft_zone_start = 0x%llx, vol->mft_zone_end = "
|
||||
"0x%llx, mft_zone_size = 0x%llx.\n",
|
||||
(long long)vol->mft_zone_start,
|
||||
(long long)vol->mft_zone_end,
|
||||
(long long)mft_zone_size);
|
||||
if (zone == MFT_ZONE || mft_zone_size <= 0) {
|
||||
ntfs_log_trace("No free clusters left, going to err_ret.\n");
|
||||
/* Really no more space left on device. */
|
||||
err = ENOSPC;
|
||||
goto err_ret;
|
||||
} /* zone == DATA_ZONE && mft_zone_size > 0 */
|
||||
ntfs_log_trace("Shrinking mft zone.\n");
|
||||
zone_end = vol->mft_zone_end;
|
||||
mft_zone_size >>= 1;
|
||||
if (mft_zone_size > 0)
|
||||
vol->mft_zone_end = vol->mft_zone_start + mft_zone_size;
|
||||
else /* mft zone and data2 zone no longer exist. */
|
||||
vol->data2_zone_pos = vol->mft_zone_start =
|
||||
vol->mft_zone_end = 0;
|
||||
if (vol->mft_zone_pos >= vol->mft_zone_end) {
|
||||
vol->mft_zone_pos = vol->mft_lcn;
|
||||
if (!vol->mft_zone_end)
|
||||
vol->mft_zone_pos = 0;
|
||||
}
|
||||
bmp_pos = zone_start = bmp_initial_pos =
|
||||
vol->data1_zone_pos = vol->mft_zone_end;
|
||||
search_zone = 2;
|
||||
pass = 2;
|
||||
done_zones &= ~2;
|
||||
ntfs_log_trace("After shrinking mft zone, mft_zone_size = 0x%llx, "
|
||||
"vol->mft_zone_start = 0x%llx, "
|
||||
"vol->mft_zone_end = 0x%llx, vol->mft_zone_pos "
|
||||
"= 0x%llx, search_zone = 2, pass = 2, "
|
||||
"dones_zones = 0x%x, zone_start = 0x%llx, "
|
||||
"zone_end = 0x%llx, vol->data1_zone_pos = "
|
||||
"0x%llx, continuing outer while loop.\n",
|
||||
(long long)mft_zone_size,
|
||||
(long long)vol->mft_zone_start,
|
||||
(long long)vol->mft_zone_end,
|
||||
(long long)vol->mft_zone_pos,
|
||||
done_zones,
|
||||
(long long)zone_start,
|
||||
(long long)zone_end,
|
||||
(long long)vol->data1_zone_pos);
|
||||
}
|
||||
ntfs_log_debug("After outer while loop.\n");
|
||||
done_ret:
|
||||
ntfs_log_debug("At done_ret.\n");
|
||||
/* Add runlist terminator element. */
|
||||
rl[rlpos].vcn = rl[rlpos - 1].vcn + rl[rlpos - 1].length;
|
||||
rl[rlpos].lcn = LCN_RL_NOT_MAPPED;
|
||||
rl[rlpos].length = 0;
|
||||
if (need_writeback) {
|
||||
s64 bw;
|
||||
ntfs_log_trace("Writing back.\n");
|
||||
need_writeback = 0;
|
||||
bw = ntfs_attr_pwrite(vol->lcnbmp_na, last_read_pos, br, buf);
|
||||
if (bw != br) {
|
||||
if (bw < 0)
|
||||
err = errno;
|
||||
else
|
||||
err = EIO;
|
||||
ntfs_log_perror("Bitmap writeback failed");
|
||||
goto err_ret;
|
||||
}
|
||||
}
|
||||
done_err_ret:
|
||||
ntfs_log_debug("At done_err_ret (follows done_ret).\n");
|
||||
free(buf);
|
||||
/* Done! */
|
||||
if (!err)
|
||||
return rl;
|
||||
ntfs_log_perror("Failed to allocate clusters");
|
||||
errno = err;
|
||||
return NULL;
|
||||
wb_err_ret:
|
||||
ntfs_log_trace("At wb_err_ret.\n");
|
||||
if (need_writeback) {
|
||||
s64 bw;
|
||||
ntfs_log_trace("Writing back.\n");
|
||||
need_writeback = 0;
|
||||
bw = ntfs_attr_pwrite(vol->lcnbmp_na, last_read_pos, br, buf);
|
||||
if (bw != br) {
|
||||
if (bw < 0)
|
||||
err = errno;
|
||||
else
|
||||
err = EIO;
|
||||
ntfs_log_trace("Bitmap writeback failed in error code path "
|
||||
"with error code %i.\n", err);
|
||||
}
|
||||
}
|
||||
err_ret:
|
||||
ntfs_log_trace("At err_ret.\n");
|
||||
if (rl) {
|
||||
if (err == ENOSPC) {
|
||||
ntfs_log_trace("err = ENOSPC, first free lcn = 0x%llx, could "
|
||||
"allocate up to = 0x%llx clusters.\n",
|
||||
(long long)rl[0].lcn,
|
||||
(long long)count - clusters);
|
||||
}
|
||||
/* Add runlist terminator element. */
|
||||
rl[rlpos].vcn = rl[rlpos - 1].vcn + rl[rlpos - 1].length;
|
||||
rl[rlpos].lcn = LCN_RL_NOT_MAPPED;
|
||||
rl[rlpos].length = 0;
|
||||
/* Deallocate all allocated clusters. */
|
||||
ntfs_log_trace("Deallocating allocated clusters.\n");
|
||||
ntfs_cluster_free_from_rl(vol, rl);
|
||||
/* Free the runlist. */
|
||||
free(rl);
|
||||
rl = NULL;
|
||||
} else {
|
||||
if (err == ENOSPC) {
|
||||
ntfs_log_trace("No space left at all, err = ENOSPC, first "
|
||||
"free lcn = 0x%llx.\n",
|
||||
(long long)vol->data1_zone_pos);
|
||||
}
|
||||
}
|
||||
ntfs_log_trace("rl = NULL, going to done_err_ret.\n");
|
||||
goto done_err_ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_cluster_free_from_rl - free clusters from runlist
|
||||
* @vol: mounted ntfs volume on which to free the clusters
|
||||
* @rl: runlist from which deallocate clusters
|
||||
*
|
||||
* On success return 0 and on error return -1 with errno set to the error code.
|
||||
*/
|
||||
int ntfs_cluster_free_from_rl(ntfs_volume *vol, runlist *rl)
|
||||
{
|
||||
ntfs_log_trace("Entering.\n");
|
||||
|
||||
for (; rl->length; 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;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_cluster_free - free clusters on an ntfs volume
|
||||
* @vol: mounted ntfs volume on which to free the clusters
|
||||
* @na: attribute whose runlist describes the clusters to free
|
||||
* @start_vcn: vcn in @rl at which to start freeing clusters
|
||||
* @count: number of clusters to free or -1 for all clusters
|
||||
*
|
||||
* Free @count clusters starting at the cluster @start_vcn in the runlist
|
||||
* described by the attribute @na from the mounted ntfs volume @vol.
|
||||
*
|
||||
* If @count is -1, all clusters from @start_vcn to the end of the runlist
|
||||
* are deallocated.
|
||||
*
|
||||
* On success return the number of deallocated clusters (not counting sparse
|
||||
* clusters) and on error return -1 with errno set to the error code.
|
||||
*/
|
||||
int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, s64 count)
|
||||
{
|
||||
runlist *rl;
|
||||
s64 nr_freed, delta, to_free;
|
||||
|
||||
if (!vol || !vol->lcnbmp_na || !na || start_vcn < 0 ||
|
||||
(count < 0 && count != -1)) {
|
||||
ntfs_log_trace("Invalid arguments!\n");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
ntfs_log_trace("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;
|
||||
}
|
||||
|
||||
if (rl->lcn < 0 && rl->lcn != LCN_HOLE) {
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Find the starting cluster inside the run that needs freeing. */
|
||||
delta = start_vcn - rl->vcn;
|
||||
|
||||
/* The number of clusters in this run that need freeing. */
|
||||
to_free = rl->length - delta;
|
||||
if (count >= 0 && to_free > count)
|
||||
to_free = 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. */
|
||||
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;
|
||||
if (count >= 0)
|
||||
count -= to_free;
|
||||
|
||||
/*
|
||||
* Loop over the remaining runs, using @count as a capping value, and
|
||||
* free them.
|
||||
*/
|
||||
for (; rl->length && count != 0; ++rl) {
|
||||
// FIXME: Need to try ntfs_attr_map_runlist() for attribute
|
||||
// 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;
|
||||
}
|
||||
|
||||
/* The number of clusters in this run that need freeing. */
|
||||
to_free = rl->length;
|
||||
if (count >= 0 && to_free > 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;
|
||||
}
|
||||
/* We have freed @to_free real clusters. */
|
||||
nr_freed += to_free;
|
||||
}
|
||||
|
||||
if (count >= 0)
|
||||
count -= to_free;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* Done. Return the number of actual clusters that were freed. */
|
||||
return nr_freed;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* lcnalloc.h - Exports for cluster (de)allocation. Originated from the Linux-NTFS
|
||||
* project.
|
||||
*
|
||||
* Copyright (c) 2002 Anton Altaparmakov
|
||||
* Copyright (c) 2004 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
|
||||
* 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_LCNALLOC_H
|
||||
#define _NTFS_LCNALLOC_H
|
||||
|
||||
#include "types.h"
|
||||
#include "runlist.h"
|
||||
#include "volume.h"
|
||||
|
||||
/**
|
||||
* enum NTFS_CLUSTER_ALLOCATION_ZONES -
|
||||
*/
|
||||
typedef enum {
|
||||
FIRST_ZONE = 0, /* For sanity checking. */
|
||||
MFT_ZONE = 0, /* Allocate from $MFT zone. */
|
||||
DATA_ZONE = 1, /* Allocate from $DATA zone. */
|
||||
LAST_ZONE = 1, /* For sanity checking. */
|
||||
} NTFS_CLUSTER_ALLOCATION_ZONES;
|
||||
|
||||
extern runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count,
|
||||
LCN start_lcn, const NTFS_CLUSTER_ALLOCATION_ZONES zone);
|
||||
|
||||
extern int ntfs_cluster_free_from_rl(ntfs_volume *vol, runlist *rl);
|
||||
|
||||
extern int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn,
|
||||
s64 count);
|
||||
|
||||
#endif /* defined _NTFS_LCNALLOC_H */
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* list.h - Linked list implementation. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2000-2002 Anton Altaparmakov and others
|
||||
*
|
||||
* 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_LIST_H
|
||||
#define _NTFS_LIST_H
|
||||
|
||||
/**
|
||||
* struct list_head - Simple doubly linked list implementation.
|
||||
*
|
||||
* Copied from Linux kernel 2.4.2-ac18 into Linux-NTFS (with minor
|
||||
* modifications). - AIA
|
||||
*
|
||||
* Some of the internal functions ("__xxx") are useful when
|
||||
* manipulating whole lists rather than single entries, as
|
||||
* sometimes we already know the next/prev entries and we can
|
||||
* generate better code by using them directly rather than
|
||||
* using the generic single-entry routines.
|
||||
*/
|
||||
struct list_head {
|
||||
struct list_head *next, *prev;
|
||||
};
|
||||
|
||||
#define LIST_HEAD_INIT(name) { &(name), &(name) }
|
||||
|
||||
#define LIST_HEAD(name) \
|
||||
struct list_head name = LIST_HEAD_INIT(name)
|
||||
|
||||
#define INIT_LIST_HEAD(ptr) do { \
|
||||
(ptr)->next = (ptr); (ptr)->prev = (ptr); \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* __list_add - Insert a new entry between two known consecutive entries.
|
||||
* @new:
|
||||
* @prev:
|
||||
* @next:
|
||||
*
|
||||
* This is only for internal list manipulation where we know the prev/next
|
||||
* entries already!
|
||||
*/
|
||||
static void __list_add(struct list_head * new,
|
||||
struct list_head * prev, struct list_head * next)
|
||||
{
|
||||
next->prev = new;
|
||||
new->next = next;
|
||||
new->prev = prev;
|
||||
prev->next = new;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_add - add a new entry
|
||||
* @new: new entry to be added
|
||||
* @head: list head to add it after
|
||||
*
|
||||
* Insert a new entry after the specified head.
|
||||
* This is good for implementing stacks.
|
||||
*/
|
||||
static __inline__ void list_add(struct list_head *new, struct list_head *head)
|
||||
{
|
||||
__list_add(new, head, head->next);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_add_tail - add a new entry
|
||||
* @new: new entry to be added
|
||||
* @head: list head to add it before
|
||||
*
|
||||
* Insert a new entry before the specified head.
|
||||
* This is useful for implementing queues.
|
||||
*/
|
||||
static __inline__ void list_add_tail(struct list_head *new, struct list_head *head)
|
||||
{
|
||||
__list_add(new, head->prev, head);
|
||||
}
|
||||
|
||||
/**
|
||||
* __list_del -
|
||||
* @prev:
|
||||
* @next:
|
||||
*
|
||||
* Delete a list entry by making the prev/next entries point to each other.
|
||||
*
|
||||
* This is only for internal list manipulation where we know the prev/next
|
||||
* entries already!
|
||||
*/
|
||||
static __inline__ void __list_del(struct list_head * prev,
|
||||
struct list_head * next)
|
||||
{
|
||||
next->prev = prev;
|
||||
prev->next = next;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_del - deletes entry from list.
|
||||
* @entry: the element to delete from the list.
|
||||
*
|
||||
* Note: list_empty on entry does not return true after this, the entry is in
|
||||
* an undefined state.
|
||||
*/
|
||||
static __inline__ void list_del(struct list_head *entry)
|
||||
{
|
||||
__list_del(entry->prev, entry->next);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_del_init - deletes entry from list and reinitialize it.
|
||||
* @entry: the element to delete from the list.
|
||||
*/
|
||||
static __inline__ void list_del_init(struct list_head *entry)
|
||||
{
|
||||
__list_del(entry->prev, entry->next);
|
||||
INIT_LIST_HEAD(entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_empty - tests whether a list is empty
|
||||
* @head: the list to test.
|
||||
*/
|
||||
static __inline__ int list_empty(struct list_head *head)
|
||||
{
|
||||
return head->next == head;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_splice - join two lists
|
||||
* @list: the new list to add.
|
||||
* @head: the place to add it in the first list.
|
||||
*/
|
||||
static void list_splice(struct list_head *list,
|
||||
struct list_head *head)
|
||||
{
|
||||
struct list_head *first = list->next;
|
||||
|
||||
if (first != list) {
|
||||
struct list_head *last = list->prev;
|
||||
struct list_head *at = head->next;
|
||||
|
||||
first->prev = head;
|
||||
head->next = first;
|
||||
|
||||
last->next = at;
|
||||
at->prev = last;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* list_entry - get the struct for this entry
|
||||
* @ptr: the &struct list_head pointer.
|
||||
* @type: the type of the struct this is embedded in.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*/
|
||||
#define list_entry(ptr, type, member) \
|
||||
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
|
||||
|
||||
/**
|
||||
* list_for_each - iterate over a list
|
||||
* @pos: the &struct list_head to use as a loop counter.
|
||||
* @head: the head for your list.
|
||||
*/
|
||||
#define list_for_each(pos, head) \
|
||||
for (pos = (head)->next; pos != (head); pos = pos->next)
|
||||
|
||||
/**
|
||||
* list_for_each_safe - iterate over a list safe against removal of list entry
|
||||
* @pos: the &struct list_head to use as a loop counter.
|
||||
* @n: another &struct list_head to use as temporary storage
|
||||
* @head: the head for your list.
|
||||
*/
|
||||
#define list_for_each_safe(pos, n, head) \
|
||||
for (pos = (head)->next, n = pos->next; pos != (head); \
|
||||
pos = n, n = pos->next)
|
||||
|
||||
#endif /* defined _NTFS_LIST_H */
|
||||
|
||||
@@ -0,0 +1,762 @@
|
||||
/**
|
||||
* logfile.c - NTFS journal handling. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2002-2005 Anton Altaparmakov
|
||||
* Copyright (c) 2005 Yura Pakhuchiy
|
||||
* Copyright (c) 2005-2006 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
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file is distributed in the hope that it will be
|
||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "attrib.h"
|
||||
#include "debug.h"
|
||||
#include "logfile.h"
|
||||
#include "volume.h"
|
||||
#include "mst.h"
|
||||
#include "logging.h"
|
||||
#include "misc.h"
|
||||
|
||||
/**
|
||||
* ntfs_check_restart_page_header - check the page header for consistency
|
||||
* @rp: restart page header to check
|
||||
* @pos: position in logfile at which the restart page header resides
|
||||
*
|
||||
* Check the restart page header @rp for consistency and return TRUE if it is
|
||||
* consistent and FALSE otherwise.
|
||||
*
|
||||
* This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not
|
||||
* require the full restart page.
|
||||
*/
|
||||
static BOOL ntfs_check_restart_page_header(RESTART_PAGE_HEADER *rp, s64 pos)
|
||||
{
|
||||
u32 logfile_system_page_size, logfile_log_page_size;
|
||||
u16 ra_ofs, usa_count, usa_ofs, usa_end = 0;
|
||||
BOOL have_usa = TRUE;
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
/*
|
||||
* If the system or log page sizes are smaller than the ntfs block size
|
||||
* or either is not a power of 2 we cannot handle this log file.
|
||||
*/
|
||||
logfile_system_page_size = le32_to_cpu(rp->system_page_size);
|
||||
logfile_log_page_size = le32_to_cpu(rp->log_page_size);
|
||||
if (logfile_system_page_size < NTFS_BLOCK_SIZE ||
|
||||
logfile_log_page_size < NTFS_BLOCK_SIZE ||
|
||||
logfile_system_page_size &
|
||||
(logfile_system_page_size - 1) ||
|
||||
logfile_log_page_size & (logfile_log_page_size - 1)) {
|
||||
ntfs_log_error("$LogFile uses unsupported page size.\n");
|
||||
return FALSE;
|
||||
}
|
||||
/*
|
||||
* We must be either at !pos (1st restart page) or at pos = system page
|
||||
* size (2nd restart page).
|
||||
*/
|
||||
if (pos && pos != logfile_system_page_size) {
|
||||
ntfs_log_error("Found restart area in incorrect "
|
||||
"position in $LogFile.\n");
|
||||
return FALSE;
|
||||
}
|
||||
/* We only know how to handle version 1.1. */
|
||||
if (sle16_to_cpu(rp->major_ver) != 1 ||
|
||||
sle16_to_cpu(rp->minor_ver) != 1) {
|
||||
ntfs_log_error("$LogFile version %i.%i is not "
|
||||
"supported. (This driver supports version "
|
||||
"1.1 only.)\n", (int)sle16_to_cpu(rp->major_ver),
|
||||
(int)sle16_to_cpu(rp->minor_ver));
|
||||
return FALSE;
|
||||
}
|
||||
/*
|
||||
* If chkdsk has been run the restart page may not be protected by an
|
||||
* update sequence array.
|
||||
*/
|
||||
if (ntfs_is_chkd_record(rp->magic) && !le16_to_cpu(rp->usa_count)) {
|
||||
have_usa = FALSE;
|
||||
goto skip_usa_checks;
|
||||
}
|
||||
/* Verify the size of the update sequence array. */
|
||||
usa_count = 1 + (logfile_system_page_size >> NTFS_BLOCK_SIZE_BITS);
|
||||
if (usa_count != le16_to_cpu(rp->usa_count)) {
|
||||
ntfs_log_error("$LogFile restart page specifies "
|
||||
"inconsistent update sequence array count.\n");
|
||||
return FALSE;
|
||||
}
|
||||
/* Verify the position of the update sequence array. */
|
||||
usa_ofs = le16_to_cpu(rp->usa_ofs);
|
||||
usa_end = usa_ofs + usa_count * sizeof(u16);
|
||||
if (usa_ofs < sizeof(RESTART_PAGE_HEADER) ||
|
||||
usa_end > NTFS_BLOCK_SIZE - sizeof(u16)) {
|
||||
ntfs_log_error("$LogFile restart page specifies "
|
||||
"inconsistent update sequence array offset.\n");
|
||||
return FALSE;
|
||||
}
|
||||
skip_usa_checks:
|
||||
/*
|
||||
* Verify the position of the restart area. It must be:
|
||||
* - aligned to 8-byte boundary,
|
||||
* - after the update sequence array, and
|
||||
* - within the system page size.
|
||||
*/
|
||||
ra_ofs = le16_to_cpu(rp->restart_area_offset);
|
||||
if (ra_ofs & 7 || (have_usa ? ra_ofs < usa_end :
|
||||
ra_ofs < sizeof(RESTART_PAGE_HEADER)) ||
|
||||
ra_ofs > logfile_system_page_size) {
|
||||
ntfs_log_error("$LogFile restart page specifies "
|
||||
"inconsistent restart area offset.\n");
|
||||
return FALSE;
|
||||
}
|
||||
/*
|
||||
* Only restart pages modified by chkdsk are allowed to have chkdsk_lsn
|
||||
* set.
|
||||
*/
|
||||
if (!ntfs_is_chkd_record(rp->magic) && sle64_to_cpu(rp->chkdsk_lsn)) {
|
||||
ntfs_log_error("$LogFile restart page is not modified "
|
||||
"by chkdsk but a chkdsk LSN is specified.\n");
|
||||
return FALSE;
|
||||
}
|
||||
ntfs_log_trace("Done.\n");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_check_restart_area - check the restart area for consistency
|
||||
* @rp: restart page whose restart area to check
|
||||
*
|
||||
* Check the restart area of the restart page @rp for consistency and return
|
||||
* TRUE if it is consistent and FALSE otherwise.
|
||||
*
|
||||
* This function assumes that the restart page header has already been
|
||||
* consistency checked.
|
||||
*
|
||||
* This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not
|
||||
* require the full restart page.
|
||||
*/
|
||||
static BOOL ntfs_check_restart_area(RESTART_PAGE_HEADER *rp)
|
||||
{
|
||||
u64 file_size;
|
||||
RESTART_AREA *ra;
|
||||
u16 ra_ofs, ra_len, ca_ofs;
|
||||
u8 fs_bits;
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
ra_ofs = le16_to_cpu(rp->restart_area_offset);
|
||||
ra = (RESTART_AREA*)((u8*)rp + ra_ofs);
|
||||
/*
|
||||
* Everything before ra->file_size must be before the first word
|
||||
* protected by an update sequence number. This ensures that it is
|
||||
* safe to access ra->client_array_offset.
|
||||
*/
|
||||
if (ra_ofs + offsetof(RESTART_AREA, file_size) >
|
||||
NTFS_BLOCK_SIZE - sizeof(u16)) {
|
||||
ntfs_log_error("$LogFile restart area specifies "
|
||||
"inconsistent file offset.\n");
|
||||
return FALSE;
|
||||
}
|
||||
/*
|
||||
* Now that we can access ra->client_array_offset, make sure everything
|
||||
* up to the log client array is before the first word protected by an
|
||||
* update sequence number. This ensures we can access all of the
|
||||
* restart area elements safely. Also, the client array offset must be
|
||||
* aligned to an 8-byte boundary.
|
||||
*/
|
||||
ca_ofs = le16_to_cpu(ra->client_array_offset);
|
||||
if (((ca_ofs + 7) & ~7) != ca_ofs ||
|
||||
ra_ofs + ca_ofs > (u16)(NTFS_BLOCK_SIZE -
|
||||
sizeof(u16))) {
|
||||
ntfs_log_error("$LogFile restart area specifies "
|
||||
"inconsistent client array offset.\n");
|
||||
return FALSE;
|
||||
}
|
||||
/*
|
||||
* The restart area must end within the system page size both when
|
||||
* calculated manually and as specified by ra->restart_area_length.
|
||||
* Also, the calculated length must not exceed the specified length.
|
||||
*/
|
||||
ra_len = ca_ofs + le16_to_cpu(ra->log_clients) *
|
||||
sizeof(LOG_CLIENT_RECORD);
|
||||
if ((u32)(ra_ofs + ra_len) > le32_to_cpu(rp->system_page_size) ||
|
||||
(u32)(ra_ofs + le16_to_cpu(ra->restart_area_length)) >
|
||||
le32_to_cpu(rp->system_page_size) ||
|
||||
ra_len > le16_to_cpu(ra->restart_area_length)) {
|
||||
ntfs_log_error("$LogFile restart area is out of bounds "
|
||||
"of the system page size specified by the "
|
||||
"restart page header and/or the specified "
|
||||
"restart area length is inconsistent.\n");
|
||||
return FALSE;
|
||||
}
|
||||
/*
|
||||
* The ra->client_free_list and ra->client_in_use_list must be either
|
||||
* LOGFILE_NO_CLIENT or less than ra->log_clients or they are
|
||||
* overflowing the client array.
|
||||
*/
|
||||
if ((ra->client_free_list != LOGFILE_NO_CLIENT &&
|
||||
le16_to_cpu(ra->client_free_list) >=
|
||||
le16_to_cpu(ra->log_clients)) ||
|
||||
(ra->client_in_use_list != LOGFILE_NO_CLIENT &&
|
||||
le16_to_cpu(ra->client_in_use_list) >=
|
||||
le16_to_cpu(ra->log_clients))) {
|
||||
ntfs_log_error("$LogFile restart area specifies "
|
||||
"overflowing client free and/or in use lists.\n");
|
||||
return FALSE;
|
||||
}
|
||||
/*
|
||||
* Check ra->seq_number_bits against ra->file_size for consistency.
|
||||
* We cannot just use ffs() because the file size is not a power of 2.
|
||||
*/
|
||||
file_size = (u64)sle64_to_cpu(ra->file_size);
|
||||
fs_bits = 0;
|
||||
while (file_size) {
|
||||
file_size >>= 1;
|
||||
fs_bits++;
|
||||
}
|
||||
if (le32_to_cpu(ra->seq_number_bits) != (u32)(67 - fs_bits)) {
|
||||
ntfs_log_error("$LogFile restart area specifies "
|
||||
"inconsistent sequence number bits.\n");
|
||||
return FALSE;
|
||||
}
|
||||
/* The log record header length must be a multiple of 8. */
|
||||
if (((le16_to_cpu(ra->log_record_header_length) + 7) & ~7) !=
|
||||
le16_to_cpu(ra->log_record_header_length)) {
|
||||
ntfs_log_error("$LogFile restart area specifies "
|
||||
"inconsistent log record header length.\n");
|
||||
return FALSE;
|
||||
}
|
||||
/* Ditto for the log page data offset. */
|
||||
if (((le16_to_cpu(ra->log_page_data_offset) + 7) & ~7) !=
|
||||
le16_to_cpu(ra->log_page_data_offset)) {
|
||||
ntfs_log_error("$LogFile restart area specifies "
|
||||
"inconsistent log page data offset.\n");
|
||||
return FALSE;
|
||||
}
|
||||
ntfs_log_trace("Done.\n");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_check_log_client_array - check the log client array for consistency
|
||||
* @rp: restart page whose log client array to check
|
||||
*
|
||||
* Check the log client array of the restart page @rp for consistency and
|
||||
* return TRUE if it is consistent and FALSE otherwise.
|
||||
*
|
||||
* This function assumes that the restart page header and the restart area have
|
||||
* already been consistency checked.
|
||||
*
|
||||
* Unlike ntfs_check_restart_page_header() and ntfs_check_restart_area(), this
|
||||
* function needs @rp->system_page_size bytes in @rp, i.e. it requires the full
|
||||
* restart page and the page must be multi sector transfer deprotected.
|
||||
*/
|
||||
static BOOL ntfs_check_log_client_array(RESTART_PAGE_HEADER *rp)
|
||||
{
|
||||
RESTART_AREA *ra;
|
||||
LOG_CLIENT_RECORD *ca, *cr;
|
||||
u16 nr_clients, idx;
|
||||
BOOL in_free_list, idx_is_first;
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset));
|
||||
ca = (LOG_CLIENT_RECORD*)((u8*)ra +
|
||||
le16_to_cpu(ra->client_array_offset));
|
||||
/*
|
||||
* Check the ra->client_free_list first and then check the
|
||||
* ra->client_in_use_list. Check each of the log client records in
|
||||
* each of the lists and check that the array does not overflow the
|
||||
* ra->log_clients value. Also keep track of the number of records
|
||||
* visited as there cannot be more than ra->log_clients records and
|
||||
* that way we detect eventual loops in within a list.
|
||||
*/
|
||||
nr_clients = le16_to_cpu(ra->log_clients);
|
||||
idx = le16_to_cpu(ra->client_free_list);
|
||||
in_free_list = TRUE;
|
||||
check_list:
|
||||
for (idx_is_first = TRUE; idx != LOGFILE_NO_CLIENT_CPU; nr_clients--,
|
||||
idx = le16_to_cpu(cr->next_client)) {
|
||||
if (!nr_clients || idx >= le16_to_cpu(ra->log_clients))
|
||||
goto err_out;
|
||||
/* Set @cr to the current log client record. */
|
||||
cr = ca + idx;
|
||||
/* The first log client record must not have a prev_client. */
|
||||
if (idx_is_first) {
|
||||
if (cr->prev_client != LOGFILE_NO_CLIENT)
|
||||
goto err_out;
|
||||
idx_is_first = FALSE;
|
||||
}
|
||||
}
|
||||
/* Switch to and check the in use list if we just did the free list. */
|
||||
if (in_free_list) {
|
||||
in_free_list = FALSE;
|
||||
idx = le16_to_cpu(ra->client_in_use_list);
|
||||
goto check_list;
|
||||
}
|
||||
ntfs_log_trace("Done.\n");
|
||||
return TRUE;
|
||||
err_out:
|
||||
ntfs_log_error("$LogFile log client array is corrupt.\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_check_and_load_restart_page - check the restart page for consistency
|
||||
* @log_na: opened ntfs attribute for journal $LogFile
|
||||
* @rp: restart page to check
|
||||
* @pos: position in @log_na at which the restart page resides
|
||||
* @wrp: [OUT] copy of the multi sector transfer deprotected restart page
|
||||
* @lsn: [OUT] set to the current logfile lsn on success
|
||||
*
|
||||
* Check the restart page @rp for consistency and return 0 if it is consistent
|
||||
* and errno otherwise. The restart page may have been modified by chkdsk in
|
||||
* which case its magic is CHKD instead of RSTR.
|
||||
*
|
||||
* This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not
|
||||
* require the full restart page.
|
||||
*
|
||||
* If @wrp is not NULL, on success, *@wrp will point to a buffer containing a
|
||||
* copy of the complete multi sector transfer deprotected page. On failure,
|
||||
* *@wrp is undefined.
|
||||
*
|
||||
* Similarly, if @lsn is not NULL, on success *@lsn will be set to the current
|
||||
* logfile lsn according to this restart page. On failure, *@lsn is undefined.
|
||||
*
|
||||
* The following error codes are defined:
|
||||
* EINVAL - The restart page is inconsistent.
|
||||
* ENOMEM - Not enough memory to load the restart page.
|
||||
* EIO - Failed to reading from $LogFile.
|
||||
*/
|
||||
static int ntfs_check_and_load_restart_page(ntfs_attr *log_na,
|
||||
RESTART_PAGE_HEADER *rp, s64 pos, RESTART_PAGE_HEADER **wrp,
|
||||
LSN *lsn)
|
||||
{
|
||||
RESTART_AREA *ra;
|
||||
RESTART_PAGE_HEADER *trp;
|
||||
int err;
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
/* Check the restart page header for consistency. */
|
||||
if (!ntfs_check_restart_page_header(rp, pos)) {
|
||||
/* Error output already done inside the function. */
|
||||
return EINVAL;
|
||||
}
|
||||
/* Check the restart area for consistency. */
|
||||
if (!ntfs_check_restart_area(rp)) {
|
||||
/* Error output already done inside the function. */
|
||||
return EINVAL;
|
||||
}
|
||||
ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset));
|
||||
/*
|
||||
* Allocate a buffer to store the whole restart page so we can multi
|
||||
* sector transfer deprotect it.
|
||||
*/
|
||||
trp = ntfs_malloc(le32_to_cpu(rp->system_page_size));
|
||||
if (!trp)
|
||||
return errno;
|
||||
/*
|
||||
* Read the whole of the restart page into the buffer. If it fits
|
||||
* completely inside @rp, just copy it from there. Otherwise read it
|
||||
* from disk.
|
||||
*/
|
||||
if (le32_to_cpu(rp->system_page_size) <= NTFS_BLOCK_SIZE)
|
||||
memcpy(trp, rp, le32_to_cpu(rp->system_page_size));
|
||||
else if (ntfs_attr_pread(log_na, pos,
|
||||
le32_to_cpu(rp->system_page_size), trp) !=
|
||||
le32_to_cpu(rp->system_page_size)) {
|
||||
err = errno;
|
||||
ntfs_log_error("Failed to read whole restart page into the "
|
||||
"buffer.\n");
|
||||
if (err != ENOMEM)
|
||||
err = EIO;
|
||||
goto err_out;
|
||||
}
|
||||
/*
|
||||
* Perform the multi sector transfer deprotection on the buffer if the
|
||||
* restart page is protected.
|
||||
*/
|
||||
if ((!ntfs_is_chkd_record(trp->magic) || le16_to_cpu(trp->usa_count))
|
||||
&& ntfs_mst_post_read_fixup((NTFS_RECORD*)trp,
|
||||
le32_to_cpu(rp->system_page_size))) {
|
||||
/*
|
||||
* A multi sector tranfer error was detected. We only need to
|
||||
* abort if the restart page contents exceed the multi sector
|
||||
* transfer fixup of the first sector.
|
||||
*/
|
||||
if (le16_to_cpu(rp->restart_area_offset) +
|
||||
le16_to_cpu(ra->restart_area_length) >
|
||||
NTFS_BLOCK_SIZE - (int)sizeof(u16)) {
|
||||
ntfs_log_error("Multi sector transfer error "
|
||||
"detected in $LogFile restart page.\n");
|
||||
err = EINVAL;
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* If the restart page is modified by chkdsk or there are no active
|
||||
* logfile clients, the logfile is consistent. Otherwise, need to
|
||||
* check the log client records for consistency, too.
|
||||
*/
|
||||
err = 0;
|
||||
if (ntfs_is_rstr_record(rp->magic) &&
|
||||
ra->client_in_use_list != LOGFILE_NO_CLIENT) {
|
||||
if (!ntfs_check_log_client_array(trp)) {
|
||||
err = EINVAL;
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
if (lsn) {
|
||||
if (ntfs_is_rstr_record(rp->magic))
|
||||
*lsn = sle64_to_cpu(ra->current_lsn);
|
||||
else /* if (ntfs_is_chkd_record(rp->magic)) */
|
||||
*lsn = sle64_to_cpu(rp->chkdsk_lsn);
|
||||
}
|
||||
ntfs_log_trace("Done.\n");
|
||||
if (wrp)
|
||||
*wrp = trp;
|
||||
else {
|
||||
err_out:
|
||||
free(trp);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_check_logfile - check in the journal if the volume is consistent
|
||||
* @log_na: ntfs attribute of loaded journal $LogFile to check
|
||||
* @rp: [OUT] on success this is a copy of the current restart page
|
||||
*
|
||||
* Check the $LogFile journal for consistency and return TRUE if it is
|
||||
* consistent and FALSE if not. On success, the current restart page is
|
||||
* returned in *@rp. Caller must call ntfs_free(*@rp) when finished with it.
|
||||
*
|
||||
* At present we only check the two restart pages and ignore the log record
|
||||
* pages.
|
||||
*
|
||||
* Note that the MstProtected flag is not set on the $LogFile inode and hence
|
||||
* when reading pages they are not deprotected. This is because we do not know
|
||||
* if the $LogFile was created on a system with a different page size to ours
|
||||
* yet and mst deprotection would fail if our page size is smaller.
|
||||
*/
|
||||
BOOL ntfs_check_logfile(ntfs_attr *log_na, RESTART_PAGE_HEADER **rp)
|
||||
{
|
||||
s64 size, pos;
|
||||
LSN rstr1_lsn, rstr2_lsn;
|
||||
ntfs_volume *vol = log_na->ni->vol;
|
||||
u8 *kaddr = NULL;
|
||||
RESTART_PAGE_HEADER *rstr1_ph = NULL;
|
||||
RESTART_PAGE_HEADER *rstr2_ph = NULL;
|
||||
int log_page_size, log_page_mask, err;
|
||||
BOOL logfile_is_empty = TRUE;
|
||||
u8 log_page_bits;
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
/* An empty $LogFile must have been clean before it got emptied. */
|
||||
if (NVolLogFileEmpty(vol))
|
||||
goto is_empty;
|
||||
size = log_na->data_size;
|
||||
/* Make sure the file doesn't exceed the maximum allowed size. */
|
||||
if (size > (s64)MaxLogFileSize)
|
||||
size = MaxLogFileSize;
|
||||
log_page_size = DefaultLogPageSize;
|
||||
log_page_mask = log_page_size - 1;
|
||||
/*
|
||||
* Use generic_ffs() instead of ffs() to enable the compiler to
|
||||
* optimize log_page_size and log_page_bits into constants.
|
||||
*/
|
||||
log_page_bits = ffs(log_page_size) - 1;
|
||||
size &= ~(log_page_size - 1);
|
||||
|
||||
/*
|
||||
* Ensure the log file is big enough to store at least the two restart
|
||||
* pages and the minimum number of log record pages.
|
||||
*/
|
||||
if (size < log_page_size * 2 || (size - log_page_size * 2) >>
|
||||
log_page_bits < MinLogRecordPages) {
|
||||
ntfs_log_error("$LogFile is too small.\n");
|
||||
return FALSE;
|
||||
}
|
||||
/* Allocate memory for restart page. */
|
||||
kaddr = ntfs_malloc(NTFS_BLOCK_SIZE);
|
||||
if (!kaddr)
|
||||
return FALSE;
|
||||
/*
|
||||
* Read through the file looking for a restart page. Since the restart
|
||||
* page header is at the beginning of a page we only need to search at
|
||||
* what could be the beginning of a page (for each page size) rather
|
||||
* than scanning the whole file byte by byte. If all potential places
|
||||
* contain empty and uninitialized records, the log file can be assumed
|
||||
* to be empty.
|
||||
*/
|
||||
for (pos = 0; pos < size; pos <<= 1) {
|
||||
/*
|
||||
* Read first NTFS_BLOCK_SIZE bytes of potential restart page.
|
||||
*/
|
||||
if (ntfs_attr_pread(log_na, pos, NTFS_BLOCK_SIZE, kaddr) !=
|
||||
NTFS_BLOCK_SIZE) {
|
||||
ntfs_log_error("Failed to read first NTFS_BLOCK_SIZE "
|
||||
"bytes of potential restart page.\n");
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
/*
|
||||
* A non-empty block means the logfile is not empty while an
|
||||
* empty block after a non-empty block has been encountered
|
||||
* means we are done.
|
||||
*/
|
||||
if (!ntfs_is_empty_recordp((le32*)kaddr))
|
||||
logfile_is_empty = FALSE;
|
||||
else if (!logfile_is_empty)
|
||||
break;
|
||||
/*
|
||||
* A log record page means there cannot be a restart page after
|
||||
* this so no need to continue searching.
|
||||
*/
|
||||
if (ntfs_is_rcrd_recordp((le32*)kaddr))
|
||||
break;
|
||||
/* If not a (modified by chkdsk) restart page, continue. */
|
||||
if (!ntfs_is_rstr_recordp((le32*)kaddr) &&
|
||||
!ntfs_is_chkd_recordp((le32*)kaddr)) {
|
||||
if (!pos)
|
||||
pos = NTFS_BLOCK_SIZE >> 1;
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
* Check the (modified by chkdsk) restart page for consistency
|
||||
* and get a copy of the complete multi sector transfer
|
||||
* deprotected restart page.
|
||||
*/
|
||||
err = ntfs_check_and_load_restart_page(log_na,
|
||||
(RESTART_PAGE_HEADER*)kaddr, pos,
|
||||
!rstr1_ph ? &rstr1_ph : &rstr2_ph,
|
||||
!rstr1_ph ? &rstr1_lsn : &rstr2_lsn);
|
||||
if (!err) {
|
||||
/*
|
||||
* If we have now found the first (modified by chkdsk)
|
||||
* restart page, continue looking for the second one.
|
||||
*/
|
||||
if (!pos) {
|
||||
pos = NTFS_BLOCK_SIZE >> 1;
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
* We have now found the second (modified by chkdsk)
|
||||
* restart page, so we can stop looking.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* Error output already done inside the function. Note, we do
|
||||
* not abort if the restart page was invalid as we might still
|
||||
* find a valid one further in the file.
|
||||
*/
|
||||
if (err != EINVAL)
|
||||
goto err_out;
|
||||
/* Continue looking. */
|
||||
if (!pos)
|
||||
pos = NTFS_BLOCK_SIZE >> 1;
|
||||
}
|
||||
if (kaddr) {
|
||||
free(kaddr);
|
||||
kaddr = NULL;
|
||||
}
|
||||
if (logfile_is_empty) {
|
||||
NVolSetLogFileEmpty(vol);
|
||||
is_empty:
|
||||
ntfs_log_trace("Done. ($LogFile is empty.)\n");
|
||||
return TRUE;
|
||||
}
|
||||
if (!rstr1_ph) {
|
||||
if (rstr2_ph)
|
||||
ntfs_log_error("BUG: rstr2_ph isn't NULL!\n");
|
||||
ntfs_log_error("Did not find any restart pages in "
|
||||
"$LogFile and it was not empty.\n");
|
||||
return FALSE;
|
||||
}
|
||||
/* If both restart pages were found, use the more recent one. */
|
||||
if (rstr2_ph) {
|
||||
/*
|
||||
* If the second restart area is more recent, switch to it.
|
||||
* Otherwise just throw it away.
|
||||
*/
|
||||
if (rstr2_lsn > rstr1_lsn) {
|
||||
ntfs_log_debug("Using second restart page as it is more "
|
||||
"recent.\n");
|
||||
free(rstr1_ph);
|
||||
rstr1_ph = rstr2_ph;
|
||||
/* rstr1_lsn = rstr2_lsn; */
|
||||
} else {
|
||||
ntfs_log_debug("Using first restart page as it is more "
|
||||
"recent.\n");
|
||||
free(rstr2_ph);
|
||||
}
|
||||
rstr2_ph = NULL;
|
||||
}
|
||||
/* All consistency checks passed. */
|
||||
if (rp)
|
||||
*rp = rstr1_ph;
|
||||
else
|
||||
free(rstr1_ph);
|
||||
ntfs_log_trace("Done.\n");
|
||||
return TRUE;
|
||||
err_out:
|
||||
free(kaddr);
|
||||
free(rstr1_ph);
|
||||
free(rstr2_ph);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_is_logfile_clean - check in the journal if the volume is clean
|
||||
* @log_na: ntfs attribute of loaded journal $LogFile to check
|
||||
* @rp: copy of the current restart page
|
||||
*
|
||||
* Analyze the $LogFile journal and return TRUE if it indicates the volume was
|
||||
* shutdown cleanly and FALSE if not.
|
||||
*
|
||||
* At present we only look at the two restart pages and ignore the log record
|
||||
* pages. This is a little bit crude in that there will be a very small number
|
||||
* of cases where we think that a volume is dirty when in fact it is clean.
|
||||
* This should only affect volumes that have not been shutdown cleanly but did
|
||||
* not have any pending, non-check-pointed i/o, i.e. they were completely idle
|
||||
* at least for the five seconds preceding the unclean shutdown.
|
||||
*
|
||||
* This function assumes that the $LogFile journal has already been consistency
|
||||
* checked by a call to ntfs_check_logfile() and in particular if the $LogFile
|
||||
* is empty this function requires that NVolLogFileEmpty() is true otherwise an
|
||||
* empty volume will be reported as dirty.
|
||||
*/
|
||||
BOOL ntfs_is_logfile_clean(ntfs_attr *log_na, RESTART_PAGE_HEADER *rp)
|
||||
{
|
||||
RESTART_AREA *ra;
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
/* An empty $LogFile must have been clean before it got emptied. */
|
||||
if (NVolLogFileEmpty(log_na->ni->vol)) {
|
||||
ntfs_log_trace("Done. ($LogFile is empty.)\n");
|
||||
return TRUE;
|
||||
}
|
||||
if (!rp) {
|
||||
ntfs_log_error("Restart page header is NULL.\n");
|
||||
return FALSE;
|
||||
}
|
||||
if (!ntfs_is_rstr_record(rp->magic) &&
|
||||
!ntfs_is_chkd_record(rp->magic)) {
|
||||
ntfs_log_error("Restart page buffer is invalid. This is "
|
||||
"probably a bug in that the $LogFile should "
|
||||
"have been consistency checked before calling "
|
||||
"this function.\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset));
|
||||
/*
|
||||
* If the $LogFile has active clients, i.e. it is open, and we do not
|
||||
* have the RESTART_VOLUME_IS_CLEAN bit set in the restart area flags,
|
||||
* we assume there was an unclean shutdown.
|
||||
*/
|
||||
if (ra->client_in_use_list != LOGFILE_NO_CLIENT &&
|
||||
!(ra->flags & RESTART_VOLUME_IS_CLEAN)) {
|
||||
ntfs_log_debug("Done. $LogFile indicates a dirty shutdown.\n");
|
||||
return FALSE;
|
||||
}
|
||||
/* $LogFile indicates a clean shutdown. */
|
||||
ntfs_log_trace("Done. $LogFile indicates a clean shutdown.\n");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_empty_logfile - empty the contents of the $LogFile journal
|
||||
* @na: ntfs attribute of journal $LogFile to empty
|
||||
*
|
||||
* Empty the contents of the $LogFile journal @na and return 0 on success and
|
||||
* -1 on error.
|
||||
*
|
||||
* This function assumes that the $LogFile journal has already been consistency
|
||||
* checked by a call to ntfs_check_logfile() and that ntfs_is_logfile_clean()
|
||||
* has been used to ensure that the $LogFile is clean.
|
||||
*/
|
||||
int ntfs_empty_logfile(ntfs_attr *na)
|
||||
{
|
||||
s64 len, pos, count;
|
||||
char buf[NTFS_BUF_SIZE];
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
if (NVolLogFileEmpty(na->ni->vol))
|
||||
return 0;
|
||||
|
||||
/* The $DATA attribute of the $LogFile has to be non-resident. */
|
||||
if (!NAttrNonResident(na)) {
|
||||
errno = EIO;
|
||||
ntfs_log_perror("$LogFile $DATA attribute is resident!?!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Get length of $LogFile contents. */
|
||||
len = na->data_size;
|
||||
if (!len) {
|
||||
ntfs_log_debug("$LogFile has zero length, no disk write "
|
||||
"needed.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read $LogFile until its end. We do this as a check for correct
|
||||
length thus making sure we are decompressing the mapping pairs
|
||||
array correctly and hence writing below is safe as well. */
|
||||
pos = 0;
|
||||
while ((count = ntfs_attr_pread(na, pos, NTFS_BUF_SIZE, buf)) > 0)
|
||||
pos += count;
|
||||
|
||||
if (count == -1 || pos != len) {
|
||||
ntfs_log_error("Amount of $LogFile data read does not "
|
||||
"correspond to expected length!\n");
|
||||
if (count != -1)
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Fill the buffer with 0xff's. */
|
||||
memset(buf, -1, NTFS_BUF_SIZE);
|
||||
|
||||
/* Set the $DATA attribute. */
|
||||
pos = 0;
|
||||
while ((count = len - pos) > 0) {
|
||||
if (count > NTFS_BUF_SIZE)
|
||||
count = NTFS_BUF_SIZE;
|
||||
|
||||
if ((count = ntfs_attr_pwrite(na, pos, count, buf)) <= 0) {
|
||||
ntfs_log_perror("Failed to set the $LogFile attribute "
|
||||
"value.\n");
|
||||
if (count != -1)
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
pos += count;
|
||||
}
|
||||
|
||||
/* Set the flag so we do not have to do it again on remount. */
|
||||
NVolSetLogFileEmpty(na->ni->vol);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,394 @@
|
||||
/*
|
||||
* logfile.h - Exports for $LogFile handling. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2000-2005 Anton Altaparmakov
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file is distributed in the hope that it will be
|
||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_LOGFILE_H
|
||||
#define _NTFS_LOGFILE_H
|
||||
|
||||
#include "types.h"
|
||||
#include "endians.h"
|
||||
#include "layout.h"
|
||||
|
||||
/*
|
||||
* Journal ($LogFile) organization:
|
||||
*
|
||||
* Two restart areas present in the first two pages (restart pages, one restart
|
||||
* area in each page). When the volume is dismounted they should be identical,
|
||||
* except for the update sequence array which usually has a different update
|
||||
* sequence number.
|
||||
*
|
||||
* These are followed by log records organized in pages headed by a log record
|
||||
* header going up to log file size. Not all pages contain log records when a
|
||||
* volume is first formatted, but as the volume ages, all records will be used.
|
||||
* When the log file fills up, the records at the beginning are purged (by
|
||||
* modifying the oldest_lsn to a higher value presumably) and writing begins
|
||||
* at the beginning of the file. Effectively, the log file is viewed as a
|
||||
* circular entity.
|
||||
*
|
||||
* NOTE: Windows NT, 2000, and XP all use log file version 1.1 but they accept
|
||||
* versions <= 1.x, including 0.-1. (Yes, that is a minus one in there!) We
|
||||
* probably only want to support 1.1 as this seems to be the current version
|
||||
* and we don't know how that differs from the older versions. The only
|
||||
* exception is if the journal is clean as marked by the two restart pages
|
||||
* then it doesn't matter whether we are on an earlier version. We can just
|
||||
* reinitialize the logfile and start again with version 1.1.
|
||||
*/
|
||||
|
||||
/* Some $LogFile related constants. */
|
||||
#define MaxLogFileSize 0x100000000ULL
|
||||
#define DefaultLogPageSize 4096
|
||||
#define MinLogRecordPages 48
|
||||
|
||||
/**
|
||||
* struct RESTART_PAGE_HEADER - Log file restart page header.
|
||||
*
|
||||
* Begins the restart area.
|
||||
*/
|
||||
typedef struct {
|
||||
/*Ofs*/
|
||||
/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */
|
||||
/* 0*/ NTFS_RECORD_TYPES magic;/* The magic is "RSTR". */
|
||||
/* 4*/ le16 usa_ofs; /* See NTFS_RECORD definition in layout.h.
|
||||
When creating, set this to be immediately
|
||||
after this header structure (without any
|
||||
alignment). */
|
||||
/* 6*/ le16 usa_count; /* See NTFS_RECORD definition in layout.h. */
|
||||
|
||||
/* 8*/ leLSN chkdsk_lsn; /* The last log file sequence number found by
|
||||
chkdsk. Only used when the magic is changed
|
||||
to "CHKD". Otherwise this is zero. */
|
||||
/* 16*/ le32 system_page_size; /* Byte size of system pages when the log file
|
||||
was created, has to be >= 512 and a power of
|
||||
2. Use this to calculate the required size
|
||||
of the usa (usa_count) and add it to usa_ofs.
|
||||
Then verify that the result is less than the
|
||||
value of the restart_area_offset. */
|
||||
/* 20*/ le32 log_page_size; /* Byte size of log file pages, has to be >=
|
||||
512 and a power of 2. The default is 4096
|
||||
and is used when the system page size is
|
||||
between 4096 and 8192. Otherwise this is
|
||||
set to the system page size instead. */
|
||||
/* 24*/ le16 restart_area_offset;/* Byte offset from the start of this header to
|
||||
the RESTART_AREA. Value has to be aligned
|
||||
to 8-byte boundary. When creating, set this
|
||||
to be after the usa. */
|
||||
/* 26*/ sle16 minor_ver; /* Log file minor version. Only check if major
|
||||
version is 1. */
|
||||
/* 28*/ sle16 major_ver; /* Log file major version. We only support
|
||||
version 1.1. */
|
||||
/* sizeof() = 30 (0x1e) bytes */
|
||||
} __attribute__((__packed__)) RESTART_PAGE_HEADER;
|
||||
|
||||
/*
|
||||
* Constant for the log client indices meaning that there are no client records
|
||||
* in this particular client array. Also inside the client records themselves,
|
||||
* this means that there are no client records preceding or following this one.
|
||||
*/
|
||||
#define LOGFILE_NO_CLIENT const_cpu_to_le16(0xffff)
|
||||
#define LOGFILE_NO_CLIENT_CPU 0xffff
|
||||
|
||||
/*
|
||||
* These are the so far known RESTART_AREA_* flags (16-bit) which contain
|
||||
* information about the log file in which they are present.
|
||||
*/
|
||||
enum {
|
||||
RESTART_VOLUME_IS_CLEAN = const_cpu_to_le16(0x0002),
|
||||
RESTART_SPACE_FILLER = 0xffff, /* gcc: Force enum bit width to 16. */
|
||||
} __attribute__((__packed__));
|
||||
|
||||
typedef le16 RESTART_AREA_FLAGS;
|
||||
|
||||
/**
|
||||
* struct RESTART_AREA - Log file restart area record.
|
||||
*
|
||||
* The offset of this record is found by adding the offset of the
|
||||
* RESTART_PAGE_HEADER to the restart_area_offset value found in it.
|
||||
* See notes at restart_area_offset above.
|
||||
*/
|
||||
typedef struct {
|
||||
/*Ofs*/
|
||||
/* 0*/ leLSN current_lsn; /* The current, i.e. last LSN inside the log
|
||||
when the restart area was last written.
|
||||
This happens often but what is the interval?
|
||||
Is it just fixed time or is it every time a
|
||||
check point is written or something else?
|
||||
On create set to 0. */
|
||||
/* 8*/ le16 log_clients; /* Number of log client records in the array of
|
||||
log client records which follows this
|
||||
restart area. Must be 1. */
|
||||
/* 10*/ le16 client_free_list; /* The index of the first free log client record
|
||||
in the array of log client records.
|
||||
LOGFILE_NO_CLIENT means that there are no
|
||||
free log client records in the array.
|
||||
If != LOGFILE_NO_CLIENT, check that
|
||||
log_clients > client_free_list. On Win2k
|
||||
and presumably earlier, on a clean volume
|
||||
this is != LOGFILE_NO_CLIENT, and it should
|
||||
be 0, i.e. the first (and only) client
|
||||
record is free and thus the logfile is
|
||||
closed and hence clean. A dirty volume
|
||||
would have left the logfile open and hence
|
||||
this would be LOGFILE_NO_CLIENT. On WinXP
|
||||
and presumably later, the logfile is always
|
||||
open, even on clean shutdown so this should
|
||||
always be LOGFILE_NO_CLIENT. */
|
||||
/* 12*/ le16 client_in_use_list;/* The index of the first in-use log client
|
||||
record in the array of log client records.
|
||||
LOGFILE_NO_CLIENT means that there are no
|
||||
in-use log client records in the array. If
|
||||
!= LOGFILE_NO_CLIENT check that log_clients
|
||||
> client_in_use_list. On Win2k and
|
||||
presumably earlier, on a clean volume this
|
||||
is LOGFILE_NO_CLIENT, i.e. there are no
|
||||
client records in use and thus the logfile
|
||||
is closed and hence clean. A dirty volume
|
||||
would have left the logfile open and hence
|
||||
this would be != LOGFILE_NO_CLIENT, and it
|
||||
should be 0, i.e. the first (and only)
|
||||
client record is in use. On WinXP and
|
||||
presumably later, the logfile is always
|
||||
open, even on clean shutdown so this should
|
||||
always be 0. */
|
||||
/* 14*/ RESTART_AREA_FLAGS flags;/* Flags modifying LFS behaviour. On Win2k
|
||||
and presumably earlier this is always 0. On
|
||||
WinXP and presumably later, if the logfile
|
||||
was shutdown cleanly, the second bit,
|
||||
RESTART_VOLUME_IS_CLEAN, is set. This bit
|
||||
is cleared when the volume is mounted by
|
||||
WinXP and set when the volume is dismounted,
|
||||
thus if the logfile is dirty, this bit is
|
||||
clear. Thus we don't need to check the
|
||||
Windows version to determine if the logfile
|
||||
is clean. Instead if the logfile is closed,
|
||||
we know it must be clean. If it is open and
|
||||
this bit is set, we also know it must be
|
||||
clean. If on the other hand the logfile is
|
||||
open and this bit is clear, we can be almost
|
||||
certain that the logfile is dirty. */
|
||||
/* 16*/ le32 seq_number_bits; /* How many bits to use for the sequence
|
||||
number. This is calculated as 67 - the
|
||||
number of bits required to store the logfile
|
||||
size in bytes and this can be used in with
|
||||
the specified file_size as a consistency
|
||||
check. */
|
||||
/* 20*/ le16 restart_area_length;/* Length of the restart area including the
|
||||
client array. Following checks required if
|
||||
version matches. Otherwise, skip them.
|
||||
restart_area_offset + restart_area_length
|
||||
has to be <= system_page_size. Also,
|
||||
restart_area_length has to be >=
|
||||
client_array_offset + (log_clients *
|
||||
sizeof(log client record)). */
|
||||
/* 22*/ le16 client_array_offset;/* Offset from the start of this record to
|
||||
the first log client record if versions are
|
||||
matched. When creating, set this to be
|
||||
after this restart area structure, aligned
|
||||
to 8-bytes boundary. If the versions do not
|
||||
match, this is ignored and the offset is
|
||||
assumed to be (sizeof(RESTART_AREA) + 7) &
|
||||
~7, i.e. rounded up to first 8-byte
|
||||
boundary. Either way, client_array_offset
|
||||
has to be aligned to an 8-byte boundary.
|
||||
Also, restart_area_offset +
|
||||
client_array_offset has to be <= 510.
|
||||
Finally, client_array_offset + (log_clients
|
||||
* sizeof(log client record)) has to be <=
|
||||
system_page_size. On Win2k and presumably
|
||||
earlier, this is 0x30, i.e. immediately
|
||||
following this record. On WinXP and
|
||||
presumably later, this is 0x40, i.e. there
|
||||
are 16 extra bytes between this record and
|
||||
the client array. This probably means that
|
||||
the RESTART_AREA record is actually bigger
|
||||
in WinXP and later. */
|
||||
/* 24*/ sle64 file_size; /* Usable byte size of the log file. If the
|
||||
restart_area_offset + the offset of the
|
||||
file_size are > 510 then corruption has
|
||||
occurred. This is the very first check when
|
||||
starting with the restart_area as if it
|
||||
fails it means that some of the above values
|
||||
will be corrupted by the multi sector
|
||||
transfer protection. The file_size has to
|
||||
be rounded down to be a multiple of the
|
||||
log_page_size in the RESTART_PAGE_HEADER and
|
||||
then it has to be at least big enough to
|
||||
store the two restart pages and 48 (0x30)
|
||||
log record pages. */
|
||||
/* 32*/ le32 last_lsn_data_length;/* Length of data of last LSN, not including
|
||||
the log record header. On create set to
|
||||
0. */
|
||||
/* 36*/ le16 log_record_header_length;/* Byte size of the log record header.
|
||||
If the version matches then check that the
|
||||
value of log_record_header_length is a
|
||||
multiple of 8, i.e.
|
||||
(log_record_header_length + 7) & ~7 ==
|
||||
log_record_header_length. When creating set
|
||||
it to sizeof(LOG_RECORD_HEADER), aligned to
|
||||
8 bytes. */
|
||||
/* 38*/ le16 log_page_data_offset;/* Offset to the start of data in a log record
|
||||
page. Must be a multiple of 8. On create
|
||||
set it to immediately after the update
|
||||
sequence array of the log record page. */
|
||||
/* 40*/ le32 restart_log_open_count;/* A counter that gets incremented every
|
||||
time the logfile is restarted which happens
|
||||
at mount time when the logfile is opened.
|
||||
When creating set to a random value. Win2k
|
||||
sets it to the low 32 bits of the current
|
||||
system time in NTFS format (see time.h). */
|
||||
/* 44*/ le32 reserved; /* Reserved/alignment to 8-byte boundary. */
|
||||
/* sizeof() = 48 (0x30) bytes */
|
||||
} __attribute__((__packed__)) RESTART_AREA;
|
||||
|
||||
/**
|
||||
* struct LOG_CLIENT_RECORD - Log client record.
|
||||
*
|
||||
* The offset of this record is found by adding the offset of the
|
||||
* RESTART_AREA to the client_array_offset value found in it.
|
||||
*/
|
||||
typedef struct {
|
||||
/*Ofs*/
|
||||
/* 0*/ leLSN oldest_lsn; /* Oldest LSN needed by this client. On create
|
||||
set to 0. */
|
||||
/* 8*/ leLSN client_restart_lsn;/* LSN at which this client needs to restart
|
||||
the volume, i.e. the current position within
|
||||
the log file. At present, if clean this
|
||||
should = current_lsn in restart area but it
|
||||
probably also = current_lsn when dirty most
|
||||
of the time. At create set to 0. */
|
||||
/* 16*/ le16 prev_client; /* The offset to the previous log client record
|
||||
in the array of log client records.
|
||||
LOGFILE_NO_CLIENT means there is no previous
|
||||
client record, i.e. this is the first one.
|
||||
This is always LOGFILE_NO_CLIENT. */
|
||||
/* 18*/ le16 next_client; /* The offset to the next log client record in
|
||||
the array of log client records.
|
||||
LOGFILE_NO_CLIENT means there are no next
|
||||
client records, i.e. this is the last one.
|
||||
This is always LOGFILE_NO_CLIENT. */
|
||||
/* 20*/ le16 seq_number; /* On Win2k and presumably earlier, this is set
|
||||
to zero every time the logfile is restarted
|
||||
and it is incremented when the logfile is
|
||||
closed at dismount time. Thus it is 0 when
|
||||
dirty and 1 when clean. On WinXP and
|
||||
presumably later, this is always 0. */
|
||||
/* 22*/ u8 reserved[6]; /* Reserved/alignment. */
|
||||
/* 28*/ le32 client_name_length;/* Length of client name in bytes. Should
|
||||
always be 8. */
|
||||
/* 32*/ ntfschar client_name[64];/* Name of the client in Unicode. Should
|
||||
always be "NTFS" with the remaining bytes
|
||||
set to 0. */
|
||||
/* sizeof() = 160 (0xa0) bytes */
|
||||
} __attribute__((__packed__)) LOG_CLIENT_RECORD;
|
||||
|
||||
/**
|
||||
* struct RECORD_PAGE_HEADER - Log page record page header.
|
||||
*
|
||||
* Each log page begins with this header and is followed by several LOG_RECORD
|
||||
* structures, starting at offset 0x40 (the size of this structure and the
|
||||
* following update sequence array and then aligned to 8 byte boundary, but is
|
||||
* this specified anywhere?).
|
||||
*/
|
||||
typedef struct {
|
||||
/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */
|
||||
NTFS_RECORD_TYPES magic;/* Usually the magic is "RCRD". */
|
||||
u16 usa_ofs; /* See NTFS_RECORD definition in layout.h.
|
||||
When creating, set this to be immediately
|
||||
after this header structure (without any
|
||||
alignment). */
|
||||
u16 usa_count; /* See NTFS_RECORD definition in layout.h. */
|
||||
|
||||
union {
|
||||
LSN last_lsn;
|
||||
s64 file_offset;
|
||||
} __attribute__((__packed__)) copy;
|
||||
u32 flags;
|
||||
u16 page_count;
|
||||
u16 page_position;
|
||||
union {
|
||||
struct {
|
||||
u16 next_record_offset;
|
||||
u8 reserved[6];
|
||||
LSN last_end_lsn;
|
||||
} __attribute__((__packed__)) packed;
|
||||
} __attribute__((__packed__)) header;
|
||||
} __attribute__((__packed__)) RECORD_PAGE_HEADER;
|
||||
|
||||
/**
|
||||
* enum LOG_RECORD_FLAGS - Possible 16-bit flags for log records.
|
||||
*
|
||||
* (Or is it log record pages?)
|
||||
*/
|
||||
typedef enum {
|
||||
LOG_RECORD_MULTI_PAGE = const_cpu_to_le16(0x0001), /* ??? */
|
||||
LOG_RECORD_SIZE_PLACE_HOLDER = 0xffff,
|
||||
/* This has nothing to do with the log record. It is only so
|
||||
gcc knows to make the flags 16-bit. */
|
||||
} __attribute__((__packed__)) LOG_RECORD_FLAGS;
|
||||
|
||||
/**
|
||||
* struct LOG_CLIENT_ID - The log client id structure identifying a log client.
|
||||
*/
|
||||
typedef struct {
|
||||
u16 seq_number;
|
||||
u16 client_index;
|
||||
} __attribute__((__packed__)) LOG_CLIENT_ID;
|
||||
|
||||
/**
|
||||
* struct LOG_RECORD - Log record header.
|
||||
*
|
||||
* Each log record seems to have a constant size of 0x70 bytes.
|
||||
*/
|
||||
typedef struct {
|
||||
LSN this_lsn;
|
||||
LSN client_previous_lsn;
|
||||
LSN client_undo_next_lsn;
|
||||
u32 client_data_length;
|
||||
LOG_CLIENT_ID client_id;
|
||||
u32 record_type;
|
||||
u32 transaction_id;
|
||||
u16 flags;
|
||||
u16 reserved_or_alignment[3];
|
||||
/* Now are at ofs 0x30 into struct. */
|
||||
u16 redo_operation;
|
||||
u16 undo_operation;
|
||||
u16 redo_offset;
|
||||
u16 redo_length;
|
||||
u16 undo_offset;
|
||||
u16 undo_length;
|
||||
u16 target_attribute;
|
||||
u16 lcns_to_follow; /* Number of lcn_list entries
|
||||
following this entry. */
|
||||
/* Now at ofs 0x40. */
|
||||
u16 record_offset;
|
||||
u16 attribute_offset;
|
||||
u32 alignment_or_reserved;
|
||||
VCN target_vcn;
|
||||
/* Now at ofs 0x50. */
|
||||
struct { /* Only present if lcns_to_follow
|
||||
is not 0. */
|
||||
LCN lcn;
|
||||
} __attribute__((__packed__)) lcn_list[0];
|
||||
} __attribute__((__packed__)) LOG_RECORD;
|
||||
|
||||
extern BOOL ntfs_check_logfile(ntfs_attr *log_na, RESTART_PAGE_HEADER **rp);
|
||||
extern BOOL ntfs_is_logfile_clean(ntfs_attr *log_na, RESTART_PAGE_HEADER *rp);
|
||||
extern int ntfs_empty_logfile(ntfs_attr *na);
|
||||
|
||||
#endif /* defined _NTFS_LOGFILE_H */
|
||||
@@ -0,0 +1,647 @@
|
||||
/**
|
||||
* logging.c - Centralised logging. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2005 Richard Russon
|
||||
* Copyright (c) 2005-2006 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
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file is distributed in the hope that it will be
|
||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDIO_H
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDARG_H
|
||||
#include <stdarg.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYSLOG_H
|
||||
#include <syslog.h>
|
||||
#endif
|
||||
|
||||
#include "logging.h"
|
||||
#include "misc.h"
|
||||
|
||||
#ifndef PATH_SEP
|
||||
#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";
|
||||
|
||||
/**
|
||||
* struct ntfs_logging - Control info for the logging system
|
||||
* @levels: Bitfield of logging levels
|
||||
* @flags: Flags which affect the output style
|
||||
* @handler: Function to perform the actual logging
|
||||
*/
|
||||
struct ntfs_logging {
|
||||
u32 levels;
|
||||
u32 flags;
|
||||
ntfs_log_handler *handler;
|
||||
};
|
||||
|
||||
/**
|
||||
* ntfs_log
|
||||
* This struct controls all the logging within the library and tools.
|
||||
*/
|
||||
static struct ntfs_logging ntfs_log = {
|
||||
#ifdef DEBUG
|
||||
NTFS_LOG_LEVEL_DEBUG | NTFS_LOG_LEVEL_TRACE |
|
||||
#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 |
|
||||
NTFS_LOG_LEVEL_PROGRESS,
|
||||
NTFS_LOG_FLAG_ONLYNAME,
|
||||
#ifdef DEBUG
|
||||
ntfs_log_handler_outerr
|
||||
#else
|
||||
ntfs_log_handler_null
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* ntfs_log_get_levels - Get a list of the current logging levels
|
||||
*
|
||||
* Find out which logging levels are enabled.
|
||||
*
|
||||
* Returns: Log levels in a 32-bit field
|
||||
*/
|
||||
u32 ntfs_log_get_levels(void)
|
||||
{
|
||||
return ntfs_log.levels;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_log_set_levels - Enable extra logging levels
|
||||
* @levels: 32-bit field of log levels to set
|
||||
*
|
||||
* Enable one or more logging levels.
|
||||
* The logging levels are named: NTFS_LOG_LEVEL_*.
|
||||
*
|
||||
* Returns: Log levels that were enabled before the call
|
||||
*/
|
||||
u32 ntfs_log_set_levels(u32 levels)
|
||||
{
|
||||
u32 old;
|
||||
old = ntfs_log.levels;
|
||||
ntfs_log.levels |= levels;
|
||||
return old;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_log_clear_levels - Disable some logging levels
|
||||
* @levels: 32-bit field of log levels to clear
|
||||
*
|
||||
* Disable one or more logging levels.
|
||||
* The logging levels are named: NTFS_LOG_LEVEL_*.
|
||||
*
|
||||
* Returns: Log levels that were enabled before the call
|
||||
*/
|
||||
u32 ntfs_log_clear_levels(u32 levels)
|
||||
{
|
||||
u32 old;
|
||||
old = ntfs_log.levels;
|
||||
ntfs_log.levels &= (~levels);
|
||||
return old;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ntfs_log_get_flags - Get a list of logging style flags
|
||||
*
|
||||
* Find out which logging flags are enabled.
|
||||
*
|
||||
* Returns: Logging flags in a 32-bit field
|
||||
*/
|
||||
u32 ntfs_log_get_flags(void)
|
||||
{
|
||||
return ntfs_log.flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_log_set_flags - Enable extra logging style flags
|
||||
* @flags: 32-bit field of logging flags to set
|
||||
*
|
||||
* Enable one or more logging flags.
|
||||
* The log flags are named: NTFS_LOG_LEVEL_*.
|
||||
*
|
||||
* Returns: Logging flags that were enabled before the call
|
||||
*/
|
||||
u32 ntfs_log_set_flags(u32 flags)
|
||||
{
|
||||
u32 old;
|
||||
old = ntfs_log.flags;
|
||||
ntfs_log.flags |= flags;
|
||||
return old;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_log_clear_flags - Disable some logging styles
|
||||
* @flags: 32-bit field of logging flags to clear
|
||||
*
|
||||
* Disable one or more logging flags.
|
||||
* The log flags are named: NTFS_LOG_LEVEL_*.
|
||||
*
|
||||
* Returns: Logging flags that were enabled before the call
|
||||
*/
|
||||
u32 ntfs_log_clear_flags(u32 flags)
|
||||
{
|
||||
u32 old;
|
||||
old = ntfs_log.flags;
|
||||
ntfs_log.flags &= (~flags);
|
||||
return old;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ntfs_log_get_stream - Default output streams for logging levels
|
||||
* @level: Log level
|
||||
*
|
||||
* By default, urgent messages are sent to "stderr".
|
||||
* Other messages are sent to "stdout".
|
||||
*
|
||||
* Returns: "string" Prefix to be used
|
||||
*/
|
||||
static FILE * ntfs_log_get_stream(u32 level)
|
||||
{
|
||||
#ifdef _BEOS_
|
||||
return NULL;
|
||||
#else
|
||||
FILE *stream;
|
||||
|
||||
switch (level) {
|
||||
case NTFS_LOG_LEVEL_INFO:
|
||||
case NTFS_LOG_LEVEL_QUIET:
|
||||
case NTFS_LOG_LEVEL_PROGRESS:
|
||||
case NTFS_LOG_LEVEL_VERBOSE:
|
||||
stream = stdout;
|
||||
break;
|
||||
|
||||
case NTFS_LOG_LEVEL_DEBUG:
|
||||
case NTFS_LOG_LEVEL_TRACE:
|
||||
case NTFS_LOG_LEVEL_WARNING:
|
||||
case NTFS_LOG_LEVEL_ERROR:
|
||||
case NTFS_LOG_LEVEL_CRITICAL:
|
||||
case NTFS_LOG_LEVEL_PERROR:
|
||||
default:
|
||||
stream = stderr;
|
||||
break;
|
||||
}
|
||||
|
||||
return stream;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_log_get_prefix - Default prefixes for logging levels
|
||||
* @level: Log level to be prefixed
|
||||
*
|
||||
* Prefixing the logging output can make it easier to parse.
|
||||
*
|
||||
* Returns: "string" Prefix to be used
|
||||
*/
|
||||
static const char * ntfs_log_get_prefix(u32 level)
|
||||
{
|
||||
const char *prefix;
|
||||
|
||||
switch (level) {
|
||||
case NTFS_LOG_LEVEL_DEBUG:
|
||||
prefix = "DEBUG: ";
|
||||
break;
|
||||
case NTFS_LOG_LEVEL_TRACE:
|
||||
prefix = "TRACE: ";
|
||||
break;
|
||||
case NTFS_LOG_LEVEL_QUIET:
|
||||
prefix = "QUIET: ";
|
||||
break;
|
||||
case NTFS_LOG_LEVEL_INFO:
|
||||
prefix = "INFO: ";
|
||||
break;
|
||||
case NTFS_LOG_LEVEL_VERBOSE:
|
||||
prefix = "VERBOSE: ";
|
||||
break;
|
||||
case NTFS_LOG_LEVEL_PROGRESS:
|
||||
prefix = "PROGRESS: ";
|
||||
break;
|
||||
case NTFS_LOG_LEVEL_WARNING:
|
||||
prefix = "WARNING: ";
|
||||
break;
|
||||
case NTFS_LOG_LEVEL_ERROR:
|
||||
prefix = "ERROR: ";
|
||||
break;
|
||||
case NTFS_LOG_LEVEL_PERROR:
|
||||
prefix = "ERROR: ";
|
||||
break;
|
||||
case NTFS_LOG_LEVEL_CRITICAL:
|
||||
prefix = "CRITICAL: ";
|
||||
break;
|
||||
default:
|
||||
prefix = "";
|
||||
break;
|
||||
}
|
||||
|
||||
return prefix;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ntfs_log_set_handler - Provide an alternate logging handler
|
||||
* @handler: function to perform the logging
|
||||
*
|
||||
* This alternate handler will be called for all future logging requests.
|
||||
* If no @handler is specified, logging will revert to the default handler.
|
||||
*/
|
||||
void ntfs_log_set_handler(ntfs_log_handler *handler)
|
||||
{
|
||||
if (handler) {
|
||||
ntfs_log.handler = handler;
|
||||
#ifdef HAVE_SYSLOG_H
|
||||
if (handler == ntfs_log_handler_syslog)
|
||||
openlog("libntfs", LOG_PID, LOG_USER);
|
||||
#endif
|
||||
} else
|
||||
ntfs_log.handler = ntfs_log_handler_null;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_log_redirect - Pass on the request to the real handler
|
||||
* @function: Function in which the log line occurred
|
||||
* @file: File in which the log line occurred
|
||||
* @line: Line number on which the log line occurred
|
||||
* @level: Level at which the line is logged
|
||||
* @data: User specified data, possibly specific to a handler
|
||||
* @format: printf-style formatting string
|
||||
* @...: Arguments to be formatted
|
||||
*
|
||||
* This is just a redirector function. The arguments are simply passed to the
|
||||
* main logging handler (as defined in the global logging struct @ntfs_log).
|
||||
*
|
||||
* Returns: -1 Error occurred
|
||||
* 0 Message wasn't logged
|
||||
* num Number of output characters
|
||||
*/
|
||||
int ntfs_log_redirect(const char *function, const char *file,
|
||||
int line, u32 level, void *data, const char *format, ...)
|
||||
{
|
||||
int olderr = errno;
|
||||
int ret;
|
||||
va_list args;
|
||||
|
||||
if (!(ntfs_log.levels & level)) /* Don't log this message */
|
||||
return 0;
|
||||
|
||||
va_start(args, format);
|
||||
errno = olderr;
|
||||
ret = ntfs_log.handler(function, file, line, level, data, format, args);
|
||||
va_end(args);
|
||||
|
||||
errno = olderr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ntfs_log_handler_syslog - syslog logging handler
|
||||
* @function: Function in which the log line occurred
|
||||
* @file: File in which the log line occurred
|
||||
* @line: Line number on which the log line occurred
|
||||
* @level: Level at which the line is logged
|
||||
* @data: User specified data, possibly specific to a handler
|
||||
* @format: printf-style formatting string
|
||||
* @args: Arguments to be formatted
|
||||
*
|
||||
* A simple syslog logging handler. Ignores colors.
|
||||
*
|
||||
* Returns: -1 Error occurred
|
||||
* 0 Message wasn't logged
|
||||
* num Number of output characters
|
||||
*/
|
||||
|
||||
#ifdef HAVE_SYSLOG_H
|
||||
int ntfs_log_handler_syslog(const char *function __attribute__((unused)),
|
||||
const char *file, __attribute__((unused)) int line,
|
||||
u32 level __attribute__((unused)), void *data __attribute__((unused)),
|
||||
const char *format, va_list args)
|
||||
{
|
||||
int ret = 0;
|
||||
int olderr = errno;
|
||||
|
||||
if ((ntfs_log.flags & NTFS_LOG_FLAG_ONLYNAME) &&
|
||||
(strchr(file, PATH_SEP))) /* Abbreviate the filename */
|
||||
file = strrchr(file, PATH_SEP) + 1;
|
||||
#if 0 /* FIXME: Implement this all. */
|
||||
if (ntfs_log.flags & NTFS_LOG_FLAG_PREFIX) /* Prefix the output */
|
||||
ret += fprintf(stream, "%s", ntfs_log_get_prefix(level));
|
||||
|
||||
if (ntfs_log.flags & NTFS_LOG_FLAG_FILENAME) /* Source filename */
|
||||
ret += fprintf(stream, "%s ", file);
|
||||
|
||||
if (ntfs_log.flags & NTFS_LOG_FLAG_LINE) /* Source line number */
|
||||
ret += fprintf(stream, "(%d) ", line);
|
||||
|
||||
if ((ntfs_log.flags & NTFS_LOG_FLAG_FUNCTION) || /* Source function */
|
||||
(level & NTFS_LOG_LEVEL_TRACE))
|
||||
ret += fprintf(stream, "%s(): ", function);
|
||||
|
||||
ret += vfprintf(stream, format, args);
|
||||
|
||||
if (level & NTFS_LOG_LEVEL_PERROR) {
|
||||
if (reason)
|
||||
ret += fprintf(stream, ": %s\n", reason);
|
||||
else
|
||||
ret += fprintf(stream, ": %s\n", strerror(olderr));
|
||||
}
|
||||
#endif
|
||||
vsyslog(LOG_NOTICE, format, args);
|
||||
ret = 1; /* FIXME: caclulate how many bytes had been written. */
|
||||
|
||||
errno = olderr;
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* ntfs_log_handler_fprintf - Basic logging handler
|
||||
* @function: Function in which the log line occurred
|
||||
* @file: File in which the log line occurred
|
||||
* @line: Line number on which the log line occurred
|
||||
* @level: Level at which the line is logged
|
||||
* @data: User specified data, possibly specific to a handler
|
||||
* @format: printf-style formatting string
|
||||
* @args: Arguments to be formatted
|
||||
*
|
||||
* A simple logging handler. This is where the log line is finally displayed.
|
||||
* It is more likely that you will want to set the handler to either
|
||||
* ntfs_log_handler_outerr or ntfs_log_handler_stderr.
|
||||
*
|
||||
* Note: For this handler, @data is a pointer to a FILE output stream.
|
||||
* If @data is NULL, nothing will be displayed.
|
||||
*
|
||||
* Returns: -1 Error occurred
|
||||
* 0 Message wasn't logged
|
||||
* num Number of output characters
|
||||
*/
|
||||
int ntfs_log_handler_fprintf(const char *function, const char *file,
|
||||
int line, u32 level, void *data, const char *format, va_list args)
|
||||
{
|
||||
#ifdef _BEOS_
|
||||
return 0;
|
||||
#else
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (col_prefix)
|
||||
ret += fprintf(stream, col_prefix);
|
||||
|
||||
if ((ntfs_log.flags & NTFS_LOG_FLAG_ONLYNAME) &&
|
||||
(strchr(file, PATH_SEP))) /* Abbreviate the filename */
|
||||
file = strrchr(file, PATH_SEP) + 1;
|
||||
|
||||
if (ntfs_log.flags & NTFS_LOG_FLAG_PREFIX) /* Prefix the output */
|
||||
ret += fprintf(stream, "%s", ntfs_log_get_prefix(level));
|
||||
|
||||
if (ntfs_log.flags & NTFS_LOG_FLAG_FILENAME) /* Source filename */
|
||||
ret += fprintf(stream, "%s ", file);
|
||||
|
||||
if (ntfs_log.flags & NTFS_LOG_FLAG_LINE) /* Source line number */
|
||||
ret += fprintf(stream, "(%d) ", line);
|
||||
|
||||
if ((ntfs_log.flags & NTFS_LOG_FLAG_FUNCTION) || /* Source function */
|
||||
(level & NTFS_LOG_LEVEL_TRACE))
|
||||
ret += fprintf(stream, "%s(): ", function);
|
||||
|
||||
ret += vfprintf(stream, format, args);
|
||||
|
||||
if (level & NTFS_LOG_LEVEL_PERROR)
|
||||
ret += fprintf(stream, ": %s\n", strerror(olderr));
|
||||
|
||||
if (col_suffix)
|
||||
ret += fprintf(stream, col_suffix);
|
||||
|
||||
|
||||
fflush(stream);
|
||||
errno = olderr;
|
||||
return ret;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_log_handler_null - Null logging handler (no output)
|
||||
* @function: Function in which the log line occurred
|
||||
* @file: File in which the log line occurred
|
||||
* @line: Line number on which the log line occurred
|
||||
* @level: Level at which the line is logged
|
||||
* @data: User specified data, possibly specific to a handler
|
||||
* @format: printf-style formatting string
|
||||
* @args: Arguments to be formatted
|
||||
*
|
||||
* This handler produces no output. It provides a way to temporarily disable
|
||||
* logging, without having to change the levels and flags.
|
||||
*
|
||||
* Returns: 0 Message wasn't logged
|
||||
*/
|
||||
int ntfs_log_handler_null(const char *function __attribute__((unused)), const char *file __attribute__((unused)),
|
||||
int line __attribute__((unused)), u32 level __attribute__((unused)), void *data __attribute__((unused)),
|
||||
const char *format __attribute__((unused)), va_list args __attribute__((unused)))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_log_handler_stdout - All logs go to stdout
|
||||
* @function: Function in which the log line occurred
|
||||
* @file: File in which the log line occurred
|
||||
* @line: Line number on which the log line occurred
|
||||
* @level: Level at which the line is logged
|
||||
* @data: User specified data, possibly specific to a handler
|
||||
* @format: printf-style formatting string
|
||||
* @args: Arguments to be formatted
|
||||
*
|
||||
* Display a log message to stdout.
|
||||
*
|
||||
* Note: For this handler, @data is a pointer to a FILE output stream.
|
||||
* If @data is NULL, then stdout will be used.
|
||||
*
|
||||
* Note: This function calls ntfs_log_handler_fprintf to do the main work.
|
||||
*
|
||||
* Returns: -1 Error occurred
|
||||
* 0 Message wasn't logged
|
||||
* num Number of output characters
|
||||
*/
|
||||
int ntfs_log_handler_stdout(const char *function, const char *file,
|
||||
int line, u32 level, void *data, const char *format, va_list args)
|
||||
{
|
||||
#ifndef _BEOS_
|
||||
if (!data)
|
||||
data = stdout;
|
||||
|
||||
return ntfs_log_handler_fprintf(function, file, line, level, data, format, args);
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_log_handler_outerr - Logs go to stdout/stderr depending on level
|
||||
* @function: Function in which the log line occurred
|
||||
* @file: File in which the log line occurred
|
||||
* @line: Line number on which the log line occurred
|
||||
* @level: Level at which the line is logged
|
||||
* @data: User specified data, possibly specific to a handler
|
||||
* @format: printf-style formatting string
|
||||
* @args: Arguments to be formatted
|
||||
*
|
||||
* Display a log message. The output stream will be determined by the log
|
||||
* level.
|
||||
*
|
||||
* Note: For this handler, @data is a pointer to a FILE output stream.
|
||||
* If @data is NULL, the function ntfs_log_get_stream will be called
|
||||
*
|
||||
* Note: This function calls ntfs_log_handler_fprintf to do the main work.
|
||||
*
|
||||
* Returns: -1 Error occurred
|
||||
* 0 Message wasn't logged
|
||||
* num Number of output characters
|
||||
*/
|
||||
int ntfs_log_handler_outerr(const char *function, const char *file,
|
||||
int line, u32 level, void *data, const char *format, va_list args)
|
||||
{
|
||||
if (!data)
|
||||
data = ntfs_log_get_stream(level);
|
||||
|
||||
return ntfs_log_handler_fprintf(function, file, line, level, data, format, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_log_handler_stderr - All logs go to stderr
|
||||
* @function: Function in which the log line occurred
|
||||
* @file: File in which the log line occurred
|
||||
* @line: Line number on which the log line occurred
|
||||
* @level: Level at which the line is logged
|
||||
* @data: User specified data, possibly specific to a handler
|
||||
* @format: printf-style formatting string
|
||||
* @args: Arguments to be formatted
|
||||
*
|
||||
* Display a log message to stderr.
|
||||
*
|
||||
* Note: For this handler, @data is a pointer to a FILE output stream.
|
||||
* If @data is NULL, then stdout will be used.
|
||||
*
|
||||
* Note: This function calls ntfs_log_handler_fprintf to do the main work.
|
||||
*
|
||||
* Returns: -1 Error occurred
|
||||
* 0 Message wasn't logged
|
||||
* num Number of output characters
|
||||
*/
|
||||
int ntfs_log_handler_stderr(const char *function, const char *file,
|
||||
int line, u32 level, void *data, const char *format, va_list args)
|
||||
{
|
||||
if (!data)
|
||||
data = stderr;
|
||||
|
||||
return ntfs_log_handler_fprintf(function, file, line, level, data, format, args);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ntfs_log_parse_option - Act upon command line options
|
||||
* @option: Option flag
|
||||
*
|
||||
* Delegate some of the work of parsing the command line. All the options begin
|
||||
* with "--log-". Options cause log levels to be enabled in @ntfs_log (the
|
||||
* global logging structure).
|
||||
*
|
||||
* Note: The "colour" option changes the logging handler.
|
||||
*
|
||||
* Returns: TRUE Option understood
|
||||
* FALSE Invalid log option
|
||||
*/
|
||||
BOOL ntfs_log_parse_option(const char *option)
|
||||
{
|
||||
if (strcmp(option, "--log-debug") == 0) {
|
||||
ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG);
|
||||
return TRUE;
|
||||
} else if (strcmp(option, "--log-verbose") == 0) {
|
||||
ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE);
|
||||
return TRUE;
|
||||
} else if (strcmp(option, "--log-quiet") == 0) {
|
||||
ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET);
|
||||
return TRUE;
|
||||
} 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);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* logging.h - Centralised logging. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* 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 _LOGGING_H_
|
||||
#define _LOGGING_H_
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDARG_H
|
||||
#include <stdarg.h>
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
|
||||
/* Function prototype for the logging handlers */
|
||||
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. */
|
||||
void ntfs_log_set_handler(ntfs_log_handler *handler);
|
||||
|
||||
/* Logging handlers */
|
||||
ntfs_log_handler ntfs_log_handler_syslog __attribute__((format(printf, 6, 0)));
|
||||
ntfs_log_handler ntfs_log_handler_fprintf __attribute__((format(printf, 6, 0)));
|
||||
ntfs_log_handler ntfs_log_handler_null __attribute__((format(printf, 6, 0)));
|
||||
ntfs_log_handler ntfs_log_handler_stdout __attribute__((format(printf, 6, 0)));
|
||||
ntfs_log_handler ntfs_log_handler_outerr __attribute__((format(printf, 6, 0)));
|
||||
ntfs_log_handler ntfs_log_handler_stderr __attribute__((format(printf, 6, 0)));
|
||||
|
||||
/* Enable/disable certain log levels */
|
||||
u32 ntfs_log_set_levels(u32 levels);
|
||||
u32 ntfs_log_clear_levels(u32 levels);
|
||||
u32 ntfs_log_get_levels(void);
|
||||
|
||||
/* Enable/disable certain log flags */
|
||||
u32 ntfs_log_set_flags(u32 flags);
|
||||
u32 ntfs_log_clear_flags(u32 flags);
|
||||
u32 ntfs_log_get_flags(void);
|
||||
|
||||
/* Turn command-line options into logging flags */
|
||||
BOOL ntfs_log_parse_option(const char *option);
|
||||
|
||||
int ntfs_log_redirect(const char *function, const char *file, int line,
|
||||
u32 level, void *data, const char *format, ...)
|
||||
__attribute__((format(printf, 6, 7)));
|
||||
|
||||
/* Logging levels - Determine what gets logged */
|
||||
#define NTFS_LOG_LEVEL_DEBUG (1 << 0) /* x = 42 */
|
||||
#define NTFS_LOG_LEVEL_TRACE (1 << 1) /* Entering function x() */
|
||||
#define NTFS_LOG_LEVEL_QUIET (1 << 2) /* Quietable output */
|
||||
#define NTFS_LOG_LEVEL_INFO (1 << 3) /* Volume needs defragmenting */
|
||||
#define NTFS_LOG_LEVEL_VERBOSE (1 << 4) /* Forced to continue */
|
||||
#define NTFS_LOG_LEVEL_PROGRESS (1 << 5) /* 54% complete */
|
||||
#define NTFS_LOG_LEVEL_WARNING (1 << 6) /* You should backup before starting */
|
||||
#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 */
|
||||
|
||||
/* Logging style flags - Manage the style of the output */
|
||||
#define NTFS_LOG_FLAG_PREFIX (1 << 0) /* Prefix messages with "ERROR: ", etc */
|
||||
#define NTFS_LOG_FLAG_FILENAME (1 << 1) /* Show the file origin of the message */
|
||||
#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.
|
||||
*/
|
||||
#define ntfs_log_critical(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_CRITICAL,NULL,FORMAT,##ARGS)
|
||||
#define ntfs_log_error(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_ERROR,NULL,FORMAT,##ARGS)
|
||||
#define ntfs_log_info(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_INFO,NULL,FORMAT,##ARGS)
|
||||
#define ntfs_log_perror(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_PERROR,NULL,FORMAT,##ARGS)
|
||||
#define ntfs_log_progress(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_PROGRESS,NULL,FORMAT,##ARGS)
|
||||
#define ntfs_log_quiet(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_QUIET,NULL,FORMAT,##ARGS)
|
||||
#define ntfs_log_verbose(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_VERBOSE,NULL,FORMAT,##ARGS)
|
||||
#define ntfs_log_warning(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_WARNING,NULL,FORMAT,##ARGS)
|
||||
|
||||
/* By default debug and trace messages are compiled into the program,
|
||||
* but not displayed.
|
||||
*/
|
||||
#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)
|
||||
#else
|
||||
#define ntfs_log_debug(FORMAT, ARGS...)do {} while (0)
|
||||
#define ntfs_log_trace(FORMAT, ARGS...)do {} while (0)
|
||||
#endif /* DEBUG */
|
||||
|
||||
#endif /* _LOGGING_H_ */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* mft.h - Exports for MFT record handling. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2000-2002 Anton Altaparmakov
|
||||
* Copyright (c) 2004-2005 Richard Russon
|
||||
* Copyright (c) 2006 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
|
||||
* 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_MFT_H
|
||||
#define _NTFS_MFT_H
|
||||
|
||||
#include "volume.h"
|
||||
#include "inode.h"
|
||||
#include "layout.h"
|
||||
|
||||
extern int ntfs_mft_records_read(const ntfs_volume *vol, const MFT_REF mref,
|
||||
const s64 count, MFT_RECORD *b);
|
||||
|
||||
/**
|
||||
* ntfs_mft_record_read - read a record from the mft
|
||||
* @vol: volume to read from
|
||||
* @mref: mft record number to read
|
||||
* @b: output data buffer
|
||||
*
|
||||
* Read the mft record specified by @mref from volume @vol into buffer @b.
|
||||
* Return 0 on success or -1 on error, with errno set to the error code.
|
||||
*
|
||||
* The read mft record is mst deprotected and is hence ready to use. The caller
|
||||
* should check the record with is_baad_record() in case mst deprotection
|
||||
* failed.
|
||||
*
|
||||
* NOTE: @b has to be at least of size vol->mft_record_size.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
extern int ntfs_file_record_read(const ntfs_volume *vol, const MFT_REF mref,
|
||||
MFT_RECORD **mrec, ATTR_RECORD **attr);
|
||||
|
||||
extern int ntfs_mft_records_write(const ntfs_volume *vol, const MFT_REF mref,
|
||||
const s64 count, MFT_RECORD *b);
|
||||
|
||||
/**
|
||||
* ntfs_mft_record_write - write an mft record to disk
|
||||
* @vol: volume to write to
|
||||
* @mref: mft record number to write
|
||||
* @b: data buffer containing the mft record to write
|
||||
*
|
||||
* Write the mft record specified by @mref from buffer @b to volume @vol.
|
||||
* Return 0 on success or -1 on error, with errno set to the error code.
|
||||
*
|
||||
* Before the mft record is written, it is mst protected. After the write, it
|
||||
* is deprotected again, thus resulting in an increase in the update sequence
|
||||
* number inside the buffer @b.
|
||||
*
|
||||
* NOTE: @b has to be at least of size vol->mft_record_size.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_mft_record_get_data_size - return number of bytes used in mft record @b
|
||||
* @m: mft record to get the data size of
|
||||
*
|
||||
* Takes the mft record @m and returns the number of bytes used in the record
|
||||
* or 0 on error (i.e. @m is not a valid mft record). Zero is not a valid size
|
||||
* for an mft record as it at least has to have the MFT_RECORD itself and a
|
||||
* zero length attribute of type AT_END, thus making the minimum size 56 bytes.
|
||||
*
|
||||
* Aside: The size is independent of NTFS versions 1.x/3.x because the 8-byte
|
||||
* alignment of the first attribute mask the difference in MFT_RECORD size
|
||||
* between NTFS 1.x and 3.x. Also, you would expect every mft record to
|
||||
* contain an update sequence array as well but that could in theory be
|
||||
* non-existent (don't know if Windows' NTFS driver/chkdsk wouldn't view this
|
||||
* as corruption in itself though).
|
||||
*/
|
||||
static __inline__ u32 ntfs_mft_record_get_data_size(const MFT_RECORD *m)
|
||||
{
|
||||
if (!m || !ntfs_is_mft_record(m->magic))
|
||||
return 0;
|
||||
/* Get the number of used bytes and return it. */
|
||||
return le32_to_cpu(m->bytes_in_use);
|
||||
}
|
||||
|
||||
extern int ntfs_mft_record_layout(const ntfs_volume *vol, const MFT_REF mref,
|
||||
MFT_RECORD *mrec);
|
||||
|
||||
extern int ntfs_mft_record_format(const ntfs_volume *vol, const MFT_REF mref);
|
||||
|
||||
extern ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, ntfs_inode *base_ni);
|
||||
|
||||
extern int ntfs_mft_record_free(ntfs_volume *vol, ntfs_inode *ni);
|
||||
|
||||
extern int ntfs_mft_usn_dec(MFT_RECORD *mrec);
|
||||
|
||||
#endif /* defined _NTFS_MFT_H */
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#include "misc.h"
|
||||
#include "logging.h"
|
||||
|
||||
/**
|
||||
* ntfs_calloc
|
||||
*
|
||||
* Return a pointer to the allocated memory or NULL if the request fails.
|
||||
*/
|
||||
void *ntfs_calloc(size_t size)
|
||||
{
|
||||
void *p;
|
||||
|
||||
p = calloc(1, size);
|
||||
if (!p)
|
||||
ntfs_log_perror("Failed to calloc %lld bytes", (long long)size);
|
||||
return p;
|
||||
}
|
||||
|
||||
void *ntfs_malloc(size_t size)
|
||||
{
|
||||
void *p;
|
||||
|
||||
p = malloc(size);
|
||||
if (!p)
|
||||
ntfs_log_perror("Failed to malloc %lld bytes", (long long)size);
|
||||
return p;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef _NTFS_MISC_H_
|
||||
#define _NTFS_MISC_H_
|
||||
|
||||
void *ntfs_calloc(size_t size);
|
||||
void *ntfs_malloc(size_t size);
|
||||
|
||||
#endif /* _NTFS_MISC_H_ */
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
/**
|
||||
* mst.c - Multi sector fixup handling code. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2000-2004 Anton Altaparmakov
|
||||
* Copyright (c) 2006 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
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file is distributed in the hope that it will be
|
||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "mst.h"
|
||||
#include "logging.h"
|
||||
|
||||
/**
|
||||
* ntfs_mst_post_read_fixup - deprotect multi sector transfer protected data
|
||||
* @b: pointer to the data to deprotect
|
||||
* @size: size in bytes of @b
|
||||
*
|
||||
* Perform the necessary post read multi sector transfer fixups and detect the
|
||||
* presence of incomplete multi sector transfers. - In that case, overwrite the
|
||||
* magic of the ntfs record header being processed with "BAAD" (in memory only!)
|
||||
* and abort processing.
|
||||
*
|
||||
* Return 0 on success and -1 on error, with errno set to the error code. The
|
||||
* following error codes are defined:
|
||||
* EINVAL Invalid arguments or invalid NTFS record in buffer @b.
|
||||
* EIO Multi sector transfer error was detected. Magic of the NTFS
|
||||
* record in @b will have been set to "BAAD".
|
||||
*/
|
||||
int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size)
|
||||
{
|
||||
u16 usa_ofs, usa_count, usn;
|
||||
u16 *usa_pos, *data_pos;
|
||||
|
||||
ntfs_log_trace("Entering\n");
|
||||
|
||||
/* Setup the variables. */
|
||||
usa_ofs = le16_to_cpu(b->usa_ofs);
|
||||
/* Decrement usa_count to get number of fixups. */
|
||||
usa_count = le16_to_cpu(b->usa_count) - 1;
|
||||
/* Size and alignment checks. */
|
||||
if (size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 ||
|
||||
(u32)(usa_ofs + (usa_count * 2)) > size ||
|
||||
(size >> NTFS_BLOCK_SIZE_BITS) != usa_count) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
/* Position of usn in update sequence array. */
|
||||
usa_pos = (u16*)b + usa_ofs/sizeof(u16);
|
||||
/*
|
||||
* The update sequence number which has to be equal to each of the
|
||||
* u16 values before they are fixed up. Note no need to care for
|
||||
* endianness since we are comparing and moving data for on disk
|
||||
* structures which means the data is consistent. - If it is
|
||||
* consistency the wrong endianness it doesn't make any difference.
|
||||
*/
|
||||
usn = *usa_pos;
|
||||
/*
|
||||
* Position in protected data of first u16 that needs fixing up.
|
||||
*/
|
||||
data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1;
|
||||
/*
|
||||
* Check for incomplete multi sector transfer(s).
|
||||
*/
|
||||
while (usa_count--) {
|
||||
if (*data_pos != usn) {
|
||||
/*
|
||||
* Incomplete multi sector transfer detected! )-:
|
||||
* Set the magic to "BAAD" and return failure.
|
||||
* Note that magic_BAAD is already converted to le32.
|
||||
*/
|
||||
b->magic = magic_BAAD;
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
|
||||
}
|
||||
/* Re-setup the variables. */
|
||||
usa_count = le16_to_cpu(b->usa_count) - 1;
|
||||
data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1;
|
||||
/* Fixup all sectors. */
|
||||
while (usa_count--) {
|
||||
/*
|
||||
* Increment position in usa and restore original data from
|
||||
* the usa into the data buffer.
|
||||
*/
|
||||
*data_pos = *(++usa_pos);
|
||||
/* Increment position in data as well. */
|
||||
data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_mst_pre_write_fixup - apply multi sector transfer protection
|
||||
* @b: pointer to the data to protect
|
||||
* @size: size in bytes of @b
|
||||
*
|
||||
* Perform the necessary pre write multi sector transfer fixup on the data
|
||||
* pointer to by @b of @size.
|
||||
*
|
||||
* Return 0 if fixups applied successfully or -1 if no fixups were performed
|
||||
* due to errors. In that case errno i set to the error code (EINVAL).
|
||||
*
|
||||
* NOTE: We consider the absence / invalidity of an update sequence array to
|
||||
* mean error. This means that you have to create a valid update sequence
|
||||
* array header in the ntfs record before calling this function, otherwise it
|
||||
* will fail (the header needs to contain the position of the update sequence
|
||||
* array together with the number of elements in the array). You also need to
|
||||
* initialise the update sequence number before calling this function
|
||||
* otherwise a random word will be used (whatever was in the record at that
|
||||
* position at that time).
|
||||
*/
|
||||
int ntfs_mst_pre_write_fixup(NTFS_RECORD *b, const u32 size)
|
||||
{
|
||||
u16 usa_ofs, usa_count, usn;
|
||||
u16 *usa_pos, *data_pos;
|
||||
|
||||
ntfs_log_trace("Entering\n");
|
||||
|
||||
/* Sanity check + only fixup if it makes sense. */
|
||||
if (!b || ntfs_is_baad_record(b->magic) ||
|
||||
ntfs_is_hole_record(b->magic)) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
/* Setup the variables. */
|
||||
usa_ofs = le16_to_cpu(b->usa_ofs);
|
||||
/* Decrement usa_count to get number of fixups. */
|
||||
usa_count = le16_to_cpu(b->usa_count) - 1;
|
||||
/* Size and alignment checks. */
|
||||
if (size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 ||
|
||||
(u32)(usa_ofs + (usa_count * 2)) > size ||
|
||||
(size >> NTFS_BLOCK_SIZE_BITS) != usa_count) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
/* Position of usn in update sequence array. */
|
||||
usa_pos = (u16*)((u8*)b + usa_ofs);
|
||||
/*
|
||||
* Cyclically increment the update sequence number
|
||||
* (skipping 0 and -1, i.e. 0xffff).
|
||||
*/
|
||||
usn = le16_to_cpup(usa_pos) + 1;
|
||||
if (usn == 0xffff || !usn)
|
||||
usn = 1;
|
||||
usn = cpu_to_le16(usn);
|
||||
*usa_pos = usn;
|
||||
/* Position in data of first u16 that needs fixing up. */
|
||||
data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1;
|
||||
/* Fixup all sectors. */
|
||||
while (usa_count--) {
|
||||
/*
|
||||
* Increment the position in the usa and save the
|
||||
* original data from the data buffer into the usa.
|
||||
*/
|
||||
*(++usa_pos) = *data_pos;
|
||||
/* Apply fixup to data. */
|
||||
*data_pos = usn;
|
||||
/* Increment position in data as well. */
|
||||
data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_mst_post_write_fixup - deprotect multi sector transfer protected data
|
||||
* @b: pointer to the data to deprotect
|
||||
*
|
||||
* Perform the necessary post write multi sector transfer fixup, not checking
|
||||
* for any errors, because we assume we have just used
|
||||
* ntfs_mst_pre_write_fixup(), thus the data will be fine or we would never
|
||||
* have gotten here.
|
||||
*/
|
||||
void ntfs_mst_post_write_fixup(NTFS_RECORD *b)
|
||||
{
|
||||
u16 *usa_pos, *data_pos;
|
||||
|
||||
u16 usa_ofs = le16_to_cpu(b->usa_ofs);
|
||||
u16 usa_count = le16_to_cpu(b->usa_count) - 1;
|
||||
|
||||
ntfs_log_trace("Entering\n");
|
||||
|
||||
/* Position of usn in update sequence array. */
|
||||
usa_pos = (u16*)b + usa_ofs/sizeof(u16);
|
||||
|
||||
/* Position in protected data of first u16 that needs fixing up. */
|
||||
data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1;
|
||||
|
||||
/* Fixup all sectors. */
|
||||
while (usa_count--) {
|
||||
/*
|
||||
* Increment position in usa and restore original data from
|
||||
* the usa into the data buffer.
|
||||
*/
|
||||
*data_pos = *(++usa_pos);
|
||||
|
||||
/* Increment position in data as well. */
|
||||
data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* mst.h - Exports for multi sector transfer fixup functions.
|
||||
* Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2000-2002 Anton Altaparmakov
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file is distributed in the hope that it will be
|
||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_MST_H
|
||||
#define _NTFS_MST_H
|
||||
|
||||
#include "types.h"
|
||||
#include "layout.h"
|
||||
|
||||
extern int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size);
|
||||
extern int ntfs_mst_pre_write_fixup(NTFS_RECORD *b, const u32 size);
|
||||
extern void ntfs_mst_post_write_fixup(NTFS_RECORD *b);
|
||||
|
||||
#endif /* defined _NTFS_MST_H */
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* ntfstime.h - NTFS time related functions. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2005 Anton Altaparmakov
|
||||
* Copyright (c) 2005 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
|
||||
* 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_NTFSTIME_H
|
||||
#define _NTFS_NTFSTIME_H
|
||||
|
||||
#ifdef HAVE_TIME_H
|
||||
#include <time.h>
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define NTFS_TIME_OFFSET ((s64)(369 * 365 + 89) * 24 * 3600 * 10000000)
|
||||
|
||||
/**
|
||||
* ntfs2utc - Convert an NTFS time to Unix time
|
||||
* @ntfs_time: An NTFS time in 100ns units since 1601
|
||||
*
|
||||
* NTFS stores times as the number of 100ns intervals since January 1st 1601 at
|
||||
* 00:00 UTC. This system will not suffer from Y2K problems until ~57000AD.
|
||||
*
|
||||
* Return: n A Unix time (number of seconds since 1970)
|
||||
*/
|
||||
static __inline__ time_t ntfs2utc(s64 ntfs_time)
|
||||
{
|
||||
return (sle64_to_cpu(ntfs_time) - (NTFS_TIME_OFFSET)) / 10000000;
|
||||
}
|
||||
|
||||
/**
|
||||
* utc2ntfs - Convert Linux time to NTFS time
|
||||
* @utc_time: Linux time to convert to NTFS
|
||||
*
|
||||
* Convert the Linux time @utc_time to its corresponding NTFS time.
|
||||
*
|
||||
* Linux stores time in a long at present and measures it as the number of
|
||||
* 1-second intervals since 1st January 1970, 00:00:00 UTC.
|
||||
*
|
||||
* NTFS uses Microsoft's standard time format which is stored in a s64 and is
|
||||
* measured as the number of 100 nano-second intervals since 1st January 1601,
|
||||
* 00:00:00 UTC.
|
||||
*
|
||||
* Return: n An NTFS time (100ns units since Jan 1601)
|
||||
*/
|
||||
static __inline__ s64 utc2ntfs(time_t utc_time)
|
||||
{
|
||||
/* Convert to 100ns intervals and then add the NTFS time offset. */
|
||||
return cpu_to_sle64((s64)utc_time * 10000000 + NTFS_TIME_OFFSET);
|
||||
}
|
||||
|
||||
#endif /* _NTFS_NTFSTIME_H */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* runlist.h - Exports for runlist handling. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2002 Anton Altaparmakov
|
||||
* Copyright (c) 2002 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_RUNLIST_H
|
||||
#define _NTFS_RUNLIST_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
/* Forward declarations */
|
||||
typedef struct _runlist_element runlist_element;
|
||||
typedef runlist_element runlist;
|
||||
|
||||
#include "attrib.h"
|
||||
#include "volume.h"
|
||||
|
||||
/**
|
||||
* struct _runlist_element - in memory vcn to lcn mapping array element.
|
||||
* @vcn: starting vcn of the current array element
|
||||
* @lcn: starting lcn of the current array element
|
||||
* @length: length in clusters of the current array element
|
||||
*
|
||||
* The last vcn (in fact the last vcn + 1) is reached when length == 0.
|
||||
*
|
||||
* When lcn == -1 this means that the count vcns starting at vcn are not
|
||||
* physically allocated (i.e. this is a hole / data is sparse).
|
||||
*/
|
||||
struct _runlist_element {/* In memory vcn to lcn mapping structure element. */
|
||||
VCN vcn; /* vcn = Starting virtual cluster number. */
|
||||
LCN lcn; /* lcn = Starting logical cluster number. */
|
||||
s64 length; /* Run length in clusters. */
|
||||
};
|
||||
|
||||
extern LCN ntfs_rl_vcn_to_lcn(const runlist_element *rl, const VCN vcn);
|
||||
|
||||
extern s64 ntfs_rl_pread(const ntfs_volume *vol, const runlist_element *rl,
|
||||
const s64 pos, s64 count, void *b);
|
||||
extern s64 ntfs_rl_pwrite(const ntfs_volume *vol, const runlist_element *rl,
|
||||
const s64 pos, s64 count, void *b);
|
||||
|
||||
extern runlist_element *ntfs_runlists_merge(runlist_element *drl,
|
||||
runlist_element *srl);
|
||||
|
||||
extern runlist_element *ntfs_mapping_pairs_decompress(const ntfs_volume *vol,
|
||||
const ATTR_RECORD *attr, runlist_element *old_rl);
|
||||
|
||||
extern int ntfs_get_nr_significant_bytes(const s64 n);
|
||||
|
||||
extern int ntfs_get_size_for_mapping_pairs(const ntfs_volume *vol,
|
||||
const runlist_element *rl, const VCN start_vcn);
|
||||
|
||||
extern int ntfs_write_significant_bytes(u8 *dst, const u8 *dst_max,
|
||||
const s64 n);
|
||||
|
||||
extern int ntfs_mapping_pairs_build(const ntfs_volume *vol, u8 *dst,
|
||||
const int dst_len, const runlist_element *rl,
|
||||
const VCN start_vcn, VCN *const stop_vcn);
|
||||
|
||||
extern int ntfs_rl_truncate(runlist **arl, const VCN start_vcn);
|
||||
|
||||
extern int ntfs_rl_sparse(runlist *rl);
|
||||
extern s64 ntfs_rl_get_compressed_size(ntfs_volume *vol, runlist *rl);
|
||||
|
||||
#ifdef NTFS_TEST
|
||||
int test_rl_main(int argc, char *argv[]);
|
||||
#endif
|
||||
|
||||
#endif /* defined _NTFS_RUNLIST_H */
|
||||
|
||||
@@ -0,0 +1,355 @@
|
||||
/**
|
||||
* security.c - Handling security/ACLs in NTFS. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2004 Anton Altaparmakov
|
||||
* Copyright (c) 2005-2006 Szabolcs Szakacsits
|
||||
* Copyright (c) 2006 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
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file is distributed in the hope that it will be
|
||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDIO_H
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
#include "layout.h"
|
||||
#include "attrib.h"
|
||||
#include "security.h"
|
||||
#include "misc.h"
|
||||
|
||||
#ifdef _BEOS_
|
||||
int be_snprintf(char *buff, size_t size, const char *format, ...)
|
||||
{
|
||||
int ret;
|
||||
char temp[1024];
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
ret = sprintf(temp, format, args);
|
||||
va_end(args);
|
||||
strncpy(buff,temp,size);
|
||||
return ret;
|
||||
}
|
||||
#define snprintf be_snprintf
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The zero GUID.
|
||||
*/
|
||||
static const GUID __zero_guid = { const_cpu_to_le32(0), const_cpu_to_le16(0),
|
||||
const_cpu_to_le16(0), { 0, 0, 0, 0, 0, 0, 0, 0 } };
|
||||
const GUID *const zero_guid = &__zero_guid;
|
||||
|
||||
/**
|
||||
* ntfs_guid_is_zero - check if a GUID is zero
|
||||
* @guid: [IN] guid to check
|
||||
*
|
||||
* Return TRUE if @guid is a valid pointer to a GUID and it is the zero GUID
|
||||
* and FALSE otherwise.
|
||||
*/
|
||||
BOOL ntfs_guid_is_zero(const GUID *guid)
|
||||
{
|
||||
return (memcmp(guid, zero_guid, sizeof(*zero_guid)));
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_guid_to_mbs - convert a GUID to a multi byte string
|
||||
* @guid: [IN] guid to convert
|
||||
* @guid_str: [OUT] string in which to return the GUID (optional)
|
||||
*
|
||||
* Convert the GUID pointed to by @guid to a multi byte string of the form
|
||||
* "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX". Therefore, @guid_str (if not NULL)
|
||||
* needs to be able to store at least 37 bytes.
|
||||
*
|
||||
* If @guid_str is not NULL it will contain the converted GUID on return. If
|
||||
* it is NULL a string will be allocated and this will be returned. The caller
|
||||
* is responsible for free()ing the string in that case.
|
||||
*
|
||||
* On success return the converted string and on failure return NULL with errno
|
||||
* set to the error code.
|
||||
*/
|
||||
char *ntfs_guid_to_mbs(const GUID *guid, char *guid_str)
|
||||
{
|
||||
char *_guid_str;
|
||||
int res;
|
||||
|
||||
if (!guid) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
_guid_str = guid_str;
|
||||
if (!_guid_str) {
|
||||
_guid_str = ntfs_malloc(37);
|
||||
if (!_guid_str)
|
||||
return _guid_str;
|
||||
}
|
||||
res = snprintf(_guid_str, 37,
|
||||
"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
|
||||
(unsigned int)le32_to_cpu(guid->data1),
|
||||
le16_to_cpu(guid->data2), le16_to_cpu(guid->data3),
|
||||
guid->data4[0], guid->data4[1],
|
||||
guid->data4[2], guid->data4[3], guid->data4[4],
|
||||
guid->data4[5], guid->data4[6], guid->data4[7]);
|
||||
if (res == 36)
|
||||
return _guid_str;
|
||||
if (!guid_str)
|
||||
free(_guid_str);
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_sid_to_mbs_size - determine maximum size for the string of a SID
|
||||
* @sid: [IN] SID for which to determine the maximum string size
|
||||
*
|
||||
* Determine the maximum multi byte string size in bytes which is needed to
|
||||
* store the standard textual representation of the SID pointed to by @sid.
|
||||
* See ntfs_sid_to_mbs(), below.
|
||||
*
|
||||
* On success return the maximum number of bytes needed to store the multi byte
|
||||
* string and on failure return -1 with errno set to the error code.
|
||||
*/
|
||||
int ntfs_sid_to_mbs_size(const SID *sid)
|
||||
{
|
||||
int size, i;
|
||||
|
||||
if (!ntfs_sid_is_valid(sid)) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
/* Start with "S-". */
|
||||
size = 2;
|
||||
/*
|
||||
* Add the SID_REVISION. Hopefully the compiler will optimize this
|
||||
* away as SID_REVISION is a constant.
|
||||
*/
|
||||
for (i = SID_REVISION; i > 0; i /= 10)
|
||||
size++;
|
||||
/* Add the "-". */
|
||||
size++;
|
||||
/*
|
||||
* Add the identifier authority. If it needs to be in decimal, the
|
||||
* maximum is 2^32-1 = 4294967295 = 10 characters. If it needs to be
|
||||
* in hexadecimal, then maximum is 0x665544332211 = 14 characters.
|
||||
*/
|
||||
if (!sid->identifier_authority.high_part)
|
||||
size += 10;
|
||||
else
|
||||
size += 14;
|
||||
/*
|
||||
* Finally, add the sub authorities. For each we have a "-" followed
|
||||
* by a decimal which can be up to 2^32-1 = 4294967295 = 10 characters.
|
||||
*/
|
||||
size += (1 + 10) * sid->sub_authority_count;
|
||||
/* We need the zero byte at the end, too. */
|
||||
size++;
|
||||
return size * sizeof(char);
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_sid_to_mbs - convert a SID to a multi byte string
|
||||
* @sid: [IN] SID to convert
|
||||
* @sid_str: [OUT] string in which to return the SID (optional)
|
||||
* @sid_str_size: [IN] size in bytes of @sid_str
|
||||
*
|
||||
* Convert the SID pointed to by @sid to its standard textual representation.
|
||||
* @sid_str (if not NULL) needs to be able to store at least
|
||||
* ntfs_sid_to_mbs_size() bytes. @sid_str_size is the size in bytes of
|
||||
* @sid_str if @sid_str is not NULL.
|
||||
*
|
||||
* The standard textual representation of the SID is of the form:
|
||||
* S-R-I-S-S...
|
||||
* Where:
|
||||
* - The first "S" is the literal character 'S' identifying the following
|
||||
* digits as a SID.
|
||||
* - R is the revision level of the SID expressed as a sequence of digits
|
||||
* in decimal.
|
||||
* - I is the 48-bit identifier_authority, expressed as digits in decimal,
|
||||
* if I < 2^32, or hexadecimal prefixed by "0x", if I >= 2^32.
|
||||
* - S... is one or more sub_authority values, expressed as digits in
|
||||
* decimal.
|
||||
*
|
||||
* If @sid_str is not NULL it will contain the converted SUID on return. If it
|
||||
* is NULL a string will be allocated and this will be returned. The caller is
|
||||
* responsible for free()ing the string in that case.
|
||||
*
|
||||
* On success return the converted string and on failure return NULL with errno
|
||||
* set to the error code.
|
||||
*/
|
||||
char *ntfs_sid_to_mbs(const SID *sid, char *sid_str, size_t sid_str_size)
|
||||
{
|
||||
u64 u;
|
||||
char *s;
|
||||
int i, j, cnt;
|
||||
|
||||
/*
|
||||
* No need to check @sid if !@sid_str since ntfs_sid_to_mbs_size() will
|
||||
* check @sid, too. 8 is the minimum SID string size.
|
||||
*/
|
||||
if (sid_str && (sid_str_size < 8 || !ntfs_sid_is_valid(sid))) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
/* Allocate string if not provided. */
|
||||
if (!sid_str) {
|
||||
cnt = ntfs_sid_to_mbs_size(sid);
|
||||
if (cnt < 0)
|
||||
return NULL;
|
||||
s = ntfs_malloc(cnt);
|
||||
if (!s)
|
||||
return s;
|
||||
sid_str = s;
|
||||
/* So we know we allocated it. */
|
||||
sid_str_size = 0;
|
||||
} else {
|
||||
s = sid_str;
|
||||
cnt = sid_str_size;
|
||||
}
|
||||
/* Start with "S-R-". */
|
||||
i = snprintf(s, cnt, "S-%hhu-", (unsigned char)sid->revision);
|
||||
if (i < 0 || i >= cnt)
|
||||
goto err_out;
|
||||
s += i;
|
||||
cnt -= i;
|
||||
/* Add the identifier authority. */
|
||||
for (u = i = 0, j = 40; i < 6; i++, j -= 8)
|
||||
u += (u64)sid->identifier_authority.value[i] << j;
|
||||
if (!sid->identifier_authority.high_part)
|
||||
i = snprintf(s, cnt, "%lu", (unsigned long)u);
|
||||
else
|
||||
i = snprintf(s, cnt, "0x%llx", (unsigned long long)u);
|
||||
if (i < 0 || i >= cnt)
|
||||
goto err_out;
|
||||
s += i;
|
||||
cnt -= i;
|
||||
/* Finally, add the sub authorities. */
|
||||
for (j = 0; j < sid->sub_authority_count; j++) {
|
||||
i = snprintf(s, cnt, "-%u", (unsigned int)
|
||||
le32_to_cpu(sid->sub_authority[j]));
|
||||
if (i < 0 || i >= cnt)
|
||||
goto err_out;
|
||||
s += i;
|
||||
cnt -= i;
|
||||
}
|
||||
return sid_str;
|
||||
err_out:
|
||||
if (i >= cnt)
|
||||
i = EMSGSIZE;
|
||||
else
|
||||
i = errno;
|
||||
if (!sid_str_size)
|
||||
free(sid_str);
|
||||
errno = i;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_generate_guid - generatates a random current guid.
|
||||
* @guid: [OUT] pointer to a GUID struct to hold the generated guid.
|
||||
*
|
||||
* perhaps not a very good random number generator though...
|
||||
*/
|
||||
void ntfs_generate_guid(GUID *guid)
|
||||
{
|
||||
unsigned int i;
|
||||
u8 *p = (u8 *)guid;
|
||||
|
||||
for (i = 0; i < sizeof(GUID); i++) {
|
||||
p[i] = (u8)(random() & 0xFF);
|
||||
if (i == 7)
|
||||
p[7] = (p[7] & 0x0F) | 0x40;
|
||||
if (i == 8)
|
||||
p[8] = (p[8] & 0x3F) | 0x80;
|
||||
}
|
||||
}
|
||||
|
||||
int ntfs_sd_add_everyone(ntfs_inode *ni)
|
||||
{
|
||||
SECURITY_DESCRIPTOR_ATTR *sd;
|
||||
ACL *acl;
|
||||
ACCESS_ALLOWED_ACE *ace;
|
||||
SID *sid;
|
||||
int ret, sd_len;
|
||||
|
||||
/* Create SECURITY_DESCRIPTOR attribute (everyone has full access). */
|
||||
/*
|
||||
* Calculate security descriptor length. We have 2 sub-authorities in
|
||||
* owner and group SIDs, but structure SID contain only one, so add
|
||||
* 4 bytes to every SID.
|
||||
*/
|
||||
sd_len = sizeof(SECURITY_DESCRIPTOR_ATTR) + 2 * (sizeof(SID) + 4) +
|
||||
sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE);
|
||||
sd = ntfs_calloc(sd_len);
|
||||
if (!sd)
|
||||
return -1;
|
||||
|
||||
sd->revision = 1;
|
||||
sd->control = SE_DACL_PRESENT | SE_SELF_RELATIVE;
|
||||
|
||||
sid = (SID *)((u8 *)sd + sizeof(SECURITY_DESCRIPTOR_ATTR));
|
||||
sid->revision = 1;
|
||||
sid->sub_authority_count = 2;
|
||||
sid->sub_authority[0] = cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID);
|
||||
sid->sub_authority[1] = cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS);
|
||||
sid->identifier_authority.value[5] = 5;
|
||||
sd->owner = cpu_to_le32((u8 *)sid - (u8 *)sd);
|
||||
|
||||
sid = (SID *)((u8 *)sid + sizeof(SID) + 4);
|
||||
sid->revision = 1;
|
||||
sid->sub_authority_count = 2;
|
||||
sid->sub_authority[0] = cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID);
|
||||
sid->sub_authority[1] = cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS);
|
||||
sid->identifier_authority.value[5] = 5;
|
||||
sd->group = cpu_to_le32((u8 *)sid - (u8 *)sd);
|
||||
|
||||
acl = (ACL *)((u8 *)sid + sizeof(SID) + 4);
|
||||
acl->revision = 2;
|
||||
acl->size = cpu_to_le16(sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE));
|
||||
acl->ace_count = cpu_to_le16(1);
|
||||
sd->dacl = cpu_to_le32((u8 *)acl - (u8 *)sd);
|
||||
|
||||
ace = (ACCESS_ALLOWED_ACE *)((u8 *)acl + sizeof(ACL));
|
||||
ace->type = ACCESS_ALLOWED_ACE_TYPE;
|
||||
ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE;
|
||||
ace->size = cpu_to_le16(sizeof(ACCESS_ALLOWED_ACE));
|
||||
ace->mask = cpu_to_le32(0x1f01ff); /* FIXME */
|
||||
ace->sid.revision = 1;
|
||||
ace->sid.sub_authority_count = 1;
|
||||
ace->sid.sub_authority[0] = 0;
|
||||
ace->sid.identifier_authority.value[5] = 1;
|
||||
|
||||
ret = ntfs_attr_add(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0, (u8 *)sd,
|
||||
sd_len);
|
||||
if (ret)
|
||||
ntfs_log_perror("Failed to add SECURITY_DESCRIPTOR\n");
|
||||
|
||||
free(sd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* security.h - Exports for handling security/ACLs in NTFS.
|
||||
* Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2004 Anton Altaparmakov
|
||||
* Copyright (c) 2005-2006 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
|
||||
* 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_SECURITY_H
|
||||
#define _NTFS_SECURITY_H
|
||||
|
||||
#include "types.h"
|
||||
#include "layout.h"
|
||||
#include "inode.h"
|
||||
|
||||
extern const GUID *const zero_guid;
|
||||
|
||||
extern BOOL ntfs_guid_is_zero(const GUID *guid);
|
||||
extern char *ntfs_guid_to_mbs(const GUID *guid, char *guid_str);
|
||||
|
||||
/**
|
||||
* ntfs_sid_is_valid - determine if a SID is valid
|
||||
* @sid: SID for which to determine if it is valid
|
||||
*
|
||||
* Determine if the SID pointed to by @sid is valid.
|
||||
*
|
||||
* Return TRUE if it is valid and FALSE otherwise.
|
||||
*/
|
||||
static __inline__ BOOL ntfs_sid_is_valid(const SID *sid)
|
||||
{
|
||||
if (!sid || sid->revision != SID_REVISION ||
|
||||
sid->sub_authority_count > SID_MAX_SUB_AUTHORITIES)
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
extern int ntfs_sid_to_mbs_size(const SID *sid);
|
||||
extern char *ntfs_sid_to_mbs(const SID *sid, char *sid_str,
|
||||
size_t sid_str_size);
|
||||
extern void ntfs_generate_guid(GUID *guid);
|
||||
extern int ntfs_sd_add_everyone(ntfs_inode *ni);
|
||||
|
||||
#endif /* defined _NTFS_SECURITY_H */
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* support.h - Useful definitions and macros. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2000-2004 Anton Altaparmakov
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file is distributed in the hope that it will be
|
||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_SUPPORT_H
|
||||
#define _NTFS_SUPPORT_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDDEF_H
|
||||
#include <stddef.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Our mailing list. Use this define to prevent typos in email address.
|
||||
*/
|
||||
#define NTFS_DEV_LIST "[email protected]"
|
||||
|
||||
/*
|
||||
* Generic macro to convert pointers to values for comparison purposes.
|
||||
*/
|
||||
#ifndef p2n
|
||||
#define p2n(p) ((ptrdiff_t)((ptrdiff_t*)(p)))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The classic min and max macros.
|
||||
*/
|
||||
#ifndef min
|
||||
#define min(a,b) ((a) <= (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#ifndef max
|
||||
#define max(a,b) ((a) >= (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Useful macro for determining the offset of a struct member.
|
||||
*/
|
||||
#ifndef offsetof
|
||||
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simple bit operation macros. NOTE: These are NOT atomic.
|
||||
*/
|
||||
#define test_bit(bit, var) ((var) & (1 << (bit)))
|
||||
#define set_bit(bit, var) (var) |= 1 << (bit)
|
||||
#define clear_bit(bit, var) (var) &= ~(1 << (bit))
|
||||
|
||||
#define test_and_set_bit(bit, var) \
|
||||
({ \
|
||||
const BOOL old_state = test_bit(bit, var); \
|
||||
set_bit(bit, var); \
|
||||
old_state; \
|
||||
})
|
||||
|
||||
#define test_and_clear_bit(bit, var) \
|
||||
({ \
|
||||
const BOOL old_state = test_bit(bit, var); \
|
||||
clear_bit(bit, var); \
|
||||
old_state; \
|
||||
})
|
||||
|
||||
#endif /* defined _NTFS_SUPPORT_H */
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* types.h - Misc type definitions not related to on-disk structure.
|
||||
* Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2000-2004 Anton Altaparmakov
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file is distributed in the hope that it will be
|
||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_TYPES_H
|
||||
#define _NTFS_TYPES_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#if HAVE_STDINT_H || !HAVE_CONFIG_H
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
typedef uint8_t u8; /* Unsigned types of an exact size */
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
|
||||
typedef int8_t s8; /* Signed types of an exact size */
|
||||
typedef int16_t s16;
|
||||
typedef int32_t s32;
|
||||
typedef int64_t s64;
|
||||
|
||||
typedef u16 le16;
|
||||
typedef u32 le32;
|
||||
typedef u64 le64;
|
||||
|
||||
/*
|
||||
* Declare sle{16,32,64} to be unsigned because we do not want sign extension
|
||||
* on BE architectures.
|
||||
*/
|
||||
typedef u16 sle16;
|
||||
typedef u32 sle32;
|
||||
typedef u64 sle64;
|
||||
|
||||
typedef u16 ntfschar; /* 2-byte Unicode character type. */
|
||||
#define UCHAR_T_SIZE_BITS 1
|
||||
|
||||
/*
|
||||
* Clusters are signed 64-bit values on NTFS volumes. We define two types, LCN
|
||||
* and VCN, to allow for type checking and better code readability.
|
||||
*/
|
||||
typedef s64 VCN;
|
||||
typedef sle64 leVCN;
|
||||
typedef s64 LCN;
|
||||
typedef sle64 leLCN;
|
||||
|
||||
/*
|
||||
* The NTFS journal $LogFile uses log sequence numbers which are signed 64-bit
|
||||
* values. We define our own type LSN, to allow for type checking and better
|
||||
* code readability.
|
||||
*/
|
||||
typedef s64 LSN;
|
||||
typedef sle64 leLSN;
|
||||
|
||||
/*
|
||||
* Cygwin has a collision between our BOOL and <windef.h>'s
|
||||
* As long as this file will be included after <windows.h> were fine.
|
||||
*/
|
||||
#ifndef _WINDEF_H
|
||||
/**
|
||||
* enum BOOL - These are just to make the code more readable...
|
||||
*/
|
||||
typedef enum {
|
||||
#ifndef FALSE
|
||||
FALSE = 0,
|
||||
#endif
|
||||
#ifndef NO
|
||||
NO = 0,
|
||||
#endif
|
||||
#ifndef ZERO
|
||||
ZERO = 0,
|
||||
#endif
|
||||
#ifndef TRUE
|
||||
TRUE = 1,
|
||||
#endif
|
||||
#ifndef YES
|
||||
YES = 1,
|
||||
#endif
|
||||
#ifndef ONE
|
||||
ONE = 1,
|
||||
#endif
|
||||
} BOOL;
|
||||
#endif /* defined _WINDEF_H */
|
||||
|
||||
/**
|
||||
* enum IGNORE_CASE_BOOL -
|
||||
*/
|
||||
typedef enum {
|
||||
CASE_SENSITIVE = 0,
|
||||
IGNORE_CASE = 1,
|
||||
} IGNORE_CASE_BOOL;
|
||||
|
||||
#define STATUS_OK (0)
|
||||
#define STATUS_ERROR (-1)
|
||||
#define STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT (-2)
|
||||
#define STATUS_KEEP_SEARCHING (-3)
|
||||
#define STATUS_NOT_FOUND (-4)
|
||||
|
||||
#endif /* defined _NTFS_TYPES_H */
|
||||
|
||||
@@ -0,0 +1,959 @@
|
||||
/**
|
||||
* unistr.c - Unicode string handling. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2000-2004 Anton Altaparmakov
|
||||
* Copyright (c) 2002-2006 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
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file is distributed in the hope that it will be
|
||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDIO_H
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_WCHAR_H
|
||||
#include <wchar.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "attrib.h"
|
||||
#include "types.h"
|
||||
#include "unistr.h"
|
||||
#include "debug.h"
|
||||
#include "logging.h"
|
||||
#include "misc.h"
|
||||
|
||||
/*
|
||||
* IMPORTANT
|
||||
* =========
|
||||
*
|
||||
* All these routines assume that the Unicode characters are in little endian
|
||||
* encoding inside the strings!!!
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is used by the name collation functions to quickly determine what
|
||||
* characters are (in)valid.
|
||||
*/
|
||||
#if 0
|
||||
static const u8 legal_ansi_char_array[0x40] = {
|
||||
0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
|
||||
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
|
||||
|
||||
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
|
||||
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
|
||||
|
||||
0x17, 0x07, 0x18, 0x17, 0x17, 0x17, 0x17, 0x17,
|
||||
0x17, 0x17, 0x18, 0x16, 0x16, 0x17, 0x07, 0x00,
|
||||
|
||||
0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
|
||||
0x17, 0x17, 0x04, 0x16, 0x18, 0x16, 0x18, 0x18,
|
||||
};
|
||||
#endif
|
||||
|
||||
/**
|
||||
* ntfs_names_are_equal - compare two Unicode names for equality
|
||||
* @s1: name to compare to @s2
|
||||
* @s1_len: length in Unicode characters of @s1
|
||||
* @s2: name to compare to @s1
|
||||
* @s2_len: length in Unicode characters of @s2
|
||||
* @ic: ignore case bool
|
||||
* @upcase: upcase table (only if @ic == IGNORE_CASE)
|
||||
* @upcase_size: length in Unicode characters of @upcase (if present)
|
||||
*
|
||||
* Compare the names @s1 and @s2 and return TRUE (1) if the names are
|
||||
* identical, or FALSE (0) if they are not identical. If @ic is IGNORE_CASE,
|
||||
* the @upcase table is used to perform a case insensitive comparison.
|
||||
*/
|
||||
BOOL ntfs_names_are_equal(const ntfschar *s1, size_t s1_len,
|
||||
const ntfschar *s2, size_t s2_len,
|
||||
const IGNORE_CASE_BOOL ic,
|
||||
const ntfschar *upcase, const u32 upcase_size)
|
||||
{
|
||||
if (s1_len != s2_len)
|
||||
return FALSE;
|
||||
if (!s1_len)
|
||||
return TRUE;
|
||||
if (ic == CASE_SENSITIVE)
|
||||
return ntfs_ucsncmp(s1, s2, s1_len) ? FALSE: TRUE;
|
||||
return ntfs_ucsncasecmp(s1, s2, s1_len, upcase, upcase_size) ? FALSE:
|
||||
TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_names_collate - collate two Unicode names
|
||||
* @name1: first Unicode name to compare
|
||||
* @name1_len: length of first Unicode name to compare
|
||||
* @name2: second Unicode name to compare
|
||||
* @name2_len: length of second Unicode name to compare
|
||||
* @err_val: if @name1 contains an invalid character return this value
|
||||
* @ic: either CASE_SENSITIVE or IGNORE_CASE
|
||||
* @upcase: upcase table (ignored if @ic is CASE_SENSITIVE)
|
||||
* @upcase_len: upcase table size (ignored if @ic is CASE_SENSITIVE)
|
||||
*
|
||||
* ntfs_names_collate() collates two Unicode names and returns:
|
||||
*
|
||||
* -1 if the first name collates before the second one,
|
||||
* 0 if the names match,
|
||||
* 1 if the second name collates before the first one, or
|
||||
* @err_val if an invalid character is found in @name1 during the comparison.
|
||||
*
|
||||
* The following characters are considered invalid: '"', '*', '<', '>' and '?'.
|
||||
*/
|
||||
int ntfs_names_collate(const ntfschar *name1, const u32 name1_len,
|
||||
const ntfschar *name2, const u32 name2_len,
|
||||
const int err_val __attribute__((unused)),
|
||||
const IGNORE_CASE_BOOL ic, const ntfschar *upcase,
|
||||
const u32 upcase_len)
|
||||
{
|
||||
u32 cnt;
|
||||
ntfschar c1, c2;
|
||||
|
||||
#ifdef DEBUG
|
||||
if (!name1 || !name2 || (ic && (!upcase || !upcase_len))) {
|
||||
ntfs_log_debug("ntfs_names_collate received NULL pointer!\n");
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
for (cnt = 0; cnt < min(name1_len, name2_len); ++cnt) {
|
||||
c1 = le16_to_cpu(*name1);
|
||||
name1++;
|
||||
c2 = le16_to_cpu(*name2);
|
||||
name2++;
|
||||
if (ic) {
|
||||
if (c1 < upcase_len)
|
||||
c1 = le16_to_cpu(upcase[c1]);
|
||||
if (c2 < upcase_len)
|
||||
c2 = le16_to_cpu(upcase[c2]);
|
||||
}
|
||||
#if 0
|
||||
if (c1 < 64 && legal_ansi_char_array[c1] & 8)
|
||||
return err_val;
|
||||
#endif
|
||||
if (c1 < c2)
|
||||
return -1;
|
||||
if (c1 > c2)
|
||||
return 1;
|
||||
}
|
||||
if (name1_len < name2_len)
|
||||
return -1;
|
||||
if (name1_len == name2_len)
|
||||
return 0;
|
||||
/* name1_len > name2_len */
|
||||
#if 0
|
||||
c1 = le16_to_cpu(*name1);
|
||||
if (c1 < 64 && legal_ansi_char_array[c1] & 8)
|
||||
return err_val;
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_ucsncmp - compare two little endian Unicode strings
|
||||
* @s1: first string
|
||||
* @s2: second string
|
||||
* @n: maximum unicode characters to compare
|
||||
*
|
||||
* Compare the first @n characters of the Unicode strings @s1 and @s2,
|
||||
* The strings in little endian format and appropriate le16_to_cpu()
|
||||
* conversion is performed on non-little endian machines.
|
||||
*
|
||||
* The function returns an integer less than, equal to, or greater than zero
|
||||
* if @s1 (or the first @n Unicode characters thereof) is found, respectively,
|
||||
* to be less than, to match, or be greater than @s2.
|
||||
*/
|
||||
int ntfs_ucsncmp(const ntfschar *s1, const ntfschar *s2, size_t n)
|
||||
{
|
||||
ntfschar c1, c2;
|
||||
size_t i;
|
||||
|
||||
#ifdef DEBUG
|
||||
if (!s1 || !s2) {
|
||||
ntfs_log_debug("ntfs_wcsncmp() received NULL pointer!\n");
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
for (i = 0; i < n; ++i) {
|
||||
c1 = le16_to_cpu(s1[i]);
|
||||
c2 = le16_to_cpu(s2[i]);
|
||||
if (c1 < c2)
|
||||
return -1;
|
||||
if (c1 > c2)
|
||||
return 1;
|
||||
if (!c1)
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_ucsncasecmp - compare two little endian Unicode strings, ignoring case
|
||||
* @s1: first string
|
||||
* @s2: second string
|
||||
* @n: maximum unicode characters to compare
|
||||
* @upcase: upcase table
|
||||
* @upcase_size: upcase table size in Unicode characters
|
||||
*
|
||||
* Compare the first @n characters of the Unicode strings @s1 and @s2,
|
||||
* ignoring case. The strings in little endian format and appropriate
|
||||
* le16_to_cpu() conversion is performed on non-little endian machines.
|
||||
*
|
||||
* Each character is uppercased using the @upcase table before the comparison.
|
||||
*
|
||||
* The function returns an integer less than, equal to, or greater than zero
|
||||
* if @s1 (or the first @n Unicode characters thereof) is found, respectively,
|
||||
* to be less than, to match, or be greater than @s2.
|
||||
*/
|
||||
int ntfs_ucsncasecmp(const ntfschar *s1, const ntfschar *s2, size_t n,
|
||||
const ntfschar *upcase, const u32 upcase_size)
|
||||
{
|
||||
ntfschar c1, c2;
|
||||
size_t i;
|
||||
|
||||
#ifdef DEBUG
|
||||
if (!s1 || !s2 || !upcase) {
|
||||
ntfs_log_debug("ntfs_wcsncasecmp() received NULL pointer!\n");
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
for (i = 0; i < n; ++i) {
|
||||
if ((c1 = le16_to_cpu(s1[i])) < upcase_size)
|
||||
c1 = le16_to_cpu(upcase[c1]);
|
||||
if ((c2 = le16_to_cpu(s2[i])) < upcase_size)
|
||||
c2 = le16_to_cpu(upcase[c2]);
|
||||
if (c1 < c2)
|
||||
return -1;
|
||||
if (c1 > c2)
|
||||
return 1;
|
||||
if (!c1)
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_ucsnlen - determine the length of a little endian Unicode string
|
||||
* @s: pointer to Unicode string
|
||||
* @maxlen: maximum length of string @s
|
||||
*
|
||||
* Return the number of Unicode characters in the little endian Unicode
|
||||
* string @s up to a maximum of maxlen Unicode characters, not including
|
||||
* the terminating (ntfschar)'\0'. If there is no (ntfschar)'\0' between @s
|
||||
* and @s + @maxlen, @maxlen is returned.
|
||||
*
|
||||
* This function never looks beyond @s + @maxlen.
|
||||
*/
|
||||
u32 ntfs_ucsnlen(const ntfschar *s, u32 maxlen)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < maxlen; i++) {
|
||||
if (!le16_to_cpu(s[i]))
|
||||
break;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_ucsndup - duplicate little endian Unicode string
|
||||
* @s: pointer to Unicode string
|
||||
* @maxlen: maximum length of string @s
|
||||
*
|
||||
* Return a pointer to a new little endian Unicode string which is a duplicate
|
||||
* of the string s. Memory for the new string is obtained with ntfs_malloc(3),
|
||||
* and can be freed with free(3).
|
||||
*
|
||||
* A maximum of @maxlen Unicode characters are copied and a terminating
|
||||
* (ntfschar)'\0' little endian Unicode character is added.
|
||||
*
|
||||
* This function never looks beyond @s + @maxlen.
|
||||
*
|
||||
* Return a pointer to the new little endian Unicode string on success and NULL
|
||||
* on failure with errno set to the error code.
|
||||
*/
|
||||
ntfschar *ntfs_ucsndup(const ntfschar *s, u32 maxlen)
|
||||
{
|
||||
ntfschar *dst;
|
||||
u32 len;
|
||||
|
||||
len = ntfs_ucsnlen(s, maxlen);
|
||||
dst = ntfs_malloc((len + 1) * sizeof(ntfschar));
|
||||
if (dst) {
|
||||
memcpy(dst, s, len * sizeof(ntfschar));
|
||||
dst[len] = cpu_to_le16(L'\0');
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_name_upcase - Map an Unicode name to its uppercase equivalent
|
||||
* @name:
|
||||
* @name_len:
|
||||
* @upcase:
|
||||
* @upcase_len:
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
void ntfs_name_upcase(ntfschar *name, u32 name_len, const ntfschar *upcase,
|
||||
const u32 upcase_len)
|
||||
{
|
||||
u32 i;
|
||||
ntfschar u;
|
||||
|
||||
for (i = 0; i < name_len; i++)
|
||||
if ((u = le16_to_cpu(name[i])) < upcase_len)
|
||||
name[i] = upcase[u];
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_file_value_upcase - Convert a filename to upper case
|
||||
* @file_name_attr:
|
||||
* @upcase:
|
||||
* @upcase_len:
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
void ntfs_file_value_upcase(FILE_NAME_ATTR *file_name_attr,
|
||||
const ntfschar *upcase, const u32 upcase_len)
|
||||
{
|
||||
ntfs_name_upcase((ntfschar*)&file_name_attr->file_name,
|
||||
file_name_attr->file_name_length, upcase, upcase_len);
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_file_values_compare - Which of two filenames should be listed first
|
||||
* @file_name_attr1:
|
||||
* @file_name_attr2:
|
||||
* @err_val:
|
||||
* @ic:
|
||||
* @upcase:
|
||||
* @upcase_len:
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
int ntfs_file_values_compare(const FILE_NAME_ATTR *file_name_attr1,
|
||||
const FILE_NAME_ATTR *file_name_attr2,
|
||||
const int err_val, const IGNORE_CASE_BOOL ic,
|
||||
const ntfschar *upcase, const u32 upcase_len)
|
||||
{
|
||||
return ntfs_names_collate((ntfschar*)&file_name_attr1->file_name,
|
||||
file_name_attr1->file_name_length,
|
||||
(ntfschar*)&file_name_attr2->file_name,
|
||||
file_name_attr2->file_name_length,
|
||||
err_val, ic, upcase, upcase_len);
|
||||
}
|
||||
|
||||
#ifdef _BEOS_
|
||||
|
||||
|
||||
static int
|
||||
to_utf8(wchar_t c,unsigned char* buf)
|
||||
{
|
||||
if(c==0)
|
||||
return 0; /* No support for embedded 0 runes */
|
||||
if(c<0x80){
|
||||
if(buf)buf[0]=(unsigned char)c;
|
||||
return 1;
|
||||
}
|
||||
if(c<0x800){
|
||||
if(buf){
|
||||
buf[0] = 0xc0 | (c>>6);
|
||||
buf[1] = 0x80 | (c & 0x3f);
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
if(c<0x10000){
|
||||
if(buf){
|
||||
buf[0] = 0xe0 | (c>>12);
|
||||
buf[1] = 0x80 | ((c>>6) & 0x3f);
|
||||
buf[2] = 0x80 | (c & 0x3f);
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
/* We don't support characters above 0xFFFF in NTFS */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Decodes a sequence of utf8 bytes into a single wide character.
|
||||
* The character is returned in host byte order.
|
||||
* Returns the number of bytes consumed, or 0 on error.
|
||||
*/
|
||||
static int
|
||||
from_utf8(const unsigned char* str,wchar_t *c)
|
||||
{
|
||||
int l=0,i;
|
||||
|
||||
if(*str<0x80){
|
||||
*c = *str;
|
||||
return 1;
|
||||
}
|
||||
if(*str<0xc0) /* lead byte must not be 10xxxxxx */
|
||||
return 0; /* is c0 a possible lead byte? */
|
||||
if(*str<0xe0){ /* 110xxxxx */
|
||||
*c = *str & 0x1f;
|
||||
l=2;
|
||||
}else if(*str<0xf0){ /* 1110xxxx */
|
||||
*c = *str & 0xf;
|
||||
l=3;
|
||||
}else if(*str<0xf8){ /* 11110xxx */
|
||||
*c = *str & 7;
|
||||
l=4;
|
||||
}else /* We don't support characters above 0xFFFF in NTFS */
|
||||
return 0;
|
||||
|
||||
|
||||
for(i=1;i<l;i++){
|
||||
/* all other bytes must be 10xxxxxx */
|
||||
if((str[i] & 0xc0) != 0x80)
|
||||
return 0;
|
||||
*c <<= 6;
|
||||
*c |= str[i] & 0x3f;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
|
||||
/* Converts wide string to UTF-8. Expects two in- and two out-parameters.
|
||||
* Returns 0 on success, or error code.
|
||||
* The caller has to free the result string.
|
||||
* There is no support for UTF-16, yet
|
||||
*/
|
||||
static int ntfs_dupuni2utf8(wchar_t* in, int in_len,char **out,int *out_len)
|
||||
{
|
||||
int i,tmp;
|
||||
int len8;
|
||||
unsigned char *result;
|
||||
|
||||
/* count the length of the resulting UTF-8 */
|
||||
for(i=len8=0;i<in_len;i++){
|
||||
tmp=to_utf8(le16_to_cpu( *(in+i) ),0);
|
||||
if(!tmp)
|
||||
/* invalid character */
|
||||
return EILSEQ;
|
||||
len8+=tmp;
|
||||
}
|
||||
*out=result=ntfs_malloc(len8+1); /* allow for zero-termination */
|
||||
|
||||
if(!result)
|
||||
return ENOMEM;
|
||||
result[len8]='\0';
|
||||
*out_len=len8;
|
||||
for(i=len8=0;i<in_len;i++)
|
||||
len8+=to_utf8(le16_to_cpu( *(in+i) ),result+len8);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Converts an UTF-8 sequence to a wide string. Same conventions as the
|
||||
* previous function
|
||||
*/
|
||||
static int ntfs_duputf82uni(unsigned char* in, int in_len,wchar_t** out,int *out_len)
|
||||
{
|
||||
int i,tmp;
|
||||
int len16;
|
||||
|
||||
wchar_t* result;
|
||||
wchar_t wtmp;
|
||||
for(i=len16=0;i<in_len;i+=tmp,len16++){
|
||||
tmp=from_utf8(in+i,&wtmp);
|
||||
if(!tmp)
|
||||
return EILSEQ;
|
||||
}
|
||||
*out=result=ntfs_malloc(2*(len16+1));
|
||||
if(!result)
|
||||
return ENOMEM;
|
||||
result[len16]=0;
|
||||
*out_len=len16;
|
||||
for(i=len16=0;i<in_len;i+=tmp,len16++)
|
||||
{
|
||||
tmp=from_utf8(in+i, &wtmp);
|
||||
*(result+len16) = cpu_to_le16(wtmp);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ntfs_ucstombs - convert a little endian Unicode string to a multibyte string
|
||||
* @ins: input Unicode string buffer
|
||||
* @ins_len: length of input string in Unicode characters
|
||||
* @outs: on return contains the (allocated) output multibyte string
|
||||
* @outs_len: length of output buffer in bytes
|
||||
*
|
||||
* Convert the input little endian, 2-byte Unicode string @ins, of length
|
||||
* @ins_len into the multibyte string format dictated by the current locale.
|
||||
*
|
||||
* If *@outs is NULL, 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 bytes written to the output
|
||||
* string *@outs (>= 0), not counting the terminating NULL byte. If the output
|
||||
* string buffer was allocated, *@outs is set to it.
|
||||
*
|
||||
* On error, -1 is returned, and errno is set to the error code. The following
|
||||
* error codes can be expected:
|
||||
* EINVAL Invalid arguments (e.g. @ins or @outs is NULL).
|
||||
* EILSEQ The input string cannot be represented as a multibyte
|
||||
* sequence according to the current locale.
|
||||
* ENAMETOOLONG Destination buffer is too small for input string.
|
||||
* ENOMEM Not enough memory to allocate destination buffer.
|
||||
*/
|
||||
int ntfs_ucstombs(const ntfschar *ins, const int ins_len, char **outs, int outs_len)
|
||||
{
|
||||
int out_len = outs_len;
|
||||
if(ntfs_dupuni2utf8(ins,ins_len,outs,&out_len)==0)
|
||||
return out_len;
|
||||
else
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* On error, -1 is returned, and errno is set to the error code. The following
|
||||
* error codes can be expected:
|
||||
* EINVAL Invalid arguments (e.g. @ins or @outs is NULL).
|
||||
* EILSEQ The input string cannot be represented as a Unicode
|
||||
* string according to the current locale.
|
||||
* 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 in_len = strlen(ins);
|
||||
int out_len = outs_len;
|
||||
if(ntfs_duputf82uni(ins,in_len,outs,&out_len)==0)
|
||||
return out_len;
|
||||
else
|
||||
return EILSEQ;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/**
|
||||
* ntfs_ucstombs - convert a little endian Unicode string to a multibyte string
|
||||
* @ins: input Unicode string buffer
|
||||
* @ins_len: length of input string in Unicode characters
|
||||
* @outs: on return contains the (allocated) output multibyte string
|
||||
* @outs_len: length of output buffer in bytes
|
||||
*
|
||||
* Convert the input little endian, 2-byte Unicode string @ins, of length
|
||||
* @ins_len into the multibyte string format dictated by the current locale.
|
||||
*
|
||||
* If *@outs is NULL, 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 bytes written to the output
|
||||
* string *@outs (>= 0), not counting the terminating NULL byte. If the output
|
||||
* string buffer was allocated, *@outs is set to it.
|
||||
*
|
||||
* On error, -1 is returned, and errno is set to the error code. The following
|
||||
* error codes can be expected:
|
||||
* EINVAL Invalid arguments (e.g. @ins or @outs is NULL).
|
||||
* EILSEQ The input string cannot be represented as a multibyte
|
||||
* sequence according to the current locale.
|
||||
* ENAMETOOLONG Destination buffer is too small for input string.
|
||||
* ENOMEM Not enough memory to allocate destination buffer.
|
||||
*/
|
||||
int ntfs_ucstombs(const ntfschar *ins, const int ins_len, char **outs,
|
||||
int outs_len)
|
||||
{
|
||||
char *mbs;
|
||||
wchar_t wc;
|
||||
int i, o, mbs_len;
|
||||
int cnt = 0;
|
||||
#ifdef HAVE_MBSINIT
|
||||
mbstate_t mbstate;
|
||||
#endif
|
||||
|
||||
if (!ins || !outs) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
mbs = *outs;
|
||||
mbs_len = outs_len;
|
||||
if (mbs && !mbs_len) {
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
if (!mbs) {
|
||||
mbs_len = (ins_len + 1) * MB_CUR_MAX;
|
||||
mbs = ntfs_malloc(mbs_len);
|
||||
if (!mbs)
|
||||
return -1;
|
||||
}
|
||||
#ifdef HAVE_MBSINIT
|
||||
memset(&mbstate, 0, sizeof(mbstate));
|
||||
#else
|
||||
wctomb(NULL, 0);
|
||||
#endif
|
||||
for (i = o = 0; i < ins_len; i++) {
|
||||
/* Reallocate memory if necessary or abort. */
|
||||
if ((int)(o + MB_CUR_MAX) > mbs_len) {
|
||||
char *tc;
|
||||
if (mbs == *outs) {
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
tc = ntfs_malloc((mbs_len + 64) & ~63);
|
||||
if (!tc)
|
||||
goto err_out;
|
||||
memcpy(tc, mbs, mbs_len);
|
||||
mbs_len = (mbs_len + 64) & ~63;
|
||||
free(mbs);
|
||||
mbs = tc;
|
||||
}
|
||||
/* Convert the LE Unicode character to a CPU wide character. */
|
||||
wc = (wchar_t)le16_to_cpu(ins[i]);
|
||||
if (!wc)
|
||||
break;
|
||||
/* Convert the CPU endian wide character to multibyte. */
|
||||
#ifdef HAVE_MBSINIT
|
||||
cnt = wcrtomb(mbs + o, wc, &mbstate);
|
||||
#else
|
||||
cnt = wctomb(mbs + o, wc);
|
||||
#endif
|
||||
if (cnt == -1)
|
||||
goto err_out;
|
||||
if (cnt <= 0) {
|
||||
ntfs_log_debug("Eeek. cnt <= 0, cnt = %i\n", cnt);
|
||||
errno = EINVAL;
|
||||
goto err_out;
|
||||
}
|
||||
o += cnt;
|
||||
}
|
||||
#ifdef HAVE_MBSINIT
|
||||
/* Make sure we are back in the initial state. */
|
||||
if (!mbsinit(&mbstate)) {
|
||||
ntfs_log_debug("Eeek. mbstate not in initial state!\n");
|
||||
errno = EILSEQ;
|
||||
goto err_out;
|
||||
}
|
||||
#endif
|
||||
/* Now write the NULL character. */
|
||||
mbs[o] = '\0';
|
||||
if (*outs != mbs)
|
||||
*outs = mbs;
|
||||
return o;
|
||||
err_out:
|
||||
if (mbs != *outs) {
|
||||
int eo = errno;
|
||||
free(mbs);
|
||||
errno = eo;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* On error, -1 is returned, and errno is set to the error code. The following
|
||||
* error codes can be expected:
|
||||
* EINVAL Invalid arguments (e.g. @ins or @outs is NULL).
|
||||
* EILSEQ The input string cannot be represented as a Unicode
|
||||
* string according to the current locale.
|
||||
* 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)
|
||||
{
|
||||
ntfschar *ucs;
|
||||
const char *s;
|
||||
wchar_t wc;
|
||||
int i, o, cnt, ins_len, ucs_len, ins_size;
|
||||
#ifdef HAVE_MBSINIT
|
||||
mbstate_t mbstate;
|
||||
#endif
|
||||
|
||||
if (!ins || !outs) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
ucs = *outs;
|
||||
ucs_len = outs_len;
|
||||
if (ucs && !ucs_len) {
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
/* Determine the size of the multi-byte string in bytes. */
|
||||
ins_size = strlen(ins);
|
||||
/* Determine the length of the multi-byte string. */
|
||||
s = ins;
|
||||
#if defined(HAVE_MBSINIT)
|
||||
memset(&mbstate, 0, sizeof(mbstate));
|
||||
ins_len = mbsrtowcs(NULL, (const char **)&s, 0, &mbstate);
|
||||
#ifdef __CYGWIN32__
|
||||
if (!ins_len && *ins) {
|
||||
/* Older Cygwin had broken mbsrtowcs() implementation. */
|
||||
ins_len = strlen(ins);
|
||||
}
|
||||
#endif
|
||||
#elif !defined(DJGPP)
|
||||
ins_len = mbstowcs(NULL, s, 0);
|
||||
#else
|
||||
/* Eeek!!! DJGPP has broken mbstowcs() implementation!!! */
|
||||
ins_len = strlen(ins);
|
||||
#endif
|
||||
if (ins_len == -1)
|
||||
return ins_len;
|
||||
#ifdef HAVE_MBSINIT
|
||||
if ((s != ins) || !mbsinit(&mbstate)) {
|
||||
#else
|
||||
if (s != ins) {
|
||||
#endif
|
||||
errno = EILSEQ;
|
||||
return -1;
|
||||
}
|
||||
/* Add the NULL terminator. */
|
||||
ins_len++;
|
||||
if (!ucs) {
|
||||
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. */
|
||||
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);
|
||||
if (!tc)
|
||||
goto err_out;
|
||||
ucs = tc;
|
||||
ucs_len /= sizeof(ntfschar);
|
||||
}
|
||||
/* Convert the multibyte character to a wide character. */
|
||||
#ifdef HAVE_MBSINIT
|
||||
cnt = mbrtowc(&wc, ins + i, ins_size - i, &mbstate);
|
||||
#else
|
||||
cnt = mbtowc(&wc, ins + i, ins_size - i);
|
||||
#endif
|
||||
if (!cnt)
|
||||
break;
|
||||
if (cnt == -1)
|
||||
goto err_out;
|
||||
if (cnt < -1) {
|
||||
ntfs_log_trace("Eeek. cnt = %i\n", cnt);
|
||||
errno = EINVAL;
|
||||
goto err_out;
|
||||
}
|
||||
/* Make sure we are not overflowing the NTFS Unicode set. */
|
||||
if ((unsigned long)wc >= (unsigned long)(1 <<
|
||||
(8 * sizeof(ntfschar)))) {
|
||||
errno = EILSEQ;
|
||||
goto err_out;
|
||||
}
|
||||
/* Convert the CPU wide character to a LE Unicode character. */
|
||||
ucs[o] = cpu_to_le16(wc);
|
||||
}
|
||||
#ifdef HAVE_MBSINIT
|
||||
/* Make sure we are back in the initial state. */
|
||||
if (!mbsinit(&mbstate)) {
|
||||
ntfs_log_trace("Eeek. mbstate not in initial state!\n");
|
||||
errno = EILSEQ;
|
||||
goto err_out;
|
||||
}
|
||||
#endif
|
||||
/* Now write the NULL character. */
|
||||
ucs[o] = cpu_to_le16(L'\0');
|
||||
if (*outs != ucs)
|
||||
*outs = ucs;
|
||||
return o;
|
||||
err_out:
|
||||
if (ucs != *outs) {
|
||||
int eo = errno;
|
||||
free(ucs);
|
||||
errno = eo;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* ntfs_upcase_table_build - build the default upcase table for NTFS
|
||||
* @uc: destination buffer where to store the built table
|
||||
* @uc_len: size of destination buffer in bytes
|
||||
*
|
||||
* ntfs_upcase_table_build() builds the default upcase table for NTFS and
|
||||
* stores it in the caller supplied buffer @uc of size @uc_len.
|
||||
*
|
||||
* Note, @uc_len must be at least 128kiB in size or bad things will happen!
|
||||
*/
|
||||
void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len)
|
||||
{
|
||||
static int uc_run_table[][3] = { /* Start, End, Add */
|
||||
{0x0061, 0x007B, -32}, {0x0451, 0x045D, -80}, {0x1F70, 0x1F72, 74},
|
||||
{0x00E0, 0x00F7, -32}, {0x045E, 0x0460, -80}, {0x1F72, 0x1F76, 86},
|
||||
{0x00F8, 0x00FF, -32}, {0x0561, 0x0587, -48}, {0x1F76, 0x1F78, 100},
|
||||
{0x0256, 0x0258, -205}, {0x1F00, 0x1F08, 8}, {0x1F78, 0x1F7A, 128},
|
||||
{0x028A, 0x028C, -217}, {0x1F10, 0x1F16, 8}, {0x1F7A, 0x1F7C, 112},
|
||||
{0x03AC, 0x03AD, -38}, {0x1F20, 0x1F28, 8}, {0x1F7C, 0x1F7E, 126},
|
||||
{0x03AD, 0x03B0, -37}, {0x1F30, 0x1F38, 8}, {0x1FB0, 0x1FB2, 8},
|
||||
{0x03B1, 0x03C2, -32}, {0x1F40, 0x1F46, 8}, {0x1FD0, 0x1FD2, 8},
|
||||
{0x03C2, 0x03C3, -31}, {0x1F51, 0x1F52, 8}, {0x1FE0, 0x1FE2, 8},
|
||||
{0x03C3, 0x03CC, -32}, {0x1F53, 0x1F54, 8}, {0x1FE5, 0x1FE6, 7},
|
||||
{0x03CC, 0x03CD, -64}, {0x1F55, 0x1F56, 8}, {0x2170, 0x2180, -16},
|
||||
{0x03CD, 0x03CF, -63}, {0x1F57, 0x1F58, 8}, {0x24D0, 0x24EA, -26},
|
||||
{0x0430, 0x0450, -32}, {0x1F60, 0x1F68, 8}, {0xFF41, 0xFF5B, -32},
|
||||
{0}
|
||||
};
|
||||
static int uc_dup_table[][2] = { /* Start, End */
|
||||
{0x0100, 0x012F}, {0x01A0, 0x01A6}, {0x03E2, 0x03EF}, {0x04CB, 0x04CC},
|
||||
{0x0132, 0x0137}, {0x01B3, 0x01B7}, {0x0460, 0x0481}, {0x04D0, 0x04EB},
|
||||
{0x0139, 0x0149}, {0x01CD, 0x01DD}, {0x0490, 0x04BF}, {0x04EE, 0x04F5},
|
||||
{0x014A, 0x0178}, {0x01DE, 0x01EF}, {0x04BF, 0x04BF}, {0x04F8, 0x04F9},
|
||||
{0x0179, 0x017E}, {0x01F4, 0x01F5}, {0x04C1, 0x04C4}, {0x1E00, 0x1E95},
|
||||
{0x018B, 0x018B}, {0x01FA, 0x0218}, {0x04C7, 0x04C8}, {0x1EA0, 0x1EF9},
|
||||
{0}
|
||||
};
|
||||
static int uc_byte_table[][2] = { /* Offset, Value */
|
||||
{0x00FF, 0x0178}, {0x01AD, 0x01AC}, {0x01F3, 0x01F1}, {0x0269, 0x0196},
|
||||
{0x0183, 0x0182}, {0x01B0, 0x01AF}, {0x0253, 0x0181}, {0x026F, 0x019C},
|
||||
{0x0185, 0x0184}, {0x01B9, 0x01B8}, {0x0254, 0x0186}, {0x0272, 0x019D},
|
||||
{0x0188, 0x0187}, {0x01BD, 0x01BC}, {0x0259, 0x018F}, {0x0275, 0x019F},
|
||||
{0x018C, 0x018B}, {0x01C6, 0x01C4}, {0x025B, 0x0190}, {0x0283, 0x01A9},
|
||||
{0x0192, 0x0191}, {0x01C9, 0x01C7}, {0x0260, 0x0193}, {0x0288, 0x01AE},
|
||||
{0x0199, 0x0198}, {0x01CC, 0x01CA}, {0x0263, 0x0194}, {0x0292, 0x01B7},
|
||||
{0x01A8, 0x01A7}, {0x01DD, 0x018E}, {0x0268, 0x0197},
|
||||
{0}
|
||||
};
|
||||
int i, r;
|
||||
|
||||
memset((char*)uc, 0, uc_len);
|
||||
uc_len >>= 1;
|
||||
if (uc_len > 65536)
|
||||
uc_len = 65536;
|
||||
for (i = 0; (u32)i < uc_len; i++)
|
||||
uc[i] = i;
|
||||
for (r = 0; uc_run_table[r][0]; r++)
|
||||
for (i = uc_run_table[r][0]; i < uc_run_table[r][1]; i++)
|
||||
uc[i] += uc_run_table[r][2];
|
||||
for (r = 0; uc_dup_table[r][0]; r++)
|
||||
for (i = uc_dup_table[r][0]; i < uc_dup_table[r][1]; i += 2)
|
||||
uc[i + 1]--;
|
||||
for (r = 0; uc_byte_table[r][0]; r++)
|
||||
uc[uc_byte_table[r][0]] = uc_byte_table[r][1];
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_str2ucs - convert a string to a valid NTFS file name
|
||||
* @s: input string
|
||||
* @len: length of output buffer in Unicode characters
|
||||
*
|
||||
* Convert the input @s string into the corresponding little endian,
|
||||
* 2-byte Unicode string. The length of the converted string is less
|
||||
* or equal to the maximum length allowed by the NTFS format (255).
|
||||
*
|
||||
* If @s is NULL then return AT_UNNAMED.
|
||||
*
|
||||
* On success the function returns the Unicode string in an allocated
|
||||
* buffer and the caller is responsible to free it when it's not needed
|
||||
* anymore.
|
||||
*
|
||||
* On error NULL is returned and errno is set to the error code.
|
||||
*/
|
||||
ntfschar *ntfs_str2ucs(const char *s, int *len)
|
||||
{
|
||||
ntfschar *ucs = NULL;
|
||||
|
||||
if (s && ((*len = ntfs_mbstoucs(s, &ucs, 0)) == -1)) {
|
||||
ntfs_log_perror("Couldn't convert '%s' to Unicode", s);
|
||||
return NULL;
|
||||
}
|
||||
if (*len > NTFS_MAX_NAME_LEN) {
|
||||
free(ucs);
|
||||
errno = ENAMETOOLONG;
|
||||
return NULL;
|
||||
}
|
||||
if (!ucs || !*len) {
|
||||
ucs = AT_UNNAMED;
|
||||
*len = 0;
|
||||
}
|
||||
return ucs;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_ucsfree - free memory allocated by ntfs_str2ucs()
|
||||
* @ucs input string to be freed
|
||||
*
|
||||
* Free memory at @ucs and which was allocated by ntfs_str2ucs.
|
||||
*
|
||||
* Return value: none.
|
||||
*/
|
||||
void ntfs_ucsfree(ntfschar *ucs)
|
||||
{
|
||||
if (ucs && (ucs != AT_UNNAMED))
|
||||
free(ucs);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* unistr.h - Exports for Unicode string handling. Originated from the Linux-NTFS
|
||||
* project.
|
||||
*
|
||||
* Copyright (c) 2000-2004 Anton Altaparmakov
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file is distributed in the hope that it will be
|
||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_UNISTR_H
|
||||
#define _NTFS_UNISTR_H
|
||||
|
||||
#include "types.h"
|
||||
#include "layout.h"
|
||||
|
||||
extern BOOL ntfs_names_are_equal(const ntfschar *s1, size_t s1_len,
|
||||
const ntfschar *s2, size_t s2_len, const IGNORE_CASE_BOOL ic,
|
||||
const ntfschar *upcase, const u32 upcase_size);
|
||||
|
||||
extern int ntfs_names_collate(const ntfschar *name1, const u32 name1_len,
|
||||
const ntfschar *name2, const u32 name2_len,
|
||||
const int err_val, const IGNORE_CASE_BOOL ic,
|
||||
const ntfschar *upcase, const u32 upcase_len);
|
||||
|
||||
extern int ntfs_ucsncmp(const ntfschar *s1, const ntfschar *s2, size_t n);
|
||||
|
||||
extern int ntfs_ucsncasecmp(const ntfschar *s1, const ntfschar *s2, size_t n,
|
||||
const ntfschar *upcase, const u32 upcase_size);
|
||||
|
||||
extern u32 ntfs_ucsnlen(const ntfschar *s, u32 maxlen);
|
||||
|
||||
extern ntfschar *ntfs_ucsndup(const ntfschar *s, u32 maxlen);
|
||||
|
||||
extern void ntfs_name_upcase(ntfschar *name, u32 name_len,
|
||||
const ntfschar *upcase, const u32 upcase_len);
|
||||
|
||||
extern void ntfs_file_value_upcase(FILE_NAME_ATTR *file_name_attr,
|
||||
const ntfschar *upcase, const u32 upcase_len);
|
||||
|
||||
extern int ntfs_file_values_compare(const FILE_NAME_ATTR *file_name_attr1,
|
||||
const FILE_NAME_ATTR *file_name_attr2,
|
||||
const int err_val, const IGNORE_CASE_BOOL ic,
|
||||
const ntfschar *upcase, const u32 upcase_len);
|
||||
|
||||
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 void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len);
|
||||
|
||||
extern ntfschar *ntfs_str2ucs(const char *s, int *len);
|
||||
|
||||
extern void ntfs_ucsfree(ntfschar *ucs);
|
||||
|
||||
#endif /* defined _NTFS_UNISTR_H */
|
||||
|
||||
@@ -0,0 +1,342 @@
|
||||
/**
|
||||
* unix_io.c - Unix style disk io functions. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2000-2006 Anton Altaparmakov
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file is distributed in the hope that it will be
|
||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDIO_H
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_IOCTL_H
|
||||
#include <sys/ioctl.h>
|
||||
#endif
|
||||
#ifdef HAVE_LINUX_FD_H
|
||||
#include <linux/fd.h>
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
#include "mst.h"
|
||||
#include "debug.h"
|
||||
#include "device.h"
|
||||
#include "logging.h"
|
||||
#include "misc.h"
|
||||
|
||||
#define DEV_FD(dev) (*(int *)dev->d_private)
|
||||
|
||||
/* Define to nothing if not present on this system. */
|
||||
#ifndef O_EXCL
|
||||
# define O_EXCL 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* ntfs_device_unix_io_open - Open a device and lock it exclusively
|
||||
* @dev:
|
||||
* @flags:
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
static int ntfs_device_unix_io_open(struct ntfs_device *dev, int flags)
|
||||
{
|
||||
struct flock flk;
|
||||
struct stat sbuf;
|
||||
int err;
|
||||
|
||||
if (NDevOpen(dev)) {
|
||||
errno = EBUSY;
|
||||
return -1;
|
||||
}
|
||||
if (stat(dev->d_name, &sbuf)) {
|
||||
ntfs_log_perror("Failed to access '%s'", dev->d_name);
|
||||
return -1;
|
||||
}
|
||||
if (S_ISBLK(sbuf.st_mode))
|
||||
NDevSetBlock(dev);
|
||||
|
||||
dev->d_private = ntfs_malloc(sizeof(int));
|
||||
if (!dev->d_private)
|
||||
return -1;
|
||||
/*
|
||||
* Open file for exclusive access if mounting r/w.
|
||||
* Fuseblk takes care about block devices.
|
||||
*/
|
||||
if (!NDevBlock(dev) && (flags & O_RDWR) == O_RDWR)
|
||||
flags |= O_EXCL;
|
||||
*(int*)dev->d_private = open(dev->d_name, flags);
|
||||
if (*(int*)dev->d_private == -1) {
|
||||
err = errno;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if ((flags & O_RDWR) != O_RDWR)
|
||||
NDevSetReadOnly(dev);
|
||||
|
||||
#ifndef _BEOS_
|
||||
memset(&flk, 0, sizeof(flk));
|
||||
if (NDevReadOnly(dev))
|
||||
flk.l_type = F_RDLCK;
|
||||
else
|
||||
flk.l_type = F_WRLCK;
|
||||
flk.l_whence = SEEK_SET;
|
||||
flk.l_start = flk.l_len = 0LL;
|
||||
if (fcntl(DEV_FD(dev), F_SETLK, &flk)) {
|
||||
err = errno;
|
||||
ntfs_log_perror("Failed to %s lock '%s'", NDevReadOnly(dev) ?
|
||||
"read" : "write", dev->d_name);
|
||||
if (close(DEV_FD(dev)))
|
||||
ntfs_log_perror("Failed to close '%s'", dev->d_name);
|
||||
goto err_out;
|
||||
}
|
||||
#endif
|
||||
|
||||
NDevSetOpen(dev);
|
||||
return 0;
|
||||
err_out:
|
||||
free(dev->d_private);
|
||||
dev->d_private = NULL;
|
||||
errno = err;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_unix_io_close - Close the device, releasing the lock
|
||||
* @dev:
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
static int ntfs_device_unix_io_close(struct ntfs_device *dev)
|
||||
{
|
||||
struct flock flk;
|
||||
|
||||
if (!NDevOpen(dev)) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
if (NDevDirty(dev))
|
||||
fsync(DEV_FD(dev));
|
||||
/* Release exclusive (mandatory) lock on the whole device. */
|
||||
#ifndef _BEOS_
|
||||
memset(&flk, 0, sizeof(flk));
|
||||
flk.l_type = F_UNLCK;
|
||||
flk.l_whence = SEEK_SET;
|
||||
flk.l_start = flk.l_len = 0LL;
|
||||
if (fcntl(DEV_FD(dev), F_SETLK, &flk))
|
||||
ntfs_log_perror("ntfs_device_unix_io_close: Warning: Could not "
|
||||
"unlock %s", dev->d_name);
|
||||
#endif
|
||||
/* Close the file descriptor and clear our open flag. */
|
||||
if (close(DEV_FD(dev)))
|
||||
return -1;
|
||||
NDevClearOpen(dev);
|
||||
free(dev->d_private);
|
||||
dev->d_private = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_unix_io_seek - Seek to a place on the device
|
||||
* @dev:
|
||||
* @offset:
|
||||
* @whence:
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
static s64 ntfs_device_unix_io_seek(struct ntfs_device *dev, s64 offset,
|
||||
int whence)
|
||||
{
|
||||
return lseek(DEV_FD(dev), offset, whence);
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_unix_io_read - Read from the device, from the current location
|
||||
* @dev:
|
||||
* @buf:
|
||||
* @count:
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
static s64 ntfs_device_unix_io_read(struct ntfs_device *dev, void *buf,
|
||||
s64 count)
|
||||
{
|
||||
return read(DEV_FD(dev), buf, count);
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_unix_io_write - Write to the device, at the current location
|
||||
* @dev:
|
||||
* @buf:
|
||||
* @count:
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
static s64 ntfs_device_unix_io_write(struct ntfs_device *dev, const void *buf,
|
||||
s64 count)
|
||||
{
|
||||
if (NDevReadOnly(dev)) {
|
||||
errno = EROFS;
|
||||
return -1;
|
||||
}
|
||||
NDevSetDirty(dev);
|
||||
return write(DEV_FD(dev), buf, count);
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_unix_io_pread - Perform a positioned read from the device
|
||||
* @dev:
|
||||
* @buf:
|
||||
* @count:
|
||||
* @offset:
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
static s64 ntfs_device_unix_io_pread(struct ntfs_device *dev, void *buf,
|
||||
s64 count, s64 offset)
|
||||
{
|
||||
#ifdef _BEOS_
|
||||
return read_pos(DEV_FD(dev), offset, buf,count);
|
||||
#else
|
||||
return pread(DEV_FD(dev), buf, count, offset);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_unix_io_pwrite - Perform a positioned write to the device
|
||||
* @dev:
|
||||
* @buf:
|
||||
* @count:
|
||||
* @offset:
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
static s64 ntfs_device_unix_io_pwrite(struct ntfs_device *dev, const void *buf,
|
||||
s64 count, s64 offset)
|
||||
{
|
||||
if (NDevReadOnly(dev)) {
|
||||
errno = EROFS;
|
||||
return -1;
|
||||
}
|
||||
NDevSetDirty(dev);
|
||||
#ifdef _BEOS_
|
||||
return write_pos(DEV_FD(dev), offset, buf,count);
|
||||
#else
|
||||
return pwrite(DEV_FD(dev), buf, count, offset);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_unix_io_sync - Flush any buffered changes to the device
|
||||
* @dev:
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
static int ntfs_device_unix_io_sync(struct ntfs_device *dev)
|
||||
{
|
||||
if (!NDevReadOnly(dev)) {
|
||||
int res = fsync(DEV_FD(dev));
|
||||
if (!res)
|
||||
NDevClearDirty(dev);
|
||||
return res;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_unix_io_stat - Get information about the device
|
||||
* @dev:
|
||||
* @buf:
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
static int ntfs_device_unix_io_stat(struct ntfs_device *dev, struct stat *buf)
|
||||
{
|
||||
return fstat(DEV_FD(dev), buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_unix_io_ioctl - Perform an ioctl on the device
|
||||
* @dev:
|
||||
* @request:
|
||||
* @argp:
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
static int ntfs_device_unix_io_ioctl(struct ntfs_device *dev, int request,
|
||||
void *argp)
|
||||
{
|
||||
return ioctl(DEV_FD(dev), request, argp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Device operations for working with unix style devices and files.
|
||||
*/
|
||||
struct ntfs_device_operations ntfs_device_unix_io_ops = {
|
||||
.open = ntfs_device_unix_io_open,
|
||||
.close = ntfs_device_unix_io_close,
|
||||
.seek = ntfs_device_unix_io_seek,
|
||||
.read = ntfs_device_unix_io_read,
|
||||
.write = ntfs_device_unix_io_write,
|
||||
.pread = ntfs_device_unix_io_pread,
|
||||
.pwrite = ntfs_device_unix_io_pwrite,
|
||||
.sync = ntfs_device_unix_io_sync,
|
||||
.stat = ntfs_device_unix_io_stat,
|
||||
.ioctl = ntfs_device_unix_io_ioctl,
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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_ */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* volume.h - Exports for NTFS volume handling. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2000-2004 Anton Altaparmakov
|
||||
* Copyright (c) 2004-2005 Richard Russon
|
||||
* Copyright (c) 2005-2006 Yura Pakhuchiy
|
||||
* Copyright (c) 2005-2006 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
|
||||
* 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_VOLUME_H
|
||||
#define _NTFS_VOLUME_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDIO_H
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_PARAM_H
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_MOUNT_H
|
||||
#include <sys/mount.h>
|
||||
#endif
|
||||
#ifdef HAVE_MNTENT_H
|
||||
#include <mntent.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Under Cygwin, DJGPP and FreeBSD we do not have MS_RDONLY and MS_NOATIME,
|
||||
* 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
|
||||
|
||||
/* Forward declaration */
|
||||
typedef struct _ntfs_volume ntfs_volume;
|
||||
|
||||
#include "types.h"
|
||||
#include "support.h"
|
||||
#include "device.h"
|
||||
#include "inode.h"
|
||||
#include "attrib.h"
|
||||
|
||||
/**
|
||||
* enum ntfs_mount_flags -
|
||||
*
|
||||
* Flags returned by the ntfs_check_if_mounted() function.
|
||||
*/
|
||||
typedef enum {
|
||||
NTFS_MF_MOUNTED = 1, /* Device is mounted. */
|
||||
NTFS_MF_ISROOT = 2, /* Device is mounted as system root. */
|
||||
NTFS_MF_READONLY = 4, /* Device is mounted read-only. */
|
||||
} ntfs_mount_flags;
|
||||
|
||||
extern int ntfs_check_if_mounted(const char *file, unsigned long *mnt_flags);
|
||||
|
||||
/**
|
||||
* enum ntfs_volume_state_bits -
|
||||
*
|
||||
* Defined bits for the state field in the ntfs_volume structure.
|
||||
*/
|
||||
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)
|
||||
#define set_nvol_flag(nv, flag) set_bit(NV_##flag, (nv)->state)
|
||||
#define clear_nvol_flag(nv, flag) clear_bit(NV_##flag, (nv)->state)
|
||||
|
||||
#define NVolReadOnly(nv) test_nvol_flag(nv, ReadOnly)
|
||||
#define NVolSetReadOnly(nv) set_nvol_flag(nv, ReadOnly)
|
||||
#define NVolClearReadOnly(nv) clear_nvol_flag(nv, ReadOnly)
|
||||
|
||||
#define NVolCaseSensitive(nv) test_nvol_flag(nv, CaseSensitive)
|
||||
#define NVolSetCaseSensitive(nv) set_nvol_flag(nv, CaseSensitive)
|
||||
#define NVolClearCaseSensitive(nv) clear_nvol_flag(nv, CaseSensitive)
|
||||
|
||||
#define NVolLogFileEmpty(nv) test_nvol_flag(nv, LogFileEmpty)
|
||||
#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
|
||||
* NTFS version 3.0 is used by Windows 2000.
|
||||
* NTFS version 3.1 is used by Windows XP, 2003 and Vista.
|
||||
*/
|
||||
|
||||
#define NTFS_V1_1(major, minor) ((major) == 1 && (minor) == 1)
|
||||
#define NTFS_V1_2(major, minor) ((major) == 1 && (minor) == 2)
|
||||
#define NTFS_V2_X(major, minor) ((major) == 2)
|
||||
#define NTFS_V3_0(major, minor) ((major) == 3 && (minor) == 0)
|
||||
#define NTFS_V3_1(major, minor) ((major) == 3 && (minor) == 1)
|
||||
|
||||
#define NTFS_BUF_SIZE 8192
|
||||
|
||||
/**
|
||||
* struct _ntfs_volume - structure describing an open volume in memory.
|
||||
*/
|
||||
struct _ntfs_volume {
|
||||
union {
|
||||
struct ntfs_device *dev; /* NTFS device associated with
|
||||
the volume. */
|
||||
void *sb; /* For kernel porting compatibility. */
|
||||
};
|
||||
char *vol_name; /* Name of the volume. */
|
||||
unsigned long state; /* NTFS specific flags describing this volume.
|
||||
See ntfs_volume_state_bits above. */
|
||||
|
||||
ntfs_inode *vol_ni; /* ntfs_inode structure for FILE_Volume. */
|
||||
u8 major_ver; /* Ntfs major version of volume. */
|
||||
u8 minor_ver; /* Ntfs minor version of volume. */
|
||||
u16 flags; /* Bit array of VOLUME_* flags. */
|
||||
|
||||
u16 sector_size; /* Byte size of a sector. */
|
||||
u8 sector_size_bits; /* Log(2) of the byte size of a sector. */
|
||||
u32 cluster_size; /* Byte size of a cluster. */
|
||||
u32 mft_record_size; /* Byte size of a mft record. */
|
||||
u32 indx_record_size; /* Byte size of a INDX record. */
|
||||
u8 cluster_size_bits; /* Log(2) of the byte size of a cluster. */
|
||||
u8 mft_record_size_bits;/* Log(2) of the byte size of a mft record. */
|
||||
u8 indx_record_size_bits;/* Log(2) of the byte size of a INDX record. */
|
||||
|
||||
/* Variables used by the cluster and mft allocators. */
|
||||
u8 mft_zone_multiplier; /* Initial mft zone multiplier. */
|
||||
s64 mft_data_pos; /* Mft record number at which to allocate the
|
||||
next mft record. */
|
||||
LCN mft_zone_start; /* First cluster of the mft zone. */
|
||||
LCN mft_zone_end; /* First cluster beyond the mft zone. */
|
||||
LCN mft_zone_pos; /* Current position in the mft zone. */
|
||||
LCN data1_zone_pos; /* Current position in the first data zone. */
|
||||
LCN data2_zone_pos; /* Current position in the second data zone. */
|
||||
|
||||
s64 nr_clusters; /* Volume size in clusters, hence also the
|
||||
number of bits in lcn_bitmap. */
|
||||
ntfs_inode *lcnbmp_ni; /* ntfs_inode structure for FILE_Bitmap. */
|
||||
ntfs_attr *lcnbmp_na; /* ntfs_attr structure for the data attribute
|
||||
of FILE_Bitmap. Each bit represents a
|
||||
cluster on the volume, bit 0 representing
|
||||
lcn 0 and so on. A set bit means that the
|
||||
cluster and vice versa. */
|
||||
|
||||
LCN mft_lcn; /* Logical cluster number of the data attribute
|
||||
for FILE_MFT. */
|
||||
ntfs_inode *mft_ni; /* ntfs_inode structure for FILE_MFT. */
|
||||
ntfs_attr *mft_na; /* ntfs_attr structure for the data attribute
|
||||
of FILE_MFT. */
|
||||
ntfs_attr *mftbmp_na; /* ntfs_attr structure for the bitmap attribute
|
||||
of FILE_MFT. Each bit represents an mft
|
||||
record in the $DATA attribute, bit 0
|
||||
representing mft record 0 and so on. A set
|
||||
bit means that the mft record is in use and
|
||||
vice versa. */
|
||||
|
||||
int mftmirr_size; /* Size of the FILE_MFTMirr in mft records. */
|
||||
LCN mftmirr_lcn; /* Logical cluster number of the data attribute
|
||||
for FILE_MFTMirr. */
|
||||
ntfs_inode *mftmirr_ni; /* ntfs_inode structure for FILE_MFTMirr. */
|
||||
ntfs_attr *mftmirr_na; /* ntfs_attr structure for the data attribute
|
||||
of FILE_MFTMirr. */
|
||||
|
||||
ntfschar *upcase; /* Upper case equivalents of all 65536 2-byte
|
||||
Unicode characters. Obtained from
|
||||
FILE_UpCase. */
|
||||
u32 upcase_len; /* Length in Unicode characters of the upcase
|
||||
table. */
|
||||
|
||||
ATTR_DEF *attrdef; /* Attribute definitions. Obtained from
|
||||
FILE_AttrDef. */
|
||||
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 */
|
||||
};
|
||||
|
||||
extern ntfs_volume *ntfs_volume_alloc(void);
|
||||
|
||||
extern ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev,
|
||||
unsigned long flags);
|
||||
|
||||
extern ntfs_volume *ntfs_device_mount(struct ntfs_device *dev,
|
||||
unsigned long flags);
|
||||
extern int ntfs_device_umount(ntfs_volume *vol, const BOOL force);
|
||||
|
||||
extern ntfs_volume *ntfs_mount(const char *name, unsigned long flags);
|
||||
extern int ntfs_umount(ntfs_volume *vol, const BOOL force);
|
||||
|
||||
extern int ntfs_version_is_supported(ntfs_volume *vol);
|
||||
extern int ntfs_logfile_reset(ntfs_volume *vol);
|
||||
|
||||
extern int ntfs_volume_write_flags(ntfs_volume *vol, const u16 flags);
|
||||
|
||||
#endif /* defined _NTFS_VOLUME_H */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,14 @@
|
||||
#ifndef _LOCK_H
|
||||
#define _LOCK_H
|
||||
|
||||
#include <OS.h>
|
||||
|
||||
#include <lock.h>
|
||||
|
||||
typedef recursive_lock lock;
|
||||
|
||||
#define LOCK(l) recursive_lock_lock(&l);
|
||||
#define UNLOCK(l) recursive_lock_unlock(&l);
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
Copyright 1999-2001, Be Incorporated. All Rights Reserved.
|
||||
This file may be used under the terms of the Be Sample Code License.
|
||||
*/
|
||||
|
||||
struct ext_mime {
|
||||
char *extension;
|
||||
char *mime;
|
||||
};
|
||||
|
||||
struct ext_mime mimes[] = {
|
||||
{ "***", "application/x-vnd.Be-directory"},
|
||||
|
||||
{ "gz", "application/x-gzip" },
|
||||
{ "hqx", "application/x-binhex40" },
|
||||
{ "lha", "application/x-lharc" },
|
||||
{ "pcl", "application/x-pcl" },
|
||||
{ "pdf", "application/pdf" },
|
||||
{ "ps", "application/postscript" },
|
||||
{ "sit", "application/x-stuff-it" },
|
||||
{ "tar", "application/x-tar" },
|
||||
{ "tgz", "application/x-gzip" },
|
||||
{ "uue", "application/x-uuencode" },
|
||||
{ "z", "application/x-compress" },
|
||||
{ "zip", "application/zip" },
|
||||
{ "zoo", "application/x-zoo" },
|
||||
{ "rar", "application/x-rar-compressed" },
|
||||
{ "pkg", "application/x-scode-UPkg" },
|
||||
|
||||
{ "aif", "audio/x-aiff" },
|
||||
{ "aiff", "audio/x-aiff" },
|
||||
{ "au", "audio/basic" },
|
||||
{ "mid", "audio/x-midi" },
|
||||
{ "midi", "audio/x-midi" },
|
||||
{ "mod", "audio/mod" },
|
||||
{ "ra", "audio/x-real-audio" },
|
||||
{ "wav", "audio/x-wav" },
|
||||
{ "mp3", "audio/x-mpeg" },
|
||||
{ "ogg", "audio/x-vorbis" },
|
||||
|
||||
{ "avi", "video/x-msvideo" },
|
||||
{ "mov", "video/quicktime" },
|
||||
{ "mpg", "video/mpeg" },
|
||||
{ "mpeg", "video/mpeg" },
|
||||
{ "asf", "application/x-asf" },
|
||||
{ "rm", "video/vnd.rn-realvideo" },
|
||||
|
||||
{ "bmp", "image/x-bmp" },
|
||||
{ "fax", "image/g3fax" },
|
||||
{ "gif", "image/gif" },
|
||||
{ "iff", "image/x-iff" },
|
||||
{ "jpg", "image/jpeg" },
|
||||
{ "jpeg", "image/jpeg" },
|
||||
{ "pbm", "image/x-portable-bitmap" },
|
||||
{ "pcx", "image/x-pcx" },
|
||||
{ "pgm", "image/x-portable-graymap" },
|
||||
{ "png", "image/png" },
|
||||
{ "ppm", "image/x-portable-pixmap" },
|
||||
{ "rgb", "image/x-rgb" },
|
||||
{ "tga", "image/x-targa" },
|
||||
{ "tif", "image/tiff" },
|
||||
{ "tiff", "image/tiff" },
|
||||
{ "xbm", "image/x-xbitmap" },
|
||||
{ "djvu", "image/x-djvu" },
|
||||
|
||||
{ "txt", "text/plain" },
|
||||
{ "xml", "text/plain" },
|
||||
{ "doc", "text/plain" },
|
||||
{ "htm", "text/html" },
|
||||
{ "html", "text/html" },
|
||||
{ "rtf", "text/rtf" },
|
||||
{ "c", "text/x-source-code" },
|
||||
{ "cc", "text/x-source-code" },
|
||||
{ "c++", "text/x-source-code" },
|
||||
{ "h", "text/x-source-code" },
|
||||
{ "hh", "text/x-source-code" },
|
||||
{ "cxx", "text/x-source-code" },
|
||||
{ "cpp", "text/x-source-code" },
|
||||
{ "S", "text/x-source-code" },
|
||||
{ "java", "text/x-source-code" },
|
||||
|
||||
{ "exe", "application/x-vnd.Be-elfexecutable" },
|
||||
{ "dll", "application/x-vnd.Be.ELF-object" },
|
||||
|
||||
{ "ttf", "application/x-truetype" },
|
||||
|
||||
{ 0, 0 }
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
Copyright 1999-2001, Be Incorporated. All Rights Reserved.
|
||||
This file may be used under the terms of the Be Sample Code License.
|
||||
*/
|
||||
|
||||
#ifndef MIME_TYPES_H
|
||||
#define MIME_TYPES_H
|
||||
|
||||
struct ext_mime {
|
||||
char *extension;
|
||||
char *mime;
|
||||
};
|
||||
|
||||
extern struct ext_mime mimes[];
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (c) 2006 Troeglazov Gerasim (3dEyes**)
|
||||
*
|
||||
* 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 Linux-NTFS
|
||||
* 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_H
|
||||
#define _NTFS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <KernelExport.h>
|
||||
#include <NodeMonitor.h>
|
||||
#include <fs_interface.h>
|
||||
#include <fs_cache.h>
|
||||
#include <fs_attr.h>
|
||||
#include <fs_info.h>
|
||||
#include <fs_index.h>
|
||||
#include <fs_query.h>
|
||||
#include <fs_volume.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "attrib.h"
|
||||
#include "inode.h"
|
||||
#include "volume.h"
|
||||
#include "dir.h"
|
||||
#include "unistr.h"
|
||||
#include "layout.h"
|
||||
#include "index.h"
|
||||
#include "utils.h"
|
||||
#include "version.h"
|
||||
#include "ntfstime.h"
|
||||
#include "misc.h"
|
||||
#include "utils.h"
|
||||
#include "bootsect.h"
|
||||
|
||||
#include "lock.h"
|
||||
#include "ntfsdir.h"
|
||||
|
||||
#define ERRPRINT(...) ;
|
||||
//#define ERRPRINT dprintf
|
||||
|
||||
#define DEV_FD(dev) (*(int *)dev->d_private)
|
||||
|
||||
#define LOCK_VOL(vol) \
|
||||
{ \
|
||||
if (vol == NULL) { \
|
||||
return EINVAL; \
|
||||
} \
|
||||
\
|
||||
LOCK((vol)->vlock); \
|
||||
}
|
||||
|
||||
#define UNLOCK_VOL(vol) \
|
||||
{ \
|
||||
UNLOCK((vol)->vlock); \
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
NF_FreeClustersOutdate = (1 << 0), // Information about amount of free clusters is outdated.
|
||||
NF_FreeMFTOutdate = (1 << 1), // Information about amount of free MFT records is outdated.
|
||||
} ntfs_state_bits;
|
||||
|
||||
|
||||
typedef struct vnode
|
||||
{
|
||||
u64 vnid;
|
||||
u64 parent_vnid;
|
||||
char *mime;
|
||||
} vnode;
|
||||
|
||||
typedef struct filecookie
|
||||
{
|
||||
int omode;
|
||||
off_t last_size;
|
||||
} filecookie;
|
||||
|
||||
#define ntfs_mark_free_space_outdated(ns) (ns->state |= (NF_FreeClustersOutdate | NF_FreeMFTOutdate));
|
||||
|
||||
|
||||
|
||||
typedef struct nspace
|
||||
{
|
||||
ntfs_volume *ntvol;
|
||||
char devicePath[MAX_PATH];
|
||||
mount_id id;
|
||||
int free_cluster_count;
|
||||
char volumeLabel[MAX_PATH];
|
||||
|
||||
int state;
|
||||
s64 free_clusters;
|
||||
long free_mft;
|
||||
unsigned int uid;
|
||||
unsigned int gid;
|
||||
unsigned int fmask;
|
||||
unsigned int dmask;
|
||||
BOOL ro;
|
||||
BOOL show_sys_files;
|
||||
BOOL silent;
|
||||
BOOL force;
|
||||
BOOL debug;
|
||||
BOOL noatime;
|
||||
BOOL no_detach;
|
||||
|
||||
lock vlock;
|
||||
|
||||
} nspace;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,249 @@
|
||||
/* ntfsdir.c - directory functions
|
||||
*
|
||||
* Copyright (c) 2006 Troeglazov Gerasim (3dEyes**)
|
||||
*
|
||||
* 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 Linux-NTFS
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <KernelExport.h>
|
||||
#include <time.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include <KernelExport.h>
|
||||
#include <NodeMonitor.h>
|
||||
#include <fs_interface.h>
|
||||
#include <fs_cache.h>
|
||||
#include <fs_attr.h>
|
||||
#include <fs_info.h>
|
||||
#include <fs_index.h>
|
||||
#include <fs_query.h>
|
||||
#include <fs_volume.h>
|
||||
|
||||
#include "ntfs.h"
|
||||
#include "attributes.h"
|
||||
#include "lock.h"
|
||||
#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)
|
||||
{
|
||||
char *filename = NULL;
|
||||
dircookie *cookie = (dircookie*)_dirent;
|
||||
|
||||
if (name_type == FILE_NAME_DOS)
|
||||
return 0;
|
||||
|
||||
if (MREF(mref) == FILE_root || MREF(mref) >= FILE_first_user || cookie->show_sys_files) {
|
||||
if(cookie->readed==1) {
|
||||
cookie->pos=pos;
|
||||
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;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
fs_free_dircookie( void *_ns, void *node, void *cookie )
|
||||
{
|
||||
nspace *ns = (nspace*)_ns;
|
||||
|
||||
LOCK_VOL(ns);
|
||||
ERRPRINT("fs_free_dircookie - ENTER\n");
|
||||
if ( cookie != NULL )
|
||||
free( cookie );
|
||||
|
||||
ERRPRINT("fs_free_dircookie - EXIT\n");
|
||||
|
||||
UNLOCK_VOL(ns);
|
||||
|
||||
return B_NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
fs_opendir( void *_ns, void *_node, void **_cookie )
|
||||
{
|
||||
nspace *ns = (nspace*)_ns;
|
||||
vnode *node = (vnode*)_node;
|
||||
dircookie *cookie = (dircookie*)ntfs_calloc( sizeof(dircookie) );
|
||||
int result = B_NO_ERROR;
|
||||
ntfs_inode *ni=NULL;
|
||||
|
||||
LOCK_VOL(ns);
|
||||
|
||||
ERRPRINT("fs_opendir - ENTER\n");
|
||||
|
||||
ni = ntfs_inode_open(ns->ntvol, node->vnid);
|
||||
if(ni==NULL) {
|
||||
result = ENOENT;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (!(ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) {
|
||||
result = EMFILE;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if ( cookie != NULL ) {
|
||||
cookie->pos = 0;
|
||||
cookie->ino = 0;
|
||||
cookie->readed = 0;
|
||||
cookie->name[0] = 0;
|
||||
cookie->show_sys_files = ns->show_sys_files;
|
||||
*_cookie = (void*)cookie;
|
||||
}
|
||||
else
|
||||
result = ENOMEM;
|
||||
exit:
|
||||
if(ni)
|
||||
ntfs_inode_close(ni);
|
||||
|
||||
ERRPRINT("fs_opendir - EXIT\n");
|
||||
|
||||
UNLOCK_VOL(ns);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
status_t
|
||||
fs_closedir( void *_ns, void *node, void *_cookie )
|
||||
{
|
||||
nspace *ns = (nspace*)_ns;
|
||||
|
||||
LOCK_VOL(ns);
|
||||
|
||||
ERRPRINT("fs_closedir - ENTER\n");
|
||||
|
||||
ERRPRINT("fs_closedir - EXIT\n");
|
||||
|
||||
UNLOCK_VOL(ns);
|
||||
|
||||
return B_NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
fs_readdir( void *_ns, void *_node, void *_cookie, struct dirent *buf, size_t bufsize, uint32 *num )
|
||||
{
|
||||
nspace *ns = (nspace*)_ns;
|
||||
vnode *node = (vnode*)_node;
|
||||
dircookie *cookie = (dircookie*)_cookie;
|
||||
uint32 nameLength = bufsize - sizeof(buf) + 1, realLen;
|
||||
int result = B_NO_ERROR;
|
||||
ntfs_inode *ni=NULL;
|
||||
|
||||
LOCK_VOL(ns);
|
||||
|
||||
ERRPRINT("fs_readdir - ENTER\n");
|
||||
|
||||
if (!ns || !node || !cookie || !num || bufsize < sizeof(buf)) {
|
||||
result = EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if(cookie->readed == 1) {
|
||||
result = ENOENT;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ni = ntfs_inode_open(ns->ntvol, node->vnid);
|
||||
if(ni==NULL) {
|
||||
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;
|
||||
|
||||
ERRPRINT("fs_readdir - FILE: [%s]\n",buf->d_name);
|
||||
|
||||
exit:
|
||||
if(ni)
|
||||
ntfs_inode_close(ni);
|
||||
|
||||
if ( result == B_NO_ERROR )
|
||||
*num = 1;
|
||||
else
|
||||
*num = 0;
|
||||
|
||||
if ( result == ENOENT )
|
||||
result = B_NO_ERROR;
|
||||
|
||||
ERRPRINT("fs_readdir - EXIT result (%s)\n", strerror(result));
|
||||
|
||||
UNLOCK_VOL(ns);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
status_t
|
||||
fs_rewinddir( void *_ns, void *_node, void *_cookie )
|
||||
{
|
||||
nspace *ns = (nspace*)_ns;
|
||||
dircookie *cookie = (dircookie*)_cookie;
|
||||
int result = EINVAL;
|
||||
|
||||
LOCK_VOL(ns);
|
||||
|
||||
ERRPRINT("fs_rewinddir - ENTER\n");
|
||||
if ( cookie != NULL ) {
|
||||
cookie->pos = 0;
|
||||
cookie->ino = 0;
|
||||
cookie->readed = 0;
|
||||
cookie->name[0] = 0;
|
||||
result = B_NO_ERROR;
|
||||
}
|
||||
ERRPRINT("fs_rewinddir - EXIT, result is %s\n", strerror(result));
|
||||
|
||||
UNLOCK_VOL(ns);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/* ntfsdir.h - directory functions
|
||||
*
|
||||
* Copyright (c) 2006 Troeglazov Gerasim (3dEyes**)
|
||||
*
|
||||
* 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 Linux-NTFS
|
||||
* 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 _NTFSDIR_H
|
||||
#define _NTFSDIR_H
|
||||
|
||||
|
||||
typedef struct dircookie
|
||||
{
|
||||
u64 pos;
|
||||
int readed;
|
||||
ino_t ino;
|
||||
BOOL show_sys_files;
|
||||
char name[MAX_PATH];
|
||||
} dircookie;
|
||||
|
||||
status_t fs_free_dircookie( void *_ns, void *node, void *cookie );
|
||||
status_t fs_opendir( void *_ns, void *_node, void **_cookie );
|
||||
status_t fs_closedir( void *_ns, void *node, void *_cookie );
|
||||
status_t fs_rewinddir( void *_ns, void *_node, void *_cookie );
|
||||
status_t fs_readdir( void *_ns, void *_node, void *_cookie, struct dirent *buf, size_t bufsize, uint32 *num );
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* utils.h - Part of the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2002-2005 Richard Russon
|
||||
* Copyright (c) 2004 Anton Altaparmakov
|
||||
*
|
||||
* A set of shared functions for ntfs utilities
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the Linux-NTFS
|
||||
* 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_UTILS_H_
|
||||
#define _NTFS_UTILS_H_
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "types.h"
|
||||
#include "layout.h"
|
||||
#include "volume.h"
|
||||
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDARG_H
|
||||
#include <stdarg.h>
|
||||
#endif
|
||||
|
||||
extern const char *ntfs_bugs;
|
||||
extern const char *ntfs_home;
|
||||
extern const char *ntfs_gpl;
|
||||
|
||||
//int utils_set_locale(void);
|
||||
int utils_parse_size(const char *value, s64 *size, BOOL scale);
|
||||
int utils_parse_range(const char *string, s64 *start, s64 *finish, BOOL scale);
|
||||
int utils_inode_get_name(ntfs_inode *inode, char *buffer, int bufsize);
|
||||
int utils_inode_get_ucsfilename(ntfs_inode *inode,ntfschar **, int *size);
|
||||
int utils_attr_get_name(ntfs_volume *vol, ATTR_RECORD *attr, char *buffer, int bufsize);
|
||||
int utils_cluster_in_use(ntfs_volume *vol, long long lcn);
|
||||
int utils_mftrec_in_use(ntfs_volume *vol, MFT_REF mref);
|
||||
int utils_is_metadata(ntfs_inode *inode);
|
||||
void utils_dump_mem(void *buf, int start, int length, int flags);
|
||||
MFT_REF ntfs_get_parent_ref(ntfs_inode *ni);
|
||||
|
||||
ATTR_RECORD * find_attribute(const ATTR_TYPES type, ntfs_attr_search_ctx *ctx);
|
||||
ATTR_RECORD * find_first_attribute(const ATTR_TYPES type, MFT_RECORD *mft);
|
||||
|
||||
int utils_valid_device(const char *name, int force);
|
||||
ntfs_volume * utils_mount_volume(const char *device, unsigned long flags, BOOL force);
|
||||
|
||||
/**
|
||||
* defines...
|
||||
* if *not in use* then the other flags are ignored?
|
||||
*/
|
||||
#define FEMR_IN_USE (1 << 0)
|
||||
#define FEMR_NOT_IN_USE (1 << 1)
|
||||
#define FEMR_FILE (1 << 2) // $DATA
|
||||
#define FEMR_DIR (1 << 3) // $INDEX_ROOT, "$I30"
|
||||
#define FEMR_METADATA (1 << 4)
|
||||
#define FEMR_NOT_METADATA (1 << 5)
|
||||
#define FEMR_BASE_RECORD (1 << 6)
|
||||
#define FEMR_NOT_BASE_RECORD (1 << 7)
|
||||
#define FEMR_ALL_RECORDS 0xFF
|
||||
|
||||
/**
|
||||
* struct mft_search_ctx
|
||||
*/
|
||||
struct mft_search_ctx {
|
||||
int flags_search;
|
||||
int flags_match;
|
||||
ntfs_inode *inode;
|
||||
ntfs_volume *vol;
|
||||
u64 mft_num;
|
||||
};
|
||||
|
||||
struct mft_search_ctx * mft_get_search_ctx(ntfs_volume *vol);
|
||||
void mft_put_search_ctx(struct mft_search_ctx *ctx);
|
||||
int mft_next_record(struct mft_search_ctx *ctx);
|
||||
|
||||
// Flags for dump mem
|
||||
#define DM_DEFAULTS 0
|
||||
#define DM_NO_ASCII (1 << 0)
|
||||
#define DM_NO_DIVIDER (1 << 1)
|
||||
#define DM_INDENT (1 << 2)
|
||||
#define DM_RED (1 << 3)
|
||||
#define DM_GREEN (1 << 4)
|
||||
#define DM_BLUE (1 << 5)
|
||||
#define DM_BOLD (1 << 6)
|
||||
|
||||
#endif /* _NTFS_UTILS_H_ */
|
||||
@@ -0,0 +1,222 @@
|
||||
/* volume_util.c - volume functions
|
||||
*
|
||||
* Copyright (c) 2006 Troeglazov Gerasim (3dEyes**)
|
||||
*
|
||||
* 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 Linux-NTFS
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "support.h"
|
||||
#include "endians.h"
|
||||
#include "bootsect.h"
|
||||
#include "device.h"
|
||||
#include "attrib.h"
|
||||
#include "volume.h"
|
||||
#include "mft.h"
|
||||
#include "bitmap.h"
|
||||
#include "inode.h"
|
||||
#include "runlist.h"
|
||||
#include "utils.h"
|
||||
#include "version.h"
|
||||
#include "ntfs.h"
|
||||
#include "volume_util.h"
|
||||
|
||||
static unsigned char ntfs_bits_table[] = { 4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0 };
|
||||
|
||||
static uint8 ntfs_count_bits(unsigned char byte, unsigned char shift)
|
||||
{
|
||||
int i;
|
||||
unsigned char counter = 0;
|
||||
|
||||
if(shift < 8)
|
||||
{
|
||||
for(i=0; i<shift; i++)
|
||||
{
|
||||
if(!(byte & 0x80))counter++;
|
||||
byte = byte << 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
counter += ntfs_bits_table[byte & 0x0F];
|
||||
counter += ntfs_bits_table[(byte & 0xF0) >> 4];
|
||||
}
|
||||
|
||||
return counter;
|
||||
}
|
||||
|
||||
|
||||
int ntfs_calc_free_space(nspace *_ns)
|
||||
{
|
||||
nspace *ns = (nspace*)_ns;
|
||||
ntfs_volume *vol = ns->ntvol;
|
||||
ntfs_attr *data = vol->lcnbmp_na;
|
||||
s64 free_clusters = 0;
|
||||
off_t pos = 0;
|
||||
size_t readed;
|
||||
unsigned char *buf = NULL;
|
||||
|
||||
if(ns== NULL || vol==NULL || data==NULL)
|
||||
return -1;
|
||||
|
||||
if(vol->lcnbmp_na==NULL)
|
||||
return -1;
|
||||
|
||||
if (!(ns->state & NF_FreeClustersOutdate))
|
||||
return -1;
|
||||
|
||||
buf = (unsigned char*)ntfs_malloc(vol->cluster_size);
|
||||
if(!buf)goto exit;
|
||||
|
||||
while( pos < data->data_size)
|
||||
{
|
||||
if( pos % vol->cluster_size == 0)
|
||||
{
|
||||
readed = ntfs_attr_pread(vol->lcnbmp_na, pos, min(data->data_size - pos, vol->cluster_size), buf);
|
||||
if(readed < B_NO_ERROR) goto error;
|
||||
if(readed != min(data->data_size - pos, vol->cluster_size))
|
||||
goto error;
|
||||
}
|
||||
|
||||
free_clusters += ntfs_count_bits( buf[pos%vol->cluster_size], min( (vol->nr_clusters) - (pos * 8), 8));
|
||||
pos++;
|
||||
}
|
||||
error:
|
||||
if(buf)
|
||||
free(buf);
|
||||
exit:
|
||||
ns->free_clusters = free_clusters;
|
||||
ns->state &= ~(NF_FreeClustersOutdate);
|
||||
|
||||
return B_NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static int resize_resident_attribute_value(MFT_RECORD *m, ATTR_RECORD *a,
|
||||
const u32 new_vsize)
|
||||
{
|
||||
int new_alen, new_muse;
|
||||
|
||||
/* New attribute length and mft record bytes used. */
|
||||
new_alen = (le16_to_cpu(a->value_offset) + new_vsize + 7) & ~7;
|
||||
new_muse = le32_to_cpu(m->bytes_in_use) - le32_to_cpu(a->length) +
|
||||
new_alen;
|
||||
/* Check for sufficient space. */
|
||||
if ((u32)new_muse > le32_to_cpu(m->bytes_allocated)) {
|
||||
errno = ENOSPC;
|
||||
return -1;
|
||||
}
|
||||
/* Move attributes behind @a to their new location. */
|
||||
memmove((char*)a + new_alen, (char*)a + le32_to_cpu(a->length),
|
||||
le32_to_cpu(m->bytes_in_use) - ((char*)a - (char*)m) -
|
||||
le32_to_cpu(a->length));
|
||||
/* Adjust @m to reflect change in used space. */
|
||||
m->bytes_in_use = cpu_to_le32(new_muse);
|
||||
/* Adjust @a to reflect new value size. */
|
||||
a->length = cpu_to_le32(new_alen);
|
||||
a->value_length = cpu_to_le32(new_vsize);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int ntfs_change_label(ntfs_volume *vol, char *label)
|
||||
{
|
||||
ntfs_attr_search_ctx *ctx;
|
||||
ntfschar *new_label = NULL;
|
||||
ATTR_RECORD *a;
|
||||
int label_len;
|
||||
int result = 0;
|
||||
|
||||
ctx = ntfs_attr_get_search_ctx(vol->vol_ni, NULL);
|
||||
if (!ctx) {
|
||||
ntfs_log_perror("Failed to get attribute search context");
|
||||
goto err_out;
|
||||
}
|
||||
if (ntfs_attr_lookup(AT_VOLUME_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0,
|
||||
ctx)) {
|
||||
if (errno != ENOENT) {
|
||||
ntfs_log_perror("Lookup of $VOLUME_NAME attribute failed");
|
||||
goto err_out;
|
||||
}
|
||||
/* The volume name attribute does not exist. Need to add it. */
|
||||
a = NULL;
|
||||
} else {
|
||||
a = ctx->attr;
|
||||
if (a->non_resident) {
|
||||
ntfs_log_error("Error: Attribute $VOLUME_NAME must be "
|
||||
"resident.\n");
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
label_len = ntfs_mbstoucs(label, &new_label, 0);
|
||||
if (label_len == -1) {
|
||||
ntfs_log_perror("Unable to convert label string to Unicode");
|
||||
goto err_out;
|
||||
}
|
||||
label_len *= sizeof(ntfschar);
|
||||
if (label_len > 0x100) {
|
||||
ntfs_log_error("New label is too long. Maximum %u characters "
|
||||
"allowed. Truncating excess characters.\n",
|
||||
(unsigned)(0x100 / sizeof(ntfschar)));
|
||||
label_len = 0x100;
|
||||
new_label[label_len / sizeof(ntfschar)] = cpu_to_le16(L'\0');
|
||||
}
|
||||
if (a) {
|
||||
if (resize_resident_attribute_value(ctx->mrec, a, label_len)) {
|
||||
ntfs_log_perror("Error resizing resident attribute");
|
||||
goto err_out;
|
||||
}
|
||||
} else {
|
||||
/* sizeof(resident attribute record header) == 24 */
|
||||
int asize = (24 + label_len + 7) & ~7;
|
||||
u32 biu = le32_to_cpu(ctx->mrec->bytes_in_use);
|
||||
if (biu + asize > le32_to_cpu(ctx->mrec->bytes_allocated)) {
|
||||
errno = ENOSPC;
|
||||
ntfs_log_perror("Error adding resident attribute");
|
||||
goto err_out;
|
||||
}
|
||||
a = ctx->attr;
|
||||
memmove((u8*)a + asize, a, biu - ((u8*)a - (u8*)ctx->mrec));
|
||||
ctx->mrec->bytes_in_use = cpu_to_le32(biu + asize);
|
||||
a->type = AT_VOLUME_NAME;
|
||||
a->length = cpu_to_le32(asize);
|
||||
a->non_resident = 0;
|
||||
a->name_length = 0;
|
||||
a->name_offset = cpu_to_le16(24);
|
||||
a->flags = cpu_to_le16(0);
|
||||
a->instance = ctx->mrec->next_attr_instance;
|
||||
ctx->mrec->next_attr_instance = cpu_to_le16((le16_to_cpu(
|
||||
ctx->mrec->next_attr_instance) + 1) & 0xffff);
|
||||
a->value_length = cpu_to_le32(label_len);
|
||||
a->value_offset = a->name_offset;
|
||||
a->resident_flags = 0;
|
||||
a->reservedR = 0;
|
||||
}
|
||||
memcpy((u8*)a + le16_to_cpu(a->value_offset), new_label, label_len);
|
||||
if (ntfs_inode_sync(vol->vol_ni)) {
|
||||
ntfs_log_perror("Error writing MFT Record to disk");
|
||||
goto err_out;
|
||||
}
|
||||
result = 0;
|
||||
err_out:
|
||||
free(new_label);
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/* volume_util.h - volume functions
|
||||
*
|
||||
* Copyright (c) 2006 Troeglazov Gerasim (3dEyes**)
|
||||
*
|
||||
* 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 Linux-NTFS
|
||||
* 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 _NTFSFS_VOLUTIL_H_
|
||||
#define _NTFSFS_VOLUTIL_H_
|
||||
|
||||
int ntfs_calc_free_space(nspace *ns);
|
||||
int ntfs_change_label(ntfs_volume *vol, char *label);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user