blob: 2f290b4de09c5bd2097478230ace8deb2069325a [file] [log] [blame]
/*
* support.c - Specific support functions
*
* Copyright (C) 1997 Martin von Löwis
* Copyright (C) 1997 Régis Duchesne
* Copyright (C) 2001 Anton Altaparmakov (AIA)
*/
#include "ntfstypes.h"
#include "struct.h"
#include "support.h"
#include <stdarg.h>
#include <linux/slab.h>
#include <linux/locks.h>
#include <linux/fs.h>
#include "util.h"
#include "inode.h"
#include "macros.h"
#include <linux/nls.h>
static char print_buf[1024];
#ifdef DEBUG
#include "sysctl.h"
#include <linux/kernel.h>
/* Debugging output */
void ntfs_debug(int mask, const char *fmt, ...)
{
va_list ap;
/* Filter it with the debugging level required */
if (ntdebug & mask) {
va_start(ap,fmt);
strcpy(print_buf, KERN_DEBUG "NTFS: ");
vsprintf(print_buf + 9, fmt, ap);
printk(print_buf);
va_end(ap);
}
}
#ifndef ntfs_malloc
/* Verbose kmalloc */
void *ntfs_malloc(int size)
{
void *ret;
ret = kmalloc(size, GFP_KERNEL);
ntfs_debug(DEBUG_MALLOC, "Allocating %x at %p\n", size, ret);
return ret;
}
#endif
#ifndef ntfs_free
/* Verbose kfree() */
void ntfs_free(void *block)
{
ntfs_debug(DEBUG_MALLOC, "Freeing memory at %p\n", block);
kfree(block);
}
#endif
#else /* End of DEBUG functions. Normal ones below... */
#ifndef ntfs_malloc
void *ntfs_malloc(int size)
{
return kmalloc(size, GFP_KERNEL);
}
#endif
#ifndef ntfs_free
void ntfs_free(void *block)
{
kfree(block);
}
#endif
#endif /* DEBUG */
void ntfs_bzero(void *s, int n)
{
memset(s, 0, n);
}
/* These functions deliberately return no value. It is dest, anyway,
and not used anywhere in the NTFS code. */
void ntfs_memcpy(void *dest, const void *src, ntfs_size_t n)
{
memcpy(dest, src, n);
}
void ntfs_memmove(void *dest, const void *src, ntfs_size_t n)
{
memmove(dest, src, n);
}
/* Warn that an error occurred. */
void ntfs_error(const char *fmt,...)
{
va_list ap;
va_start(ap, fmt);
strcpy(print_buf, KERN_ERR "NTFS: ");
vsprintf(print_buf + 9, fmt, ap);
printk(print_buf);
va_end(ap);
}
int ntfs_read_mft_record(ntfs_volume *vol, int mftno, char *buf)
{
int error;
ntfs_io io;
ntfs_debug(DEBUG_OTHER, "read_mft_record 0x%x\n", mftno);
if (mftno == FILE_Mft)
{
ntfs_memcpy(buf, vol->mft, vol->mft_record_size);
return 0;
}
if (!vol->mft_ino)
{
printk(KERN_ERR "NTFS: mft_ino is NULL. Something is terribly "
"wrong here!\n");
return -ENODATA;
}
io.fn_put = ntfs_put;
io.fn_get = 0;
io.param = buf;
io.size = vol->mft_record_size;
ntfs_debug(DEBUG_OTHER, "read_mft_record: calling ntfs_read_attr with: "
"mftno = 0x%x, vol->mft_record_size_bits = 0x%x, "
"mftno << vol->mft_record_size_bits = 0x%Lx\n", mftno,
vol->mft_record_size_bits,
(__s64)mftno << vol->mft_record_size_bits);
error = ntfs_read_attr(vol->mft_ino, vol->at_data, NULL,
(__s64)mftno << vol->mft_record_size_bits, &io);
if (error || (io.size != vol->mft_record_size)) {
ntfs_debug(DEBUG_OTHER, "read_mft_record: read 0x%x failed "
"(%d,%d,%d)\n", mftno, error, io.size,
vol->mft_record_size);
return error ? error : -ENODATA;
}
ntfs_debug(DEBUG_OTHER, "read_mft_record: finished read 0x%x\n", mftno);
if (!ntfs_check_mft_record(vol, buf)) {
/* FIXME: This is incomplete behaviour. We might be able to
* recover at this stage. ntfs_check_mft_record() is too
* conservative at aborting it's operations. It is OK for
* now as we just can't handle some on disk structures
* this way. (AIA) */
printk(KERN_WARNING "NTFS: Invalid MFT record for 0x%x\n", mftno);
return -EIO;
}
ntfs_debug(DEBUG_OTHER, "read_mft_record: Done 0x%x\n", mftno);
return 0;
}
int ntfs_getput_clusters(ntfs_volume *vol, int cluster, ntfs_size_t start_offs,
ntfs_io *buf)
{
struct super_block *sb = NTFS_SB(vol);
struct buffer_head *bh;
int length = buf->size;
int error = 0;
ntfs_size_t to_copy;
ntfs_debug(DEBUG_OTHER, "%s_clusters %d %d %d\n",
buf->do_read ? "get" : "put", cluster, start_offs, length);
to_copy = vol->cluster_size - start_offs;
while (length) {
if (!(bh = bread(sb->s_dev, cluster, vol->cluster_size))) {
ntfs_debug(DEBUG_OTHER, "%s failed\n",
buf->do_read ? "Reading" : "Writing");
error = -EIO;
goto error_ret;
}
if (to_copy > length)
to_copy = length;
lock_buffer(bh);
if (buf->do_read) {
buf->fn_put(buf, bh->b_data + start_offs, to_copy);
unlock_buffer(bh);
} else {
buf->fn_get(bh->b_data + start_offs, buf, to_copy);
mark_buffer_dirty(bh);
unlock_buffer(bh);
/*
* Note: We treat synchronous IO on a per volume basis
* disregarding flags of individual inodes. This can
* lead to some strange write ordering effects upon a
* remount with a change in the sync flag but it should
* not break anything. [Except if the system crashes
* at that point in time but there would be more thigs
* to worry about than that in that case...]. (AIA)
*/
if (sb->s_flags & MS_SYNCHRONOUS) {
ll_rw_block(WRITE, 1, &bh);
wait_on_buffer(bh);
if (buffer_req(bh) && !buffer_uptodate(bh)) {
printk(KERN_ERR "IO error syncing NTFS "
"cluster [%s:%i]\n",
bdevname(sb->s_dev), cluster);
brelse(bh);
error = -EIO;
goto error_ret;
}
}
}
brelse(bh);
length -= to_copy;
start_offs = 0;
to_copy = vol->cluster_size;
cluster++;
}
error_ret:
return error;
}
ntfs_time64_t ntfs_now(void)
{
return ntfs_unixutc2ntutc(CURRENT_TIME);
}
int ntfs_dupuni2map(ntfs_volume *vol, ntfs_u16 *in, int in_len, char **out,
int *out_len)
{
int i, o, chl, chi;
char *result, *buf, charbuf[NLS_MAX_CHARSET_SIZE];
struct nls_table *nls = vol->nls_map;
result = ntfs_malloc(in_len + 1);
if (!result)
return -ENOMEM;
*out_len = in_len;
for (i = o = 0; i < in_len; i++) {
/* FIXME: Byte order? */
wchar_t uni = in[i];
if ((chl = nls->uni2char(uni, charbuf,
NLS_MAX_CHARSET_SIZE)) > 0) {
/* Adjust result buffer. */
if (chl > 1) {
buf = ntfs_malloc(*out_len + chl - 1);
if (!buf) {
i = -ENOMEM;
goto err_ret;
}
memcpy(buf, result, o);
ntfs_free(result);
result = buf;
*out_len += (chl - 1);
}
for (chi = 0; chi < chl; chi++)
result[o++] = charbuf[chi];
} else {
/* Invalid character. */
printk(KERN_ERR "NTFS: Unicode name contains a "
"character that cannot be converted "
"to chosen character set. Remount "
"with utf8 encoding and this should "
"work.\n");
i = -EILSEQ;
goto err_ret;
}
}
result[*out_len] = '\0';
*out = result;
return 0;
err_ret:
ntfs_free(result);
*out_len = 0;
*out = NULL;
return i;
}
int ntfs_dupmap2uni(ntfs_volume *vol, char* in, int in_len, ntfs_u16 **out,
int *out_len)
{
int i, o;
ntfs_u16 *result;
struct nls_table *nls = vol->nls_map;
*out = result = ntfs_malloc(2 * in_len);
if (!result) {
*out_len = 0;
return -ENOMEM;
}
*out_len = in_len;
for (i = o = 0; i < in_len; i++, o++) {
wchar_t uni;
int charlen;
charlen = nls->char2uni(&in[i], in_len - i, &uni);
if (charlen < 0) {
i = charlen;
goto err_ret;
}
*out_len -= charlen - 1;
i += charlen - 1;
/* FIXME: Byte order? */
result[o] = uni;
if (!result[o]) {
i = -EILSEQ;
goto err_ret;
}
}
return 0;
err_ret:
printk(KERN_ERR "NTFS: Name contains a character that cannot be "
"converted to Unicode.\n");
ntfs_free(result);
*out_len = 0;
*out = NULL;
return i;
}