--- zzzz-none-000/linux-2.4.17/fs/ntfs/fs.c 2001-12-21 17:42:03.000000000 +0000 +++ sangam-fb-322/linux-2.4.17/fs/ntfs/fs.c 2004-11-24 13:22:16.000000000 +0000 @@ -1,1166 +1,1166 @@ -/* - * fs.c - NTFS driver for Linux 2.4.x - * - * Legato Systems, Inc. (http://www.legato.com) have sponsored Anton - * Altaparmakov to develop NTFS on Linux since June 2001. - * - * Copyright (C) 1995-1997, 1999 Martin von Löwis - * Copyright (C) 1996 Richard Russon - * Copyright (C) 1996-1997 Régis Duchesne - * Copyright (C) 2000-2001, Anton Altaparmakov (AIA) - */ - -#include -#include -#include "ntfstypes.h" -#include "struct.h" -#include "util.h" -#include "inode.h" -#include "super.h" -#include "dir.h" -#include "support.h" -#include "macros.h" -#include "sysctl.h" -#include "attr.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Forward declarations. */ -static struct inode_operations ntfs_dir_inode_operations; -static struct file_operations ntfs_dir_operations; - -#define ITEM_SIZE 2040 - -/* Io functions to user space. */ -static void ntfs_putuser(ntfs_io* dest, void *src, ntfs_size_t len) -{ - copy_to_user(dest->param, src, len); - dest->param += len; -} - -#ifdef CONFIG_NTFS_RW -struct ntfs_getuser_update_vm_s { - const char *user; - struct inode *ino; - loff_t off; -}; - -static void ntfs_getuser_update_vm(void *dest, ntfs_io *src, ntfs_size_t len) -{ - struct ntfs_getuser_update_vm_s *p = src->param; - - copy_from_user(dest, p->user, len); - p->user += len; - p->off += len; -} -#endif - -/* loff_t is 64 bit signed, so is cool. */ -static ssize_t ntfs_read(struct file *filp, char *buf, size_t count,loff_t *off) -{ - int error; - ntfs_io io; - ntfs_attribute *attr; - ntfs_inode *ino = NTFS_LINO2NINO(filp->f_dentry->d_inode); - - /* Inode is not properly initialized. */ - if (!ino) - return -EINVAL; - ntfs_debug(DEBUG_OTHER, "ntfs_read %x, %Lx, %x ->", - (unsigned)ino->i_number, (unsigned long long)*off, - (unsigned)count); - attr = ntfs_find_attr(ino, ino->vol->at_data, NULL); - /* Inode has no unnamed data attribute. */ - if (!attr) { - ntfs_debug(DEBUG_OTHER, "ntfs_read: $DATA not found!\n"); - return -EINVAL; - } - if (attr->flags & ATTR_IS_ENCRYPTED) - return -EACCES; - /* Read the data. */ - io.fn_put = ntfs_putuser; - io.fn_get = 0; - io.param = buf; - io.size = count; - error = ntfs_read_attr(ino, ino->vol->at_data, NULL, *off, &io); - if (error && !io.size) { - ntfs_debug(DEBUG_OTHER, "ntfs_read: read_attr failed with " - "error %i, io size %u.\n", error, io.size); - return error; - } - *off += io.size; - ntfs_debug(DEBUG_OTHER, "ntfs_read: finished. read %u bytes.\n", - io.size); - return io.size; -} - -#ifdef CONFIG_NTFS_RW -static ssize_t ntfs_write(struct file *filp, const char *buf, size_t count, - loff_t *pos) -{ - int err; - struct inode *vfs_ino = filp->f_dentry->d_inode; - ntfs_inode *ntfs_ino = NTFS_LINO2NINO(vfs_ino); - ntfs_attribute *data; - ntfs_io io; - struct ntfs_getuser_update_vm_s param; - - if (!ntfs_ino) - return -EINVAL; - ntfs_debug(DEBUG_LINUX, __FUNCTION__ "(): Entering for inode 0x%lx, " - "*pos 0x%Lx, count 0x%x.\n", ntfs_ino->i_number, *pos, - count); - /* Allows to lock fs ro at any time. */ - if (vfs_ino->i_sb->s_flags & MS_RDONLY) - return -EROFS; - data = ntfs_find_attr(ntfs_ino, ntfs_ino->vol->at_data, NULL); - if (!data) - return -EINVAL; - /* Evaluating O_APPEND is the file system's job... */ - if (filp->f_flags & O_APPEND) - *pos = vfs_ino->i_size; - if (!data->resident && *pos + count > data->allocated) { - err = ntfs_extend_attr(ntfs_ino, data, *pos + count); - if (err < 0) - return err; - } - param.user = buf; - param.ino = vfs_ino; - param.off = *pos; - io.fn_put = 0; - io.fn_get = ntfs_getuser_update_vm; - io.param = ¶m; - io.size = count; - io.do_read = 0; - err = ntfs_readwrite_attr(ntfs_ino, data, *pos, &io); - ntfs_debug(DEBUG_LINUX, __FUNCTION__ "(): Returning %i\n", -err); - if (!err) { - *pos += io.size; - if (*pos > vfs_ino->i_size) - vfs_ino->i_size = *pos; - mark_inode_dirty(vfs_ino); - return io.size; - } - return err; -} -#endif - -struct ntfs_filldir { - struct inode *dir; - filldir_t filldir; - unsigned int type; - u32 ph, pl; - void *dirent; - char *name; - int namelen; - int ret_code; -}; - -static int ntfs_printcb(ntfs_u8 *entry, void *param) -{ - unsigned long inum = NTFS_GETU64(entry) & 0xffffffffffff; - struct ntfs_filldir *nf = param; - u32 flags = NTFS_GETU32(entry + 0x48); - char show_sys_files = 0; - u8 name_len = NTFS_GETU8(entry + 0x50); - u8 name_type = NTFS_GETU8(entry + 0x51); - int err; - unsigned file_type; - - switch (nf->type) { - case ngt_dos: - /* Don't display long names. */ - if (!(name_type & 2)) - return 0; - break; - case ngt_nt: - /* Don't display short-only names. */ - if ((name_type & 3) == 2) - return 0; - break; - case ngt_posix: - break; - case ngt_full: - show_sys_files = 1; - break; - default: - BUG(); - } - err = ntfs_encodeuni(NTFS_INO2VOL(nf->dir), (ntfs_u16*)(entry + 0x52), - name_len, &nf->name, &nf->namelen); - if (err) { - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Skipping " - "unrepresentable file.\n"); - err = 0; - goto err_ret; - } - if (!show_sys_files && inum < 0x10UL) { - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Skipping system " - "file (%s).\n", nf->name); - err = 0; - goto err_ret; - } - /* Do not return ".", as this is faked. */ - if (nf->namelen == 1 && nf->name[0] == '.') { - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Skipping \".\"\n"); - err = 0; - goto err_ret; - } - nf->name[nf->namelen] = 0; - if (flags & 0x10000000) /* FILE_ATTR_DUP_FILE_NAME_INDEX_PRESENT */ - file_type = DT_DIR; - else - file_type = DT_REG; - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Calling filldir for %s with " - "len %i, f_pos 0x%Lx, inode %lu, %s.\n", - nf->name, nf->namelen, (loff_t)(nf->ph << 16) | nf->pl, - inum, file_type == DT_DIR ? "DT_DIR" : "DT_REG"); - /* - * Userspace side of filldir expects an off_t rather than an loff_t. - * And it also doesn't like the most significant bit being set as it - * then considers the value to be negative. Thus this implementation - * limits the number of index records to 32766, which should be plenty. - */ - err = nf->filldir(nf->dirent, nf->name, nf->namelen, - (loff_t)(nf->ph << 16) | nf->pl, inum, file_type); - if (err) - nf->ret_code = err; -err_ret: - nf->namelen = 0; - ntfs_free(nf->name); - nf->name = NULL; - return err; -} - -/* - * readdir returns '.', then '..', then the directory entries in sequence. - * As the root directory contains an entry for itself, '.' is not emulated for - * the root directory. - */ -static int ntfs_readdir(struct file* filp, void *dirent, filldir_t filldir) -{ - struct inode *dir = filp->f_dentry->d_inode; - int err; - struct ntfs_filldir cb; - - cb.ret_code = 0; - cb.pl = filp->f_pos & 0xffff; - cb.ph = (filp->f_pos >> 16) & 0x7fff; - filp->f_pos = (loff_t)(cb.ph << 16) | cb.pl; - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Entering for inode %lu, " - "f_pos 0x%Lx, i_mode 0x%x, i_count %lu.\n", dir->i_ino, - filp->f_pos, (unsigned int)dir->i_mode, - atomic_read(&dir->i_count)); - if (!cb.ph) { - /* Start of directory. Emulate "." and "..". */ - if (!cb.pl) { - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Calling " - "filldir for . with len 1, f_pos 0x%Lx, " - "inode %lu, DT_DIR.\n", filp->f_pos, - dir->i_ino); - cb.ret_code = filldir(dirent, ".", 1, filp->f_pos, - dir->i_ino, DT_DIR); - if (cb.ret_code) - goto done; - cb.pl++; - filp->f_pos = (loff_t)(cb.ph << 16) | cb.pl; - } - if (cb.pl == (u32)1) { - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Calling " - "filldir for .. with len 2, f_pos 0x%Lx, " - "inode %lu, DT_DIR.\n", filp->f_pos, - filp->f_dentry->d_parent->d_inode->i_ino); - cb.ret_code = filldir(dirent, "..", 2, filp->f_pos, - filp->f_dentry->d_parent->d_inode->i_ino, - DT_DIR); - if (cb.ret_code) - goto done; - cb.pl++; - filp->f_pos = (loff_t)(cb.ph << 16) | cb.pl; - } - } else if (cb.ph >= 0x7fff) - /* End of directory. */ - goto done; - cb.dir = dir; - cb.filldir = filldir; - cb.dirent = dirent; - cb.type = NTFS_INO2VOL(dir)->ngt; - do { - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Looking for next " - "file using ntfs_getdir_unsorted(), f_pos " - "0x%Lx.\n", (loff_t)(cb.ph << 16) | cb.pl); - err = ntfs_getdir_unsorted(NTFS_LINO2NINO(dir), &cb.ph, &cb.pl, - ntfs_printcb, &cb); - } while (!err && !cb.ret_code && cb.ph < 0x7fff); - filp->f_pos = (loff_t)(cb.ph << 16) | cb.pl; - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After ntfs_getdir_unsorted()" - " calls, f_pos 0x%Lx.\n", filp->f_pos); - if (!err) { -done: -#ifdef DEBUG - if (!cb.ret_code) - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): EOD, f_pos " - "0x%Lx, returning 0.\n", filp->f_pos); - else - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): filldir " - "returned %i, returning 0, f_pos " - "0x%Lx.\n", cb.ret_code, filp->f_pos); -#endif - return 0; - } - ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Returning %i, f_pos 0x%Lx.\n", - err, filp->f_pos); - return err; -} - -/* Copied from vfat driver. */ -static int simple_getbool(char *s, int *setval) -{ - if (s) { - if (!strcmp(s, "1") || !strcmp(s, "yes") || !strcmp(s, "true")) - *setval = 1; - else if (!strcmp(s, "0") || !strcmp(s, "no") || - !strcmp(s, "false")) - *setval = 0; - else - return 0; - } else - *setval = 1; - return 1; -} - -/* - * This needs to be outside parse_options() otherwise a remount will reset - * these unintentionally. - */ -static void init_ntfs_super_block(ntfs_volume* vol) -{ - vol->uid = vol->gid = 0; - vol->umask = 0077; - vol->ngt = ngt_nt; - vol->nls_map = (void*)-1; - vol->mft_zone_multiplier = -1; -} - -/* Parse the (re)mount options. */ -static int parse_options(ntfs_volume *vol, char *opt) -{ - char *value; /* Defaults if not specified and !remount. */ - ntfs_uid_t uid = -1; /* 0, root user only */ - ntfs_gid_t gid = -1; /* 0, root user only */ - int umask = -1; /* 0077, owner access only */ - unsigned int ngt = -1; /* ngt_nt */ - void *nls_map = NULL; /* Try to load the default NLS. */ - int use_utf8 = -1; /* If no NLS specified and loading the default - NLS failed use utf8. */ - int mft_zone_mul = -1; /* 1 */ - - if (!opt) - goto done; - for (opt = strtok(opt, ","); opt; opt = strtok(NULL, ",")) { - if ((value = strchr(opt, '=')) != NULL) - *value ++= '\0'; - if (strcmp(opt, "uid") == 0) { - if (!value || !*value) - goto needs_arg; - uid = simple_strtoul(value, &value, 0); - if (*value) { - printk(KERN_ERR "NTFS: uid invalid argument\n"); - return 0; - } - } else if (strcmp(opt, "gid") == 0) { - if (!value || !*value) - goto needs_arg; - gid = simple_strtoul(value, &value, 0); - if (*value) { - printk(KERN_ERR "NTFS: gid invalid argument\n"); - return 0; - } - } else if (strcmp(opt, "umask") == 0) { - if (!value || !*value) - goto needs_arg; - umask = simple_strtoul(value, &value, 0); - if (*value) { - printk(KERN_ERR "NTFS: umask invalid " - "argument\n"); - return 0; - } - } else if (strcmp(opt, "mft_zone_multiplier") == 0) { - unsigned long ul; - - if (!value || !*value) - goto needs_arg; - ul = simple_strtoul(value, &value, 0); - if (*value) { - printk(KERN_ERR "NTFS: mft_zone_multiplier " - "invalid argument\n"); - return 0; - } - if (ul >= 1 && ul <= 4) - mft_zone_mul = ul; - else { - mft_zone_mul = 1; - printk(KERN_WARNING "NTFS: mft_zone_multiplier " - "out of range. Setting to 1.\n"); - } - } else if (strcmp(opt, "posix") == 0) { - int val; - if (!value || !*value) - goto needs_arg; - if (!simple_getbool(value, &val)) - goto needs_bool; - ngt = val ? ngt_posix : ngt_nt; - } else if (strcmp(opt, "show_sys_files") == 0) { - int val = 0; - if (!value || !*value) - val = 1; - else if (!simple_getbool(value, &val)) - goto needs_bool; - ngt = val ? ngt_full : ngt_nt; - } else if (strcmp(opt, "iocharset") == 0) { - if (!value || !*value) - goto needs_arg; - nls_map = load_nls(value); - if (!nls_map) { - printk(KERN_ERR "NTFS: charset not found"); - return 0; - } - } else if (strcmp(opt, "utf8") == 0) { - int val = 0; - if (!value || !*value) - val = 1; - else if (!simple_getbool(value, &val)) - goto needs_bool; - use_utf8 = val; - } else { - printk(KERN_ERR "NTFS: unkown option '%s'\n", opt); - return 0; - } - } -done: - if (use_utf8 == -1) { - /* utf8 was not specified at all. */ - if (!nls_map) { - /* - * No NLS was specified. If first mount, load the - * default NLS, otherwise don't change the NLS setting. - */ - if (vol->nls_map == (void*)-1) - vol->nls_map = load_nls_default(); - } else { - /* If an NLS was already loaded, unload it first. */ - if (vol->nls_map && vol->nls_map != (void*)-1) - unload_nls(vol->nls_map); - /* Use the specified NLS. */ - vol->nls_map = nls_map; - } - } else { - /* utf8 was specified. */ - if (use_utf8 && nls_map) { - unload_nls(nls_map); - printk(KERN_ERR "NTFS: utf8 cannot be combined with " - "iocharset.\n"); - return 0; - } - /* If an NLS was already loaded, unload it first. */ - if (vol->nls_map && vol->nls_map != (void*)-1) - unload_nls(vol->nls_map); - if (!use_utf8) { - /* utf8 was specified as false. */ - if (!nls_map) - /* No NLS was specified, load the default. */ - vol->nls_map = load_nls_default(); - else - /* Use the specified NLS. */ - vol->nls_map = nls_map; - } else - /* utf8 was specified as true. */ - vol->nls_map = NULL; - } - if (uid != -1) - vol->uid = uid; - if (gid != -1) - vol->gid = gid; - if (umask != -1) - vol->umask = (ntmode_t)umask; - if (ngt != -1) - vol->ngt = ngt; - if (mft_zone_mul != -1) { - /* mft_zone_multiplier was specified. */ - if (vol->mft_zone_multiplier != -1) { - /* This is a remount, ignore a change and warn user. */ - if (vol->mft_zone_multiplier != mft_zone_mul) - printk(KERN_WARNING "NTFS: Ignoring changes in " - "mft_zone_multiplier on " - "remount. If you want to " - "change this you need to " - "umount and mount again.\n"); - } else - /* Use the specified multiplier. */ - vol->mft_zone_multiplier = mft_zone_mul; - } else if (vol->mft_zone_multiplier == -1) - /* No multiplier specified and first mount, so set default. */ - vol->mft_zone_multiplier = 1; - return 1; -needs_arg: - printk(KERN_ERR "NTFS: %s needs an argument", opt); - return 0; -needs_bool: - printk(KERN_ERR "NTFS: %s needs boolean argument", opt); - return 0; -} - -static struct dentry *ntfs_lookup(struct inode *dir, struct dentry *d) -{ - struct inode *res = 0; - char *item = 0; - ntfs_iterate_s walk; - int err; - - ntfs_debug(DEBUG_NAME1, __FUNCTION__ "(): Looking up %s in directory " - "ino 0x%x.\n", d->d_name.name, (unsigned)dir->i_ino); - walk.name = NULL; - walk.namelen = 0; - /* Convert to wide string. */ - err = ntfs_decodeuni(NTFS_INO2VOL(dir), (char*)d->d_name.name, - d->d_name.len, &walk.name, &walk.namelen); - if (err) - goto err_ret; - item = ntfs_malloc(ITEM_SIZE); - if (!item) { - err = -ENOMEM; - goto err_ret; - } - /* ntfs_getdir will place the directory entry into item, and the first - * long long is the MFT record number. */ - walk.type = BY_NAME; - walk.dir = NTFS_LINO2NINO(dir); - walk.result = item; - if (ntfs_getdir_byname(&walk)) - res = iget(dir->i_sb, NTFS_GETU32(item)); - d_add(d, res); - ntfs_free(item); - ntfs_free(walk.name); - /* Always return success, the dcache will handle negative entries. */ - return NULL; -err_ret: - ntfs_free(walk.name); - return ERR_PTR(err); -} - -static struct file_operations ntfs_file_operations = { - llseek: generic_file_llseek, - read: ntfs_read, -#ifdef CONFIG_NTFS_RW - write: ntfs_write, -#endif - open: generic_file_open, -}; - -static struct inode_operations ntfs_inode_operations; - -#ifdef CONFIG_NTFS_RW -static int ntfs_create(struct inode* dir, struct dentry *d, int mode) -{ - struct inode *r = 0; - ntfs_inode *ino = 0; - ntfs_volume *vol; - int error = 0; - ntfs_attribute *si; - - r = new_inode(dir->i_sb); - if (!r) { - error = -ENOMEM; - goto fail; - } - ntfs_debug(DEBUG_OTHER, "ntfs_create %s\n", d->d_name.name); - vol = NTFS_INO2VOL(dir); - ino = NTFS_LINO2NINO(r); - error = ntfs_alloc_file(NTFS_LINO2NINO(dir), ino, (char*)d->d_name.name, - d->d_name.len); - if (error) { - ntfs_error("ntfs_alloc_file FAILED: error = %i", error); - goto fail; - } - /* Not doing this one was causing a huge amount of corruption! Now the - * bugger bytes the dust! (-8 (AIA) */ - r->i_ino = ino->i_number; - error = ntfs_update_inode(ino); - if (error) - goto fail; - error = ntfs_update_inode(NTFS_LINO2NINO(dir)); - if (error) - goto fail; - r->i_uid = vol->uid; - r->i_gid = vol->gid; - /* FIXME: dirty? dev? */ - /* Get the file modification times from the standard information. */ - si = ntfs_find_attr(ino, vol->at_standard_information, NULL); - if (si) { - char *attr = si->d.data; - r->i_atime = ntfs_ntutc2unixutc(NTFS_GETU64(attr + 0x18)); - r->i_ctime = ntfs_ntutc2unixutc(NTFS_GETU64(attr)); - r->i_mtime = ntfs_ntutc2unixutc(NTFS_GETU64(attr + 8)); - } - /* It's not a directory */ - r->i_op = &ntfs_inode_operations; - r->i_fop = &ntfs_file_operations; - r->i_mode = S_IFREG | S_IRUGO; -#ifdef CONFIG_NTFS_RW - r->i_mode |= S_IWUGO; -#endif - r->i_mode &= ~vol->umask; - insert_inode_hash(r); - d_instantiate(d, r); - return 0; - fail: - if (r) - iput(r); - return error; -} - -static int _linux_ntfs_mkdir(struct inode *dir, struct dentry* d, int mode) -{ - int error; - struct inode *r = 0; - ntfs_volume *vol; - ntfs_inode *ino; - ntfs_attribute *si; - - ntfs_debug (DEBUG_DIR1, "mkdir %s in %x\n", d->d_name.name, dir->i_ino); - error = -ENAMETOOLONG; - if (d->d_name.len > /* FIXME: */ 255) - goto out; - error = -EIO; - r = new_inode(dir->i_sb); - if (!r) - goto out; - vol = NTFS_INO2VOL(dir); - ino = NTFS_LINO2NINO(r); - error = ntfs_mkdir(NTFS_LINO2NINO(dir), d->d_name.name, d->d_name.len, - ino); - if (error) - goto out; - /* Not doing this one was causing a huge amount of corruption! Now the - * bugger bytes the dust! (-8 (AIA) */ - r->i_ino = ino->i_number; - r->i_uid = vol->uid; - r->i_gid = vol->gid; - si = ntfs_find_attr(ino, vol->at_standard_information, NULL); - if (si) { - char *attr = si->d.data; - r->i_atime = ntfs_ntutc2unixutc(NTFS_GETU64(attr + 0x18)); - r->i_ctime = ntfs_ntutc2unixutc(NTFS_GETU64(attr)); - r->i_mtime = ntfs_ntutc2unixutc(NTFS_GETU64(attr + 8)); - } - /* It's a directory. */ - r->i_op = &ntfs_dir_inode_operations; - r->i_fop = &ntfs_dir_operations; - r->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; -#ifdef CONFIG_NTFS_RW - r->i_mode |= S_IWUGO; -#endif - r->i_mode &= ~vol->umask; - - insert_inode_hash(r); - d_instantiate(d, r); - error = 0; - out: - ntfs_debug (DEBUG_DIR1, "mkdir returns %d\n", error); - return error; -} -#endif - -static struct file_operations ntfs_dir_operations = { - read: generic_read_dir, - readdir: ntfs_readdir, -}; - -static struct inode_operations ntfs_dir_inode_operations = { - lookup: ntfs_lookup, -#ifdef CONFIG_NTFS_RW - create: ntfs_create, - mkdir: _linux_ntfs_mkdir, -#endif -}; - -/* ntfs_read_inode() is called by the Virtual File System (the kernel layer - * that deals with filesystems) when iget is called requesting an inode not - * already present in the inode table. Typically filesystems have separate - * inode_operations for directories, files and symlinks. */ -static void ntfs_read_inode(struct inode* inode) -{ - ntfs_volume *vol; - ntfs_inode *ino; - ntfs_attribute *data; - ntfs_attribute *si; - - vol = NTFS_INO2VOL(inode); - inode->i_mode = 0; - ntfs_debug(DEBUG_OTHER, "ntfs_read_inode 0x%lx\n", inode->i_ino); - switch (inode->i_ino) { - /* Those are loaded special files. */ - case FILE_Mft: - if (!vol->mft_ino || ((vol->ino_flags & 1) == 0)) - goto sys_file_error; - ntfs_memcpy(&inode->u.ntfs_i, vol->mft_ino, sizeof(ntfs_inode)); - ino = vol->mft_ino; - vol->mft_ino = &inode->u.ntfs_i; - vol->ino_flags &= ~1; - ntfs_free(ino); - ino = vol->mft_ino; - ntfs_debug(DEBUG_OTHER, "Opening $MFT!\n"); - break; - case FILE_MftMirr: - if (!vol->mftmirr || ((vol->ino_flags & 2) == 0)) - goto sys_file_error; - ntfs_memcpy(&inode->u.ntfs_i, vol->mftmirr, sizeof(ntfs_inode)); - ino = vol->mftmirr; - vol->mftmirr = &inode->u.ntfs_i; - vol->ino_flags &= ~2; - ntfs_free(ino); - ino = vol->mftmirr; - ntfs_debug(DEBUG_OTHER, "Opening $MFTMirr!\n"); - break; - case FILE_BitMap: - if (!vol->bitmap || ((vol->ino_flags & 4) == 0)) - goto sys_file_error; - ntfs_memcpy(&inode->u.ntfs_i, vol->bitmap, sizeof(ntfs_inode)); - ino = vol->bitmap; - vol->bitmap = &inode->u.ntfs_i; - vol->ino_flags &= ~4; - ntfs_free(ino); - ino = vol->bitmap; - ntfs_debug(DEBUG_OTHER, "Opening $Bitmap!\n"); - break; - case FILE_LogFile ... FILE_AttrDef: - /* No need to log root directory accesses. */ - case FILE_Boot ... FILE_UpCase: - ntfs_debug(DEBUG_OTHER, "Opening system file %i!\n", - inode->i_ino); - default: - ino = &inode->u.ntfs_i; - if (!ino || ntfs_init_inode(ino, NTFS_INO2VOL(inode), - inode->i_ino)) - { - ntfs_debug(DEBUG_OTHER, "NTFS: Error loading inode " - "0x%x\n", (unsigned int)inode->i_ino); - return; - } - } - /* Set uid/gid from mount options */ - inode->i_uid = vol->uid; - inode->i_gid = vol->gid; - inode->i_nlink = 1; - /* Use the size of the data attribute as file size */ - data = ntfs_find_attr(ino, vol->at_data, NULL); - if (!data) - inode->i_size = 0; - else - inode->i_size = data->size; - /* Get the file modification times from the standard information. */ - si = ntfs_find_attr(ino, vol->at_standard_information, NULL); - if (si) { - char *attr = si->d.data; - inode->i_atime = ntfs_ntutc2unixutc(NTFS_GETU64(attr + 0x18)); - inode->i_ctime = ntfs_ntutc2unixutc(NTFS_GETU64(attr)); - inode->i_mtime = ntfs_ntutc2unixutc(NTFS_GETU64(attr + 8)); - } - /* If it has an index root, it's a directory. */ - if (ntfs_find_attr(ino, vol->at_index_root, "$I30")) { - ntfs_attribute *at; - at = ntfs_find_attr(ino, vol->at_index_allocation, "$I30"); - inode->i_size = at ? at->size : 0; - inode->i_op = &ntfs_dir_inode_operations; - inode->i_fop = &ntfs_dir_operations; - inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; - } else { - inode->i_op = &ntfs_inode_operations; - inode->i_fop = &ntfs_file_operations; - inode->i_mode = S_IFREG | S_IRUGO; - } -#ifdef CONFIG_NTFS_RW - if (!data || !(data->flags & (ATTR_IS_COMPRESSED | ATTR_IS_ENCRYPTED))) - inode->i_mode |= S_IWUGO; -#endif - inode->i_mode &= ~vol->umask; - return; -sys_file_error: - ntfs_error("Critical error. Tried to call ntfs_read_inode() before we " - "have completed read_super() or VFS error.\n"); - // FIXME: Should we panic() at this stage? -} - -#ifdef CONFIG_NTFS_RW -static void ntfs_write_inode(struct inode *ino, int unused) -{ - lock_kernel(); - ntfs_debug(DEBUG_LINUX, "ntfs_write_inode 0x%x\n", ino->i_ino); - ntfs_update_inode(NTFS_LINO2NINO(ino)); - unlock_kernel(); -} -#endif - -static void _ntfs_clear_inode(struct inode *inode) -{ - ntfs_inode *ino; - ntfs_volume *vol; - - lock_kernel(); - ntfs_debug(DEBUG_OTHER, "_ntfs_clear_inode 0x%x\n", inode->i_ino); - vol = NTFS_INO2VOL(inode); - if (!vol) - ntfs_error("_ntfs_clear_inode: vol = NTFS_INO2VOL(inode) is " - "NULL.\n"); - switch (inode->i_ino) { - case FILE_Mft: - if (vol->mft_ino && ((vol->ino_flags & 1) == 0)) { - ino = (ntfs_inode*)ntfs_malloc(sizeof(ntfs_inode)); - ntfs_memcpy(ino, &inode->u.ntfs_i, sizeof(ntfs_inode)); - vol->mft_ino = ino; - vol->ino_flags |= 1; - goto unl_out; - } - break; - case FILE_MftMirr: - if (vol->mftmirr && ((vol->ino_flags & 2) == 0)) { - ino = (ntfs_inode*)ntfs_malloc(sizeof(ntfs_inode)); - ntfs_memcpy(ino, &inode->u.ntfs_i, sizeof(ntfs_inode)); - vol->mftmirr = ino; - vol->ino_flags |= 2; - goto unl_out; - } - break; - case FILE_BitMap: - if (vol->bitmap && ((vol->ino_flags & 4) == 0)) { - ino = (ntfs_inode*)ntfs_malloc(sizeof(ntfs_inode)); - ntfs_memcpy(ino, &inode->u.ntfs_i, sizeof(ntfs_inode)); - vol->bitmap = ino; - vol->ino_flags |= 4; - goto unl_out; - } - break; - default: - /* Nothing. Just clear the inode and exit. */ - } - ntfs_clear_inode(&inode->u.ntfs_i); -unl_out: - unlock_kernel(); - return; -} - -/* Called when umounting a filesystem by do_umount() in fs/super.c. */ -static void ntfs_put_super(struct super_block *sb) -{ - ntfs_volume *vol; - - ntfs_debug(DEBUG_OTHER, "ntfs_put_super\n"); - vol = NTFS_SB2VOL(sb); - ntfs_release_volume(vol); - if (vol->nls_map) - unload_nls(vol->nls_map); - ntfs_debug(DEBUG_OTHER, "ntfs_put_super: done\n"); -} - -/* Called by the kernel when asking for stats. */ -static int ntfs_statfs(struct super_block *sb, struct statfs *sf) -{ - struct inode *mft; - ntfs_volume *vol; - __s64 size; - int error; - - ntfs_debug(DEBUG_OTHER, "ntfs_statfs\n"); - vol = NTFS_SB2VOL(sb); - sf->f_type = NTFS_SUPER_MAGIC; - sf->f_bsize = vol->cluster_size; - error = ntfs_get_volumesize(NTFS_SB2VOL(sb), &size); - if (error) - return error; - sf->f_blocks = size; /* Volumesize is in clusters. */ - size = (__s64)ntfs_get_free_cluster_count(vol->bitmap); - /* Just say zero if the call failed. */ - if (size < 0LL) - size = 0; - sf->f_bfree = sf->f_bavail = size; - ntfs_debug(DEBUG_OTHER, "ntfs_statfs: calling mft = iget(sb, " - "FILE_Mft)\n"); - mft = iget(sb, FILE_Mft); - ntfs_debug(DEBUG_OTHER, "ntfs_statfs: iget(sb, FILE_Mft) returned " - "0x%x\n", mft); - if (!mft) - return -EIO; - sf->f_files = mft->i_size >> vol->mft_record_size_bits; - ntfs_debug(DEBUG_OTHER, "ntfs_statfs: calling iput(mft)\n"); - iput(mft); - /* Should be read from volume. */ - sf->f_namelen = 255; - return 0; -} - -/* Called when remounting a filesystem by do_remount_sb() in fs/super.c. */ -static int ntfs_remount_fs(struct super_block *sb, int *flags, char *options) -{ - if (!parse_options(NTFS_SB2VOL(sb), options)) - return -EINVAL; - return 0; -} - -/* Define the super block operation that are implemented */ -static struct super_operations ntfs_super_operations = { - read_inode: ntfs_read_inode, -#ifdef CONFIG_NTFS_RW - write_inode: ntfs_write_inode, -#endif - put_super: ntfs_put_super, - statfs: ntfs_statfs, - remount_fs: ntfs_remount_fs, - clear_inode: _ntfs_clear_inode, -}; - -/** - * is_boot_sector_ntfs - check an NTFS boot sector for validity - * @b: buffer containing bootsector to check - * - * Check whether @b contains a valid NTFS boot sector. - * Return 1 if @b is a valid NTFS bootsector or 0 if not. - */ -static int is_boot_sector_ntfs(ntfs_u8 *b) -{ - ntfs_u32 i; - - /* FIXME: We don't use checksumming yet as NT4(SP6a) doesn't either... - * But we might as well have the code ready to do it. (AIA) */ -#if 0 - /* Calculate the checksum. */ - if (b < b + 0x50) { - ntfs_u32 *u; - ntfs_u32 *bi = (ntfs_u32 *)(b + 0x50); - - for (u = bi, i = 0; u < bi; ++u) - i += NTFS_GETU32(*u); - } -#endif - /* Check magic is "NTFS " */ - if (b[3] != 0x4e) goto not_ntfs; - if (b[4] != 0x54) goto not_ntfs; - if (b[5] != 0x46) goto not_ntfs; - if (b[6] != 0x53) goto not_ntfs; - for (i = 7; i < 0xb; ++i) - if (b[i] != 0x20) goto not_ntfs; - /* Check bytes per sector value is between 512 and 4096. */ - if (b[0xb] != 0) goto not_ntfs; - if (b[0xc] > 0x10) goto not_ntfs; - /* Check sectors per cluster value is valid. */ - switch (b[0xd]) { - case 1: case 2: case 4: case 8: case 16: - case 32: case 64: case 128: - break; - default: - goto not_ntfs; - } - /* Check reserved sectors value and four other fields are zero. */ - for (i = 0xe; i < 0x15; ++i) - if (b[i] != 0) goto not_ntfs; - if (b[0x16] != 0) goto not_ntfs; - if (b[0x17] != 0) goto not_ntfs; - for (i = 0x20; i < 0x24; ++i) - if (b[i] != 0) goto not_ntfs; - /* Check clusters per file record segment value is valid. */ - if (b[0x40] < 0xe1 || b[0x40] > 0xf7) { - switch (b[0x40]) { - case 1: case 2: case 4: case 8: case 16: case 32: case 64: - break; - default: - goto not_ntfs; - } - } - /* Check clusters per index block value is valid. */ - if (b[0x44] < 0xe1 || b[0x44] > 0xf7) { - switch (b[0x44]) { - case 1: case 2: case 4: case 8: case 16: case 32: case 64: - break; - default: - goto not_ntfs; - } - } - return 1; -not_ntfs: - return 0; -} - -/* Called to mount a filesystem by read_super() in fs/super.c. - * Return a super block, the main structure of a filesystem. - * - * NOTE : Don't store a pointer to an option, as the page containing the - * options is freed after ntfs_read_super() returns. - * - * NOTE : A context switch can happen in kernel code only if the code blocks - * (= calls schedule() in kernel/sched.c). */ -struct super_block *ntfs_read_super(struct super_block *sb, void *options, - int silent) -{ - ntfs_volume *vol; - struct buffer_head *bh; - int i, to_read, blocksize; - - ntfs_debug(DEBUG_OTHER, "ntfs_read_super\n"); - vol = NTFS_SB2VOL(sb); - init_ntfs_super_block(vol); - if (!parse_options(vol, (char*)options)) - goto ntfs_read_super_vol; - blocksize = get_hardsect_size(sb->s_dev); - if (blocksize < 512) - blocksize = 512; - if (set_blocksize(sb->s_dev, blocksize) < 0) { - ntfs_error("Unable to set blocksize %d.\n", blocksize); - goto ntfs_read_super_vol; - } - /* Read the super block (boot block). */ - if (!(bh = bread(sb->s_dev, 0, blocksize))) { - ntfs_error("Reading super block failed\n"); - goto ntfs_read_super_unl; - } - ntfs_debug(DEBUG_OTHER, "Done reading boot block\n"); - /* Check for valid 'NTFS' boot sector. */ - if (!is_boot_sector_ntfs(bh->b_data)) { - ntfs_debug(DEBUG_OTHER, "Not a NTFS volume\n"); - bforget(bh); - goto ntfs_read_super_unl; - } - ntfs_debug(DEBUG_OTHER, "Going to init volume\n"); - if (ntfs_init_volume(vol, bh->b_data) < 0) { - ntfs_debug(DEBUG_OTHER, "Init volume failed.\n"); - bforget(bh); - goto ntfs_read_super_unl; - } - ntfs_debug(DEBUG_OTHER, "$Mft at cluster 0x%lx\n", vol->mft_lcn); - brelse(bh); - NTFS_SB(vol) = sb; - if (vol->cluster_size > PAGE_SIZE) { - ntfs_error("Partition cluster size is not supported yet (it " - "is > max kernel blocksize).\n"); - goto ntfs_read_super_unl; - } - ntfs_debug(DEBUG_OTHER, "Done to init volume\n"); - /* Inform the kernel that a device block is a NTFS cluster. */ - sb->s_blocksize = vol->cluster_size; - sb->s_blocksize_bits = vol->cluster_size_bits; - if (blocksize != vol->cluster_size && - set_blocksize(sb->s_dev, sb->s_blocksize) < 0) { - ntfs_error("Cluster size too small for device.\n"); - goto ntfs_read_super_unl; - } - ntfs_debug(DEBUG_OTHER, "set_blocksize\n"); - /* Allocate an MFT record (MFT record can be smaller than a cluster). */ - i = vol->cluster_size; - if (i < vol->mft_record_size) - i = vol->mft_record_size; - if (!(vol->mft = ntfs_malloc(i))) - goto ntfs_read_super_unl; - - /* Read at least the MFT record for $Mft. */ - to_read = vol->mft_clusters_per_record; - if (to_read < 1) - to_read = 1; - for (i = 0; i < to_read; i++) { - if (!(bh = bread(sb->s_dev, vol->mft_lcn + i, - vol->cluster_size))) { - ntfs_error("Could not read $Mft record 0\n"); - goto ntfs_read_super_mft; - } - ntfs_memcpy(vol->mft + ((__s64)i << vol->cluster_size_bits), - bh->b_data, vol->cluster_size); - brelse(bh); - ntfs_debug(DEBUG_OTHER, "Read cluster 0x%x\n", - vol->mft_lcn + i); - } - /* Check and fixup this MFT record */ - if (!ntfs_check_mft_record(vol, vol->mft)){ - ntfs_error("Invalid $Mft record 0\n"); - goto ntfs_read_super_mft; - } - /* Inform the kernel about which super operations are available. */ - sb->s_op = &ntfs_super_operations; - sb->s_magic = NTFS_SUPER_MAGIC; - sb->s_maxbytes = ~0ULL >> 1; - ntfs_debug(DEBUG_OTHER, "Reading special files\n"); - if (ntfs_load_special_files(vol)) { - ntfs_error("Error loading special files\n"); - goto ntfs_read_super_mft; - } - ntfs_debug(DEBUG_OTHER, "Getting RootDir\n"); - /* Get the root directory. */ - if (!(sb->s_root = d_alloc_root(iget(sb, FILE_root)))) { - ntfs_error("Could not get root dir inode\n"); - goto ntfs_read_super_mft; - } -ntfs_read_super_ret: - ntfs_debug(DEBUG_OTHER, "read_super: done\n"); - return sb; -ntfs_read_super_mft: - ntfs_free(vol->mft); -ntfs_read_super_unl: -ntfs_read_super_vol: - sb = NULL; - goto ntfs_read_super_ret; -} - -/* Define the filesystem */ -static DECLARE_FSTYPE_DEV(ntfs_fs_type, "ntfs", ntfs_read_super); - -static int __init init_ntfs_fs(void) -{ - /* Comment this if you trust klogd. There are reasons not to trust it */ -#if defined(DEBUG) && !defined(MODULE) - console_verbose(); -#endif - printk(KERN_NOTICE "NTFS driver v" NTFS_VERSION " [Flags: R/" -#ifdef CONFIG_NTFS_RW - "W" -#else - "O" -#endif -#ifdef DEBUG - " DEBUG" -#endif -#ifdef MODULE - " MODULE" -#endif - "]\n"); - SYSCTL(1); - ntfs_debug(DEBUG_OTHER, "registering %s\n", ntfs_fs_type.name); - /* Add this filesystem to the kernel table of filesystems. */ - return register_filesystem(&ntfs_fs_type); -} - -static void __exit exit_ntfs_fs(void) -{ - SYSCTL(0); - ntfs_debug(DEBUG_OTHER, "unregistering %s\n", ntfs_fs_type.name); - unregister_filesystem(&ntfs_fs_type); -} - -EXPORT_NO_SYMBOLS; -/* - * Not strictly true. The driver was written originally by Martin von Löwis. - * I am just maintaining and rewriting it. - */ -MODULE_AUTHOR("Anton Altaparmakov "); -MODULE_DESCRIPTION("Linux NTFS driver"); -MODULE_LICENSE("GPL"); -#ifdef DEBUG -MODULE_PARM(ntdebug, "i"); -MODULE_PARM_DESC(ntdebug, "Debug level"); -#endif - -module_init(init_ntfs_fs) -module_exit(exit_ntfs_fs) - +/* + * fs.c - NTFS driver for Linux 2.4.x + * + * Legato Systems, Inc. (http://www.legato.com) have sponsored Anton + * Altaparmakov to develop NTFS on Linux since June 2001. + * + * Copyright (C) 1995-1997, 1999 Martin von Löwis + * Copyright (C) 1996 Richard Russon + * Copyright (C) 1996-1997 Régis Duchesne + * Copyright (C) 2000-2001, Anton Altaparmakov (AIA) + */ + +#include +#include +#include "ntfstypes.h" +#include "struct.h" +#include "util.h" +#include "inode.h" +#include "super.h" +#include "dir.h" +#include "support.h" +#include "macros.h" +#include "sysctl.h" +#include "attr.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Forward declarations. */ +static struct inode_operations ntfs_dir_inode_operations; +static struct file_operations ntfs_dir_operations; + +#define ITEM_SIZE 2040 + +/* Io functions to user space. */ +static void ntfs_putuser(ntfs_io* dest, void *src, ntfs_size_t len) +{ + copy_to_user(dest->param, src, len); + dest->param += len; +} + +#ifdef CONFIG_NTFS_RW +struct ntfs_getuser_update_vm_s { + const char *user; + struct inode *ino; + loff_t off; +}; + +static void ntfs_getuser_update_vm(void *dest, ntfs_io *src, ntfs_size_t len) +{ + struct ntfs_getuser_update_vm_s *p = src->param; + + copy_from_user(dest, p->user, len); + p->user += len; + p->off += len; +} +#endif + +/* loff_t is 64 bit signed, so is cool. */ +static ssize_t ntfs_read(struct file *filp, char *buf, size_t count,loff_t *off) +{ + int error; + ntfs_io io; + ntfs_attribute *attr; + ntfs_inode *ino = NTFS_LINO2NINO(filp->f_dentry->d_inode); + + /* Inode is not properly initialized. */ + if (!ino) + return -EINVAL; + ntfs_debug(DEBUG_OTHER, "ntfs_read %x, %Lx, %x ->", + (unsigned)ino->i_number, (unsigned long long)*off, + (unsigned)count); + attr = ntfs_find_attr(ino, ino->vol->at_data, NULL); + /* Inode has no unnamed data attribute. */ + if (!attr) { + ntfs_debug(DEBUG_OTHER, "ntfs_read: $DATA not found!\n"); + return -EINVAL; + } + if (attr->flags & ATTR_IS_ENCRYPTED) + return -EACCES; + /* Read the data. */ + io.fn_put = ntfs_putuser; + io.fn_get = 0; + io.param = buf; + io.size = count; + error = ntfs_read_attr(ino, ino->vol->at_data, NULL, *off, &io); + if (error && !io.size) { + ntfs_debug(DEBUG_OTHER, "ntfs_read: read_attr failed with " + "error %i, io size %u.\n", error, io.size); + return error; + } + *off += io.size; + ntfs_debug(DEBUG_OTHER, "ntfs_read: finished. read %u bytes.\n", + io.size); + return io.size; +} + +#ifdef CONFIG_NTFS_RW +static ssize_t ntfs_write(struct file *filp, const char *buf, size_t count, + loff_t *pos) +{ + int err; + struct inode *vfs_ino = filp->f_dentry->d_inode; + ntfs_inode *ntfs_ino = NTFS_LINO2NINO(vfs_ino); + ntfs_attribute *data; + ntfs_io io; + struct ntfs_getuser_update_vm_s param; + + if (!ntfs_ino) + return -EINVAL; + ntfs_debug(DEBUG_LINUX, __FUNCTION__ "(): Entering for inode 0x%lx, " + "*pos 0x%Lx, count 0x%x.\n", ntfs_ino->i_number, *pos, + count); + /* Allows to lock fs ro at any time. */ + if (vfs_ino->i_sb->s_flags & MS_RDONLY) + return -EROFS; + data = ntfs_find_attr(ntfs_ino, ntfs_ino->vol->at_data, NULL); + if (!data) + return -EINVAL; + /* Evaluating O_APPEND is the file system's job... */ + if (filp->f_flags & O_APPEND) + *pos = vfs_ino->i_size; + if (!data->resident && *pos + count > data->allocated) { + err = ntfs_extend_attr(ntfs_ino, data, *pos + count); + if (err < 0) + return err; + } + param.user = buf; + param.ino = vfs_ino; + param.off = *pos; + io.fn_put = 0; + io.fn_get = ntfs_getuser_update_vm; + io.param = ¶m; + io.size = count; + io.do_read = 0; + err = ntfs_readwrite_attr(ntfs_ino, data, *pos, &io); + ntfs_debug(DEBUG_LINUX, __FUNCTION__ "(): Returning %i\n", -err); + if (!err) { + *pos += io.size; + if (*pos > vfs_ino->i_size) + vfs_ino->i_size = *pos; + mark_inode_dirty(vfs_ino); + return io.size; + } + return err; +} +#endif + +struct ntfs_filldir { + struct inode *dir; + filldir_t filldir; + unsigned int type; + u32 ph, pl; + void *dirent; + char *name; + int namelen; + int ret_code; +}; + +static int ntfs_printcb(ntfs_u8 *entry, void *param) +{ + unsigned long inum = NTFS_GETU64(entry) & 0xffffffffffff; + struct ntfs_filldir *nf = param; + u32 flags = NTFS_GETU32(entry + 0x48); + char show_sys_files = 0; + u8 name_len = NTFS_GETU8(entry + 0x50); + u8 name_type = NTFS_GETU8(entry + 0x51); + int err; + unsigned file_type; + + switch (nf->type) { + case ngt_dos: + /* Don't display long names. */ + if (!(name_type & 2)) + return 0; + break; + case ngt_nt: + /* Don't display short-only names. */ + if ((name_type & 3) == 2) + return 0; + break; + case ngt_posix: + break; + case ngt_full: + show_sys_files = 1; + break; + default: + BUG(); + } + err = ntfs_encodeuni(NTFS_INO2VOL(nf->dir), (ntfs_u16*)(entry + 0x52), + name_len, &nf->name, &nf->namelen); + if (err) { + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Skipping " + "unrepresentable file.\n"); + err = 0; + goto err_ret; + } + if (!show_sys_files && inum < 0x10UL) { + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Skipping system " + "file (%s).\n", nf->name); + err = 0; + goto err_ret; + } + /* Do not return ".", as this is faked. */ + if (nf->namelen == 1 && nf->name[0] == '.') { + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Skipping \".\"\n"); + err = 0; + goto err_ret; + } + nf->name[nf->namelen] = 0; + if (flags & 0x10000000) /* FILE_ATTR_DUP_FILE_NAME_INDEX_PRESENT */ + file_type = DT_DIR; + else + file_type = DT_REG; + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Calling filldir for %s with " + "len %i, f_pos 0x%Lx, inode %lu, %s.\n", + nf->name, nf->namelen, (loff_t)(nf->ph << 16) | nf->pl, + inum, file_type == DT_DIR ? "DT_DIR" : "DT_REG"); + /* + * Userspace side of filldir expects an off_t rather than an loff_t. + * And it also doesn't like the most significant bit being set as it + * then considers the value to be negative. Thus this implementation + * limits the number of index records to 32766, which should be plenty. + */ + err = nf->filldir(nf->dirent, nf->name, nf->namelen, + (loff_t)(nf->ph << 16) | nf->pl, inum, file_type); + if (err) + nf->ret_code = err; +err_ret: + nf->namelen = 0; + ntfs_free(nf->name); + nf->name = NULL; + return err; +} + +/* + * readdir returns '.', then '..', then the directory entries in sequence. + * As the root directory contains an entry for itself, '.' is not emulated for + * the root directory. + */ +static int ntfs_readdir(struct file* filp, void *dirent, filldir_t filldir) +{ + struct inode *dir = filp->f_dentry->d_inode; + int err; + struct ntfs_filldir cb; + + cb.ret_code = 0; + cb.pl = filp->f_pos & 0xffff; + cb.ph = (filp->f_pos >> 16) & 0x7fff; + filp->f_pos = (loff_t)(cb.ph << 16) | cb.pl; + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Entering for inode %lu, " + "f_pos 0x%Lx, i_mode 0x%x, i_count %lu.\n", dir->i_ino, + filp->f_pos, (unsigned int)dir->i_mode, + atomic_read(&dir->i_count)); + if (!cb.ph) { + /* Start of directory. Emulate "." and "..". */ + if (!cb.pl) { + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Calling " + "filldir for . with len 1, f_pos 0x%Lx, " + "inode %lu, DT_DIR.\n", filp->f_pos, + dir->i_ino); + cb.ret_code = filldir(dirent, ".", 1, filp->f_pos, + dir->i_ino, DT_DIR); + if (cb.ret_code) + goto done; + cb.pl++; + filp->f_pos = (loff_t)(cb.ph << 16) | cb.pl; + } + if (cb.pl == (u32)1) { + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Calling " + "filldir for .. with len 2, f_pos 0x%Lx, " + "inode %lu, DT_DIR.\n", filp->f_pos, + filp->f_dentry->d_parent->d_inode->i_ino); + cb.ret_code = filldir(dirent, "..", 2, filp->f_pos, + filp->f_dentry->d_parent->d_inode->i_ino, + DT_DIR); + if (cb.ret_code) + goto done; + cb.pl++; + filp->f_pos = (loff_t)(cb.ph << 16) | cb.pl; + } + } else if (cb.ph >= 0x7fff) + /* End of directory. */ + goto done; + cb.dir = dir; + cb.filldir = filldir; + cb.dirent = dirent; + cb.type = NTFS_INO2VOL(dir)->ngt; + do { + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Looking for next " + "file using ntfs_getdir_unsorted(), f_pos " + "0x%Lx.\n", (loff_t)(cb.ph << 16) | cb.pl); + err = ntfs_getdir_unsorted(NTFS_LINO2NINO(dir), &cb.ph, &cb.pl, + ntfs_printcb, &cb); + } while (!err && !cb.ret_code && cb.ph < 0x7fff); + filp->f_pos = (loff_t)(cb.ph << 16) | cb.pl; + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After ntfs_getdir_unsorted()" + " calls, f_pos 0x%Lx.\n", filp->f_pos); + if (!err) { +done: +#ifdef DEBUG + if (!cb.ret_code) + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): EOD, f_pos " + "0x%Lx, returning 0.\n", filp->f_pos); + else + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): filldir " + "returned %i, returning 0, f_pos " + "0x%Lx.\n", cb.ret_code, filp->f_pos); +#endif + return 0; + } + ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Returning %i, f_pos 0x%Lx.\n", + err, filp->f_pos); + return err; +} + +/* Copied from vfat driver. */ +static int simple_getbool(char *s, int *setval) +{ + if (s) { + if (!strcmp(s, "1") || !strcmp(s, "yes") || !strcmp(s, "true")) + *setval = 1; + else if (!strcmp(s, "0") || !strcmp(s, "no") || + !strcmp(s, "false")) + *setval = 0; + else + return 0; + } else + *setval = 1; + return 1; +} + +/* + * This needs to be outside parse_options() otherwise a remount will reset + * these unintentionally. + */ +static void init_ntfs_super_block(ntfs_volume* vol) +{ + vol->uid = vol->gid = 0; + vol->umask = 0077; + vol->ngt = ngt_nt; + vol->nls_map = (void*)-1; + vol->mft_zone_multiplier = -1; +} + +/* Parse the (re)mount options. */ +static int parse_options(ntfs_volume *vol, char *opt) +{ + char *value; /* Defaults if not specified and !remount. */ + ntfs_uid_t uid = -1; /* 0, root user only */ + ntfs_gid_t gid = -1; /* 0, root user only */ + int umask = -1; /* 0077, owner access only */ + unsigned int ngt = -1; /* ngt_nt */ + void *nls_map = NULL; /* Try to load the default NLS. */ + int use_utf8 = -1; /* If no NLS specified and loading the default + NLS failed use utf8. */ + int mft_zone_mul = -1; /* 1 */ + + if (!opt) + goto done; + for (opt = strtok(opt, ","); opt; opt = strtok(NULL, ",")) { + if ((value = strchr(opt, '=')) != NULL) + *value ++= '\0'; + if (strcmp(opt, "uid") == 0) { + if (!value || !*value) + goto needs_arg; + uid = simple_strtoul(value, &value, 0); + if (*value) { + printk(KERN_ERR "NTFS: uid invalid argument\n"); + return 0; + } + } else if (strcmp(opt, "gid") == 0) { + if (!value || !*value) + goto needs_arg; + gid = simple_strtoul(value, &value, 0); + if (*value) { + printk(KERN_ERR "NTFS: gid invalid argument\n"); + return 0; + } + } else if (strcmp(opt, "umask") == 0) { + if (!value || !*value) + goto needs_arg; + umask = simple_strtoul(value, &value, 0); + if (*value) { + printk(KERN_ERR "NTFS: umask invalid " + "argument\n"); + return 0; + } + } else if (strcmp(opt, "mft_zone_multiplier") == 0) { + unsigned long ul; + + if (!value || !*value) + goto needs_arg; + ul = simple_strtoul(value, &value, 0); + if (*value) { + printk(KERN_ERR "NTFS: mft_zone_multiplier " + "invalid argument\n"); + return 0; + } + if (ul >= 1 && ul <= 4) + mft_zone_mul = ul; + else { + mft_zone_mul = 1; + printk(KERN_WARNING "NTFS: mft_zone_multiplier " + "out of range. Setting to 1.\n"); + } + } else if (strcmp(opt, "posix") == 0) { + int val; + if (!value || !*value) + goto needs_arg; + if (!simple_getbool(value, &val)) + goto needs_bool; + ngt = val ? ngt_posix : ngt_nt; + } else if (strcmp(opt, "show_sys_files") == 0) { + int val = 0; + if (!value || !*value) + val = 1; + else if (!simple_getbool(value, &val)) + goto needs_bool; + ngt = val ? ngt_full : ngt_nt; + } else if (strcmp(opt, "iocharset") == 0) { + if (!value || !*value) + goto needs_arg; + nls_map = load_nls(value); + if (!nls_map) { + printk(KERN_ERR "NTFS: charset not found"); + return 0; + } + } else if (strcmp(opt, "utf8") == 0) { + int val = 0; + if (!value || !*value) + val = 1; + else if (!simple_getbool(value, &val)) + goto needs_bool; + use_utf8 = val; + } else { + printk(KERN_ERR "NTFS: unkown option '%s'\n", opt); + return 0; + } + } +done: + if (use_utf8 == -1) { + /* utf8 was not specified at all. */ + if (!nls_map) { + /* + * No NLS was specified. If first mount, load the + * default NLS, otherwise don't change the NLS setting. + */ + if (vol->nls_map == (void*)-1) + vol->nls_map = load_nls_default(); + } else { + /* If an NLS was already loaded, unload it first. */ + if (vol->nls_map && vol->nls_map != (void*)-1) + unload_nls(vol->nls_map); + /* Use the specified NLS. */ + vol->nls_map = nls_map; + } + } else { + /* utf8 was specified. */ + if (use_utf8 && nls_map) { + unload_nls(nls_map); + printk(KERN_ERR "NTFS: utf8 cannot be combined with " + "iocharset.\n"); + return 0; + } + /* If an NLS was already loaded, unload it first. */ + if (vol->nls_map && vol->nls_map != (void*)-1) + unload_nls(vol->nls_map); + if (!use_utf8) { + /* utf8 was specified as false. */ + if (!nls_map) + /* No NLS was specified, load the default. */ + vol->nls_map = load_nls_default(); + else + /* Use the specified NLS. */ + vol->nls_map = nls_map; + } else + /* utf8 was specified as true. */ + vol->nls_map = NULL; + } + if (uid != -1) + vol->uid = uid; + if (gid != -1) + vol->gid = gid; + if (umask != -1) + vol->umask = (ntmode_t)umask; + if (ngt != -1) + vol->ngt = ngt; + if (mft_zone_mul != -1) { + /* mft_zone_multiplier was specified. */ + if (vol->mft_zone_multiplier != -1) { + /* This is a remount, ignore a change and warn user. */ + if (vol->mft_zone_multiplier != mft_zone_mul) + printk(KERN_WARNING "NTFS: Ignoring changes in " + "mft_zone_multiplier on " + "remount. If you want to " + "change this you need to " + "umount and mount again.\n"); + } else + /* Use the specified multiplier. */ + vol->mft_zone_multiplier = mft_zone_mul; + } else if (vol->mft_zone_multiplier == -1) + /* No multiplier specified and first mount, so set default. */ + vol->mft_zone_multiplier = 1; + return 1; +needs_arg: + printk(KERN_ERR "NTFS: %s needs an argument", opt); + return 0; +needs_bool: + printk(KERN_ERR "NTFS: %s needs boolean argument", opt); + return 0; +} + +static struct dentry *ntfs_lookup(struct inode *dir, struct dentry *d) +{ + struct inode *res = 0; + char *item = 0; + ntfs_iterate_s walk; + int err; + + ntfs_debug(DEBUG_NAME1, __FUNCTION__ "(): Looking up %s in directory " + "ino 0x%x.\n", d->d_name.name, (unsigned)dir->i_ino); + walk.name = NULL; + walk.namelen = 0; + /* Convert to wide string. */ + err = ntfs_decodeuni(NTFS_INO2VOL(dir), (char*)d->d_name.name, + d->d_name.len, &walk.name, &walk.namelen); + if (err) + goto err_ret; + item = ntfs_malloc(ITEM_SIZE); + if (!item) { + err = -ENOMEM; + goto err_ret; + } + /* ntfs_getdir will place the directory entry into item, and the first + * long long is the MFT record number. */ + walk.type = BY_NAME; + walk.dir = NTFS_LINO2NINO(dir); + walk.result = item; + if (ntfs_getdir_byname(&walk)) + res = iget(dir->i_sb, NTFS_GETU32(item)); + d_add(d, res); + ntfs_free(item); + ntfs_free(walk.name); + /* Always return success, the dcache will handle negative entries. */ + return NULL; +err_ret: + ntfs_free(walk.name); + return ERR_PTR(err); +} + +static struct file_operations ntfs_file_operations = { + llseek: generic_file_llseek, + read: ntfs_read, +#ifdef CONFIG_NTFS_RW + write: ntfs_write, +#endif + open: generic_file_open, +}; + +static struct inode_operations ntfs_inode_operations; + +#ifdef CONFIG_NTFS_RW +static int ntfs_create(struct inode* dir, struct dentry *d, int mode) +{ + struct inode *r = 0; + ntfs_inode *ino = 0; + ntfs_volume *vol; + int error = 0; + ntfs_attribute *si; + + r = new_inode(dir->i_sb); + if (!r) { + error = -ENOMEM; + goto fail; + } + ntfs_debug(DEBUG_OTHER, "ntfs_create %s\n", d->d_name.name); + vol = NTFS_INO2VOL(dir); + ino = NTFS_LINO2NINO(r); + error = ntfs_alloc_file(NTFS_LINO2NINO(dir), ino, (char*)d->d_name.name, + d->d_name.len); + if (error) { + ntfs_error("ntfs_alloc_file FAILED: error = %i", error); + goto fail; + } + /* Not doing this one was causing a huge amount of corruption! Now the + * bugger bytes the dust! (-8 (AIA) */ + r->i_ino = ino->i_number; + error = ntfs_update_inode(ino); + if (error) + goto fail; + error = ntfs_update_inode(NTFS_LINO2NINO(dir)); + if (error) + goto fail; + r->i_uid = vol->uid; + r->i_gid = vol->gid; + /* FIXME: dirty? dev? */ + /* Get the file modification times from the standard information. */ + si = ntfs_find_attr(ino, vol->at_standard_information, NULL); + if (si) { + char *attr = si->d.data; + r->i_atime = ntfs_ntutc2unixutc(NTFS_GETU64(attr + 0x18)); + r->i_ctime = ntfs_ntutc2unixutc(NTFS_GETU64(attr)); + r->i_mtime = ntfs_ntutc2unixutc(NTFS_GETU64(attr + 8)); + } + /* It's not a directory */ + r->i_op = &ntfs_inode_operations; + r->i_fop = &ntfs_file_operations; + r->i_mode = S_IFREG | S_IRUGO; +#ifdef CONFIG_NTFS_RW + r->i_mode |= S_IWUGO; +#endif + r->i_mode &= ~vol->umask; + insert_inode_hash(r); + d_instantiate(d, r); + return 0; + fail: + if (r) + iput(r); + return error; +} + +static int _linux_ntfs_mkdir(struct inode *dir, struct dentry* d, int mode) +{ + int error; + struct inode *r = 0; + ntfs_volume *vol; + ntfs_inode *ino; + ntfs_attribute *si; + + ntfs_debug (DEBUG_DIR1, "mkdir %s in %x\n", d->d_name.name, dir->i_ino); + error = -ENAMETOOLONG; + if (d->d_name.len > /* FIXME: */ 255) + goto out; + error = -EIO; + r = new_inode(dir->i_sb); + if (!r) + goto out; + vol = NTFS_INO2VOL(dir); + ino = NTFS_LINO2NINO(r); + error = ntfs_mkdir(NTFS_LINO2NINO(dir), d->d_name.name, d->d_name.len, + ino); + if (error) + goto out; + /* Not doing this one was causing a huge amount of corruption! Now the + * bugger bytes the dust! (-8 (AIA) */ + r->i_ino = ino->i_number; + r->i_uid = vol->uid; + r->i_gid = vol->gid; + si = ntfs_find_attr(ino, vol->at_standard_information, NULL); + if (si) { + char *attr = si->d.data; + r->i_atime = ntfs_ntutc2unixutc(NTFS_GETU64(attr + 0x18)); + r->i_ctime = ntfs_ntutc2unixutc(NTFS_GETU64(attr)); + r->i_mtime = ntfs_ntutc2unixutc(NTFS_GETU64(attr + 8)); + } + /* It's a directory. */ + r->i_op = &ntfs_dir_inode_operations; + r->i_fop = &ntfs_dir_operations; + r->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; +#ifdef CONFIG_NTFS_RW + r->i_mode |= S_IWUGO; +#endif + r->i_mode &= ~vol->umask; + + insert_inode_hash(r); + d_instantiate(d, r); + error = 0; + out: + ntfs_debug (DEBUG_DIR1, "mkdir returns %d\n", error); + return error; +} +#endif + +static struct file_operations ntfs_dir_operations = { + read: generic_read_dir, + readdir: ntfs_readdir, +}; + +static struct inode_operations ntfs_dir_inode_operations = { + lookup: ntfs_lookup, +#ifdef CONFIG_NTFS_RW + create: ntfs_create, + mkdir: _linux_ntfs_mkdir, +#endif +}; + +/* ntfs_read_inode() is called by the Virtual File System (the kernel layer + * that deals with filesystems) when iget is called requesting an inode not + * already present in the inode table. Typically filesystems have separate + * inode_operations for directories, files and symlinks. */ +static void ntfs_read_inode(struct inode* inode) +{ + ntfs_volume *vol; + ntfs_inode *ino; + ntfs_attribute *data; + ntfs_attribute *si; + + vol = NTFS_INO2VOL(inode); + inode->i_mode = 0; + ntfs_debug(DEBUG_OTHER, "ntfs_read_inode 0x%lx\n", inode->i_ino); + switch (inode->i_ino) { + /* Those are loaded special files. */ + case FILE_Mft: + if (!vol->mft_ino || ((vol->ino_flags & 1) == 0)) + goto sys_file_error; + ntfs_memcpy(&inode->u.ntfs_i, vol->mft_ino, sizeof(ntfs_inode)); + ino = vol->mft_ino; + vol->mft_ino = &inode->u.ntfs_i; + vol->ino_flags &= ~1; + ntfs_free(ino); + ino = vol->mft_ino; + ntfs_debug(DEBUG_OTHER, "Opening $MFT!\n"); + break; + case FILE_MftMirr: + if (!vol->mftmirr || ((vol->ino_flags & 2) == 0)) + goto sys_file_error; + ntfs_memcpy(&inode->u.ntfs_i, vol->mftmirr, sizeof(ntfs_inode)); + ino = vol->mftmirr; + vol->mftmirr = &inode->u.ntfs_i; + vol->ino_flags &= ~2; + ntfs_free(ino); + ino = vol->mftmirr; + ntfs_debug(DEBUG_OTHER, "Opening $MFTMirr!\n"); + break; + case FILE_BitMap: + if (!vol->bitmap || ((vol->ino_flags & 4) == 0)) + goto sys_file_error; + ntfs_memcpy(&inode->u.ntfs_i, vol->bitmap, sizeof(ntfs_inode)); + ino = vol->bitmap; + vol->bitmap = &inode->u.ntfs_i; + vol->ino_flags &= ~4; + ntfs_free(ino); + ino = vol->bitmap; + ntfs_debug(DEBUG_OTHER, "Opening $Bitmap!\n"); + break; + case FILE_LogFile ... FILE_AttrDef: + /* No need to log root directory accesses. */ + case FILE_Boot ... FILE_UpCase: + ntfs_debug(DEBUG_OTHER, "Opening system file %i!\n", + inode->i_ino); + default: + ino = &inode->u.ntfs_i; + if (!ino || ntfs_init_inode(ino, NTFS_INO2VOL(inode), + inode->i_ino)) + { + ntfs_debug(DEBUG_OTHER, "NTFS: Error loading inode " + "0x%x\n", (unsigned int)inode->i_ino); + return; + } + } + /* Set uid/gid from mount options */ + inode->i_uid = vol->uid; + inode->i_gid = vol->gid; + inode->i_nlink = 1; + /* Use the size of the data attribute as file size */ + data = ntfs_find_attr(ino, vol->at_data, NULL); + if (!data) + inode->i_size = 0; + else + inode->i_size = data->size; + /* Get the file modification times from the standard information. */ + si = ntfs_find_attr(ino, vol->at_standard_information, NULL); + if (si) { + char *attr = si->d.data; + inode->i_atime = ntfs_ntutc2unixutc(NTFS_GETU64(attr + 0x18)); + inode->i_ctime = ntfs_ntutc2unixutc(NTFS_GETU64(attr)); + inode->i_mtime = ntfs_ntutc2unixutc(NTFS_GETU64(attr + 8)); + } + /* If it has an index root, it's a directory. */ + if (ntfs_find_attr(ino, vol->at_index_root, "$I30")) { + ntfs_attribute *at; + at = ntfs_find_attr(ino, vol->at_index_allocation, "$I30"); + inode->i_size = at ? at->size : 0; + inode->i_op = &ntfs_dir_inode_operations; + inode->i_fop = &ntfs_dir_operations; + inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; + } else { + inode->i_op = &ntfs_inode_operations; + inode->i_fop = &ntfs_file_operations; + inode->i_mode = S_IFREG | S_IRUGO; + } +#ifdef CONFIG_NTFS_RW + if (!data || !(data->flags & (ATTR_IS_COMPRESSED | ATTR_IS_ENCRYPTED))) + inode->i_mode |= S_IWUGO; +#endif + inode->i_mode &= ~vol->umask; + return; +sys_file_error: + ntfs_error("Critical error. Tried to call ntfs_read_inode() before we " + "have completed read_super() or VFS error.\n"); + // FIXME: Should we panic() at this stage? +} + +#ifdef CONFIG_NTFS_RW +static void ntfs_write_inode(struct inode *ino, int unused) +{ + lock_kernel(); + ntfs_debug(DEBUG_LINUX, "ntfs_write_inode 0x%x\n", ino->i_ino); + ntfs_update_inode(NTFS_LINO2NINO(ino)); + unlock_kernel(); +} +#endif + +static void _ntfs_clear_inode(struct inode *inode) +{ + ntfs_inode *ino; + ntfs_volume *vol; + + lock_kernel(); + ntfs_debug(DEBUG_OTHER, "_ntfs_clear_inode 0x%x\n", inode->i_ino); + vol = NTFS_INO2VOL(inode); + if (!vol) + ntfs_error("_ntfs_clear_inode: vol = NTFS_INO2VOL(inode) is " + "NULL.\n"); + switch (inode->i_ino) { + case FILE_Mft: + if (vol->mft_ino && ((vol->ino_flags & 1) == 0)) { + ino = (ntfs_inode*)ntfs_malloc(sizeof(ntfs_inode)); + ntfs_memcpy(ino, &inode->u.ntfs_i, sizeof(ntfs_inode)); + vol->mft_ino = ino; + vol->ino_flags |= 1; + goto unl_out; + } + break; + case FILE_MftMirr: + if (vol->mftmirr && ((vol->ino_flags & 2) == 0)) { + ino = (ntfs_inode*)ntfs_malloc(sizeof(ntfs_inode)); + ntfs_memcpy(ino, &inode->u.ntfs_i, sizeof(ntfs_inode)); + vol->mftmirr = ino; + vol->ino_flags |= 2; + goto unl_out; + } + break; + case FILE_BitMap: + if (vol->bitmap && ((vol->ino_flags & 4) == 0)) { + ino = (ntfs_inode*)ntfs_malloc(sizeof(ntfs_inode)); + ntfs_memcpy(ino, &inode->u.ntfs_i, sizeof(ntfs_inode)); + vol->bitmap = ino; + vol->ino_flags |= 4; + goto unl_out; + } + break; + default: + /* Nothing. Just clear the inode and exit. */ + } + ntfs_clear_inode(&inode->u.ntfs_i); +unl_out: + unlock_kernel(); + return; +} + +/* Called when umounting a filesystem by do_umount() in fs/super.c. */ +static void ntfs_put_super(struct super_block *sb) +{ + ntfs_volume *vol; + + ntfs_debug(DEBUG_OTHER, "ntfs_put_super\n"); + vol = NTFS_SB2VOL(sb); + ntfs_release_volume(vol); + if (vol->nls_map) + unload_nls(vol->nls_map); + ntfs_debug(DEBUG_OTHER, "ntfs_put_super: done\n"); +} + +/* Called by the kernel when asking for stats. */ +static int ntfs_statfs(struct super_block *sb, struct statfs *sf) +{ + struct inode *mft; + ntfs_volume *vol; + __s64 size; + int error; + + ntfs_debug(DEBUG_OTHER, "ntfs_statfs\n"); + vol = NTFS_SB2VOL(sb); + sf->f_type = NTFS_SUPER_MAGIC; + sf->f_bsize = vol->cluster_size; + error = ntfs_get_volumesize(NTFS_SB2VOL(sb), &size); + if (error) + return error; + sf->f_blocks = size; /* Volumesize is in clusters. */ + size = (__s64)ntfs_get_free_cluster_count(vol->bitmap); + /* Just say zero if the call failed. */ + if (size < 0LL) + size = 0; + sf->f_bfree = sf->f_bavail = size; + ntfs_debug(DEBUG_OTHER, "ntfs_statfs: calling mft = iget(sb, " + "FILE_Mft)\n"); + mft = iget(sb, FILE_Mft); + ntfs_debug(DEBUG_OTHER, "ntfs_statfs: iget(sb, FILE_Mft) returned " + "0x%x\n", mft); + if (!mft) + return -EIO; + sf->f_files = mft->i_size >> vol->mft_record_size_bits; + ntfs_debug(DEBUG_OTHER, "ntfs_statfs: calling iput(mft)\n"); + iput(mft); + /* Should be read from volume. */ + sf->f_namelen = 255; + return 0; +} + +/* Called when remounting a filesystem by do_remount_sb() in fs/super.c. */ +static int ntfs_remount_fs(struct super_block *sb, int *flags, char *options) +{ + if (!parse_options(NTFS_SB2VOL(sb), options)) + return -EINVAL; + return 0; +} + +/* Define the super block operation that are implemented */ +static struct super_operations ntfs_super_operations = { + read_inode: ntfs_read_inode, +#ifdef CONFIG_NTFS_RW + write_inode: ntfs_write_inode, +#endif + put_super: ntfs_put_super, + statfs: ntfs_statfs, + remount_fs: ntfs_remount_fs, + clear_inode: _ntfs_clear_inode, +}; + +/** + * is_boot_sector_ntfs - check an NTFS boot sector for validity + * @b: buffer containing bootsector to check + * + * Check whether @b contains a valid NTFS boot sector. + * Return 1 if @b is a valid NTFS bootsector or 0 if not. + */ +static int is_boot_sector_ntfs(ntfs_u8 *b) +{ + ntfs_u32 i; + + /* FIXME: We don't use checksumming yet as NT4(SP6a) doesn't either... + * But we might as well have the code ready to do it. (AIA) */ +#if 0 + /* Calculate the checksum. */ + if (b < b + 0x50) { + ntfs_u32 *u; + ntfs_u32 *bi = (ntfs_u32 *)(b + 0x50); + + for (u = bi, i = 0; u < bi; ++u) + i += NTFS_GETU32(*u); + } +#endif + /* Check magic is "NTFS " */ + if (b[3] != 0x4e) goto not_ntfs; + if (b[4] != 0x54) goto not_ntfs; + if (b[5] != 0x46) goto not_ntfs; + if (b[6] != 0x53) goto not_ntfs; + for (i = 7; i < 0xb; ++i) + if (b[i] != 0x20) goto not_ntfs; + /* Check bytes per sector value is between 512 and 4096. */ + if (b[0xb] != 0) goto not_ntfs; + if (b[0xc] > 0x10) goto not_ntfs; + /* Check sectors per cluster value is valid. */ + switch (b[0xd]) { + case 1: case 2: case 4: case 8: case 16: + case 32: case 64: case 128: + break; + default: + goto not_ntfs; + } + /* Check reserved sectors value and four other fields are zero. */ + for (i = 0xe; i < 0x15; ++i) + if (b[i] != 0) goto not_ntfs; + if (b[0x16] != 0) goto not_ntfs; + if (b[0x17] != 0) goto not_ntfs; + for (i = 0x20; i < 0x24; ++i) + if (b[i] != 0) goto not_ntfs; + /* Check clusters per file record segment value is valid. */ + if (b[0x40] < 0xe1 || b[0x40] > 0xf7) { + switch (b[0x40]) { + case 1: case 2: case 4: case 8: case 16: case 32: case 64: + break; + default: + goto not_ntfs; + } + } + /* Check clusters per index block value is valid. */ + if (b[0x44] < 0xe1 || b[0x44] > 0xf7) { + switch (b[0x44]) { + case 1: case 2: case 4: case 8: case 16: case 32: case 64: + break; + default: + goto not_ntfs; + } + } + return 1; +not_ntfs: + return 0; +} + +/* Called to mount a filesystem by read_super() in fs/super.c. + * Return a super block, the main structure of a filesystem. + * + * NOTE : Don't store a pointer to an option, as the page containing the + * options is freed after ntfs_read_super() returns. + * + * NOTE : A context switch can happen in kernel code only if the code blocks + * (= calls schedule() in kernel/sched.c). */ +struct super_block *ntfs_read_super(struct super_block *sb, void *options, + int silent) +{ + ntfs_volume *vol; + struct buffer_head *bh; + int i, to_read, blocksize; + + ntfs_debug(DEBUG_OTHER, "ntfs_read_super\n"); + vol = NTFS_SB2VOL(sb); + init_ntfs_super_block(vol); + if (!parse_options(vol, (char*)options)) + goto ntfs_read_super_vol; + blocksize = get_hardsect_size(sb->s_dev); + if (blocksize < 512) + blocksize = 512; + if (set_blocksize(sb->s_dev, blocksize) < 0) { + ntfs_error("Unable to set blocksize %d.\n", blocksize); + goto ntfs_read_super_vol; + } + /* Read the super block (boot block). */ + if (!(bh = bread(sb->s_dev, 0, blocksize))) { + ntfs_error("Reading super block failed\n"); + goto ntfs_read_super_unl; + } + ntfs_debug(DEBUG_OTHER, "Done reading boot block\n"); + /* Check for valid 'NTFS' boot sector. */ + if (!is_boot_sector_ntfs(bh->b_data)) { + ntfs_debug(DEBUG_OTHER, "Not a NTFS volume\n"); + bforget(bh); + goto ntfs_read_super_unl; + } + ntfs_debug(DEBUG_OTHER, "Going to init volume\n"); + if (ntfs_init_volume(vol, bh->b_data) < 0) { + ntfs_debug(DEBUG_OTHER, "Init volume failed.\n"); + bforget(bh); + goto ntfs_read_super_unl; + } + ntfs_debug(DEBUG_OTHER, "$Mft at cluster 0x%lx\n", vol->mft_lcn); + brelse(bh); + NTFS_SB(vol) = sb; + if (vol->cluster_size > PAGE_SIZE) { + ntfs_error("Partition cluster size is not supported yet (it " + "is > max kernel blocksize).\n"); + goto ntfs_read_super_unl; + } + ntfs_debug(DEBUG_OTHER, "Done to init volume\n"); + /* Inform the kernel that a device block is a NTFS cluster. */ + sb->s_blocksize = vol->cluster_size; + sb->s_blocksize_bits = vol->cluster_size_bits; + if (blocksize != vol->cluster_size && + set_blocksize(sb->s_dev, sb->s_blocksize) < 0) { + ntfs_error("Cluster size too small for device.\n"); + goto ntfs_read_super_unl; + } + ntfs_debug(DEBUG_OTHER, "set_blocksize\n"); + /* Allocate an MFT record (MFT record can be smaller than a cluster). */ + i = vol->cluster_size; + if (i < vol->mft_record_size) + i = vol->mft_record_size; + if (!(vol->mft = ntfs_malloc(i))) + goto ntfs_read_super_unl; + + /* Read at least the MFT record for $Mft. */ + to_read = vol->mft_clusters_per_record; + if (to_read < 1) + to_read = 1; + for (i = 0; i < to_read; i++) { + if (!(bh = bread(sb->s_dev, vol->mft_lcn + i, + vol->cluster_size))) { + ntfs_error("Could not read $Mft record 0\n"); + goto ntfs_read_super_mft; + } + ntfs_memcpy(vol->mft + ((__s64)i << vol->cluster_size_bits), + bh->b_data, vol->cluster_size); + brelse(bh); + ntfs_debug(DEBUG_OTHER, "Read cluster 0x%x\n", + vol->mft_lcn + i); + } + /* Check and fixup this MFT record */ + if (!ntfs_check_mft_record(vol, vol->mft)){ + ntfs_error("Invalid $Mft record 0\n"); + goto ntfs_read_super_mft; + } + /* Inform the kernel about which super operations are available. */ + sb->s_op = &ntfs_super_operations; + sb->s_magic = NTFS_SUPER_MAGIC; + sb->s_maxbytes = ~0ULL >> 1; + ntfs_debug(DEBUG_OTHER, "Reading special files\n"); + if (ntfs_load_special_files(vol)) { + ntfs_error("Error loading special files\n"); + goto ntfs_read_super_mft; + } + ntfs_debug(DEBUG_OTHER, "Getting RootDir\n"); + /* Get the root directory. */ + if (!(sb->s_root = d_alloc_root(iget(sb, FILE_root)))) { + ntfs_error("Could not get root dir inode\n"); + goto ntfs_read_super_mft; + } +ntfs_read_super_ret: + ntfs_debug(DEBUG_OTHER, "read_super: done\n"); + return sb; +ntfs_read_super_mft: + ntfs_free(vol->mft); +ntfs_read_super_unl: +ntfs_read_super_vol: + sb = NULL; + goto ntfs_read_super_ret; +} + +/* Define the filesystem */ +static DECLARE_FSTYPE_DEV(ntfs_fs_type, "ntfs", ntfs_read_super); + +static int __init init_ntfs_fs(void) +{ + /* Comment this if you trust klogd. There are reasons not to trust it */ +#if defined(DEBUG) && !defined(MODULE) + console_verbose(); +#endif + printk(KERN_NOTICE "NTFS driver v" NTFS_VERSION " [Flags: R/" +#ifdef CONFIG_NTFS_RW + "W" +#else + "O" +#endif +#ifdef DEBUG + " DEBUG" +#endif +#ifdef MODULE + " MODULE" +#endif + "]\n"); + SYSCTL(1); + ntfs_debug(DEBUG_OTHER, "registering %s\n", ntfs_fs_type.name); + /* Add this filesystem to the kernel table of filesystems. */ + return register_filesystem(&ntfs_fs_type); +} + +static void __exit exit_ntfs_fs(void) +{ + SYSCTL(0); + ntfs_debug(DEBUG_OTHER, "unregistering %s\n", ntfs_fs_type.name); + unregister_filesystem(&ntfs_fs_type); +} + +EXPORT_NO_SYMBOLS; +/* + * Not strictly true. The driver was written originally by Martin von Löwis. + * I am just maintaining and rewriting it. + */ +MODULE_AUTHOR("Anton Altaparmakov "); +MODULE_DESCRIPTION("Linux NTFS driver"); +MODULE_LICENSE("GPL"); +#ifdef DEBUG +MODULE_PARM(ntdebug, "i"); +MODULE_PARM_DESC(ntdebug, "Debug level"); +#endif + +module_init(init_ntfs_fs) +module_exit(exit_ntfs_fs) +