summaryrefslogtreecommitdiffstats
path: root/fs/ntfs/fs.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/ntfs/fs.c')
-rw-r--r--fs/ntfs/fs.c981
1 files changed, 981 insertions, 0 deletions
diff --git a/fs/ntfs/fs.c b/fs/ntfs/fs.c
new file mode 100644
index 000000000..aa6a7c40c
--- /dev/null
+++ b/fs/ntfs/fs.c
@@ -0,0 +1,981 @@
+/*
+ * fs.c
+ * NTFS driver for Linux 2.1
+ *
+ * Copyright (C) 1995-1997 Martin von Löwis
+ * Copyright (C) 1996 Richard Russon
+ * Copyright (C) 1996-1997 Régis Duchesne
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#ifdef NTFS_IN_LINUX_KERNEL
+#include <linux/config.h>
+#endif
+
+#include "types.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 <linux/module.h>
+#include <asm/uaccess.h>
+#include <linux/nls.h>
+#include <linux/locks.h>
+#include <linux/init.h>
+
+#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
+static void ntfs_getuser(void *dest,ntfs_io *src,ntfs_size_t len)
+{
+ copy_from_user(dest,src->param,len);
+ src->param+=len;
+}
+#endif
+
+static ssize_t
+ntfs_read(struct file * filp, char *buf, size_t count, loff_t *off)
+{
+ int error;
+ ntfs_io io;
+ 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,%x,%x ->",
+ (unsigned)ino->i_number,(unsigned)*off,(unsigned)count);
+ /* inode has no unnamed data attribute */
+ if(!ntfs_find_attr(ino,ino->vol->at_data,NULL))
+ return -EINVAL;
+
+ /* 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)return -error;
+
+ *off+=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)
+{
+ struct super_block* sb;
+ int ret;
+ ntfs_io io;
+ ntfs_inode *ino=NTFS_LINO2NINO(filp->f_dentry->d_inode);
+
+ if(!ino)return -EINVAL;
+ ntfs_debug(DEBUG_OTHER, "ntfs_write %x,%x,%x ->",
+ (unsigned)ino->i_number,(unsigned)*pos,(unsigned)count);
+ sb = filp->f_dentry->d_inode->i_sb;
+ /* Allows to lock fs ro at any time */
+ if(sb->s_flags & MS_RDONLY)
+ return -ENOSPC;
+ if(!ntfs_find_attr(ino,ino->vol->at_data,NULL))
+ return -EINVAL;
+
+ io.fn_put=0;
+ io.fn_get=ntfs_getuser;
+ io.param=(void*)buf; /* to get rid of the const */
+ io.size=count;
+ ret = ntfs_write_attr(ino,ino->vol->at_data,NULL,*pos,&io);
+ ntfs_debug(DEBUG_OTHER, "%x\n",ret);
+ if(ret<0)return -EINVAL;
+
+ *pos+=ret;
+ return ret;
+}
+#endif
+
+struct ntfs_filldir{
+ struct inode *dir;
+ filldir_t filldir;
+ unsigned int type;
+ ntfs_u32 ph,pl;
+ void *dirent;
+ char *name;
+ int namelen;
+};
+
+static int ntfs_printcb(ntfs_u8 *entry,void *param)
+{
+ struct ntfs_filldir* nf=param;
+ int flags=NTFS_GETU8(entry+0x51);
+ int show_hidden=0,to_lower=0;
+ int length=NTFS_GETU8(entry+0x50);
+ int inum=NTFS_GETU32(entry);
+ int i,error;
+ switch(nf->type){
+ case ngt_dos:
+ /* Don't display long names */
+ if((flags & 2)==0)
+ return 0;
+ break;
+ case ngt_nt:
+ /* Don't display short-only names */
+ switch(flags&3){
+ case 2: return 0;
+ case 3: to_lower=1;
+ }
+ break;
+ case ngt_posix:
+ break;
+ case ngt_full:
+ show_hidden=1;
+ break;
+ }
+ if(!show_hidden && ((NTFS_GETU8(entry+0x48) & 2)==2)){
+ ntfs_debug(DEBUG_OTHER,"Skipping hidden file\n");
+ return 0;
+ }
+ nf->name=0;
+ if(ntfs_encodeuni(NTFS_INO2VOL(nf->dir),(ntfs_u16*)(entry+0x52),
+ length,&nf->name,&nf->namelen)){
+ ntfs_debug(DEBUG_OTHER,"Skipping unrepresentable file\n");
+ if(nf->name)ntfs_free(nf->name);
+ return 0;
+ }
+ /* Do not return ".", as this is faked */
+ if(length==1 && *nf->name=='.')
+ return 0;
+ if(to_lower)
+ for(i=0;i<nf->namelen;i++)
+ /* This supports ASCII only. Since only DOS-only
+ names get converted, and since those are restricted
+ to ASCII, this should be correct */
+ if(nf->name[i]>='A' && nf->name[i]<='Z')
+ nf->name[i]+='a'-'A';
+ nf->name[nf->namelen]=0;
+ ntfs_debug(DEBUG_OTHER, "readdir got %s,len %d\n",nf->name,nf->namelen);
+ /* filldir expects an off_t rather than an loff_t.
+ Hope we don't have more than 65535 index records */
+ error=nf->filldir(nf->dirent,nf->name,nf->namelen,
+ (nf->ph<<16)|nf->pl,inum);
+ ntfs_free(nf->name);
+ /* Linux filldir errors are negative, other errors positive */
+ return error;
+}
+
+/* readdir returns '..', then '.', then the directory entries in sequence
+ As the root directory contains a entry for itself, '.' is not emulated
+ for the root directory */
+static int ntfs_readdir(struct file* filp, void *dirent, filldir_t filldir)
+{
+ struct ntfs_filldir cb;
+ int error;
+ struct inode *dir=filp->f_dentry->d_inode;
+
+ ntfs_debug(DEBUG_OTHER, "ntfs_readdir ino %x mode %x\n",
+ (unsigned)dir->i_ino,(unsigned int)dir->i_mode);
+ if(!dir || (dir->i_ino==0) || !S_ISDIR(dir->i_mode))return -EBADF;
+
+ ntfs_debug(DEBUG_OTHER, "readdir: Looking for file %x dircount %d\n",
+ (unsigned)filp->f_pos,dir->i_count);
+ cb.pl=filp->f_pos & 0xFFFF;
+ cb.ph=filp->f_pos >> 16;
+ /* end of directory */
+ if(cb.ph==0xFFFF){
+ /* FIXME: Maybe we can return those with the previous call */
+ switch(cb.pl){
+ case 0: filldir(dirent,".",1,filp->f_pos,dir->i_ino);
+ filp->f_pos=0xFFFF0001;
+ return 0;
+ /* FIXME: parent directory */
+ case 1: filldir(dirent,"..",2,filp->f_pos,0);
+ filp->f_pos=0xFFFF0002;
+ return 0;
+ }
+ ntfs_debug(DEBUG_OTHER, "readdir: EOD\n");
+ return 0;
+ }
+ cb.dir=dir;
+ cb.filldir=filldir;
+ cb.dirent=dirent;
+ cb.type=NTFS_INO2VOL(dir)->ngt;
+ do{
+ ntfs_debug(DEBUG_OTHER,"looking for next file\n");
+ error=ntfs_getdir_unsorted(NTFS_LINO2NINO(dir),&cb.ph,&cb.pl,
+ ntfs_printcb,&cb);
+ }while(!error && cb.ph!=0xFFFFFFFF);
+ filp->f_pos=(cb.ph<<16)|cb.pl;
+ ntfs_debug(DEBUG_OTHER, "new position %x\n",(unsigned)filp->f_pos);
+ /* -EINVAL is on user buffer full. This is not considered
+ as an error by sys_getdents */
+ if(error<0)
+ error=0;
+ /* Otherwise (device error, inconsistent data), switch the sign */
+ return -error;
+}
+
+/* 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;
+}
+
+/* Parse the (re)mount options */
+static int parse_options(ntfs_volume* vol,char *opt)
+{
+ char *value;
+
+ vol->uid=vol->gid=0;
+ vol->umask=0077;
+ vol->ngt=ngt_nt;
+ vol->nls_map=0;
+ vol->nct=0;
+ 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;
+ vol->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;
+ vol->gid=simple_strtoul(value,&value,0);
+ if(*value){
+ printk(KERN_ERR "gid invalid argument\n");
+ return 0;
+ }
+ }else if(strcmp(opt, "umask") == 0)
+ {
+ if(!value || !*value)goto needs_arg;
+ vol->umask=simple_strtoul(value,&value,0);
+ if(*value){
+ printk(KERN_ERR "umask invalid argument\n");
+ return 0;
+ }
+ }else if(strcmp(opt, "iocharset") == 0){
+ if(!value || !*value)goto needs_arg;
+ vol->nls_map=load_nls(value);
+ vol->nct |= nct_map;
+ if(!vol->nls_map){
+ printk(KERN_ERR "NTFS: charset not found");
+ return 0;
+ }
+ }else if(strcmp(opt, "posix") == 0){
+ int val;
+ if(!value || !*value)goto needs_arg;
+ if(!simple_getbool(value,&val))
+ goto needs_bool;
+ vol->ngt=val?ngt_posix:ngt_nt;
+ }else if(strcmp(opt,"utf8") == 0){
+ int val=0;
+ if(!value || !*value)
+ val=1;
+ else if(!simple_getbool(value,&val))
+ goto needs_bool;
+ if(val)
+ vol->nct|=nct_utf8;
+ }else if(strcmp(opt,"uni_xlate") == 0){
+ int val=0;
+ /* no argument: uni_vfat.
+ boolean argument: uni_vfat.
+ "2": uni.
+ */
+ if(!value || !*value)
+ val=1;
+ else if(strcmp(value,"2")==0)
+ vol->nct |= nct_uni_xlate;
+ else if(!simple_getbool(value,&val))
+ goto needs_bool;
+ if(val)
+ vol->nct |= nct_uni_xlate_vfat | nct_uni_xlate;
+ }else{
+ printk(KERN_ERR "NTFS: unkown option '%s'\n", opt);
+ return 0;
+ }
+ }
+ if(vol->nct & nct_utf8 & (nct_map | nct_uni_xlate)){
+ printk(KERN_ERR "utf8 cannot be combined with iocharset or uni_xlate\n");
+ return 0;
+ }
+ done:
+ if((vol->nct & (nct_uni_xlate | nct_map | nct_utf8))==0)
+ /* default to UTF-8 */
+ vol->nct=nct_utf8;
+ if(!vol->nls_map)
+ vol->nls_map=load_nls_default();
+ 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 int ntfs_lookup(struct inode *dir, struct dentry *d)
+{
+ struct inode *res=0;
+ char *item=0;
+ ntfs_iterate_s walk;
+ int error;
+ ntfs_debug(DEBUG_OTHER, "Looking up %s in %x\n",d->d_name.name,
+ (unsigned)dir->i_ino);
+ /* convert to wide string */
+ error=ntfs_decodeuni(NTFS_INO2VOL(dir),(char*)d->d_name.name,
+ d->d_name.len,&walk.name,&walk.namelen);
+ if(error)
+ return error;
+ item=ntfs_malloc(ITEM_SIZE);
+ /* 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);
+ return res?0:-ENOENT;
+}
+
+struct file_operations ntfs_file_operations_nommap = {
+ NULL, /* lseek */
+ ntfs_read,
+#ifdef CONFIG_NTFS_RW
+ ntfs_write,
+#else
+ NULL,
+#endif
+ NULL, /* readdir */
+ NULL, /* select */
+ NULL, /* ioctl */
+ NULL, /* mmap */
+ NULL, /* open */
+ NULL, /* release */
+ NULL, /* fsync */
+ NULL, /* fasync */
+ NULL, /* check_media_change */
+ NULL, /* revalidate */
+ NULL, /* lock */
+};
+
+struct inode_operations ntfs_inode_operations_nobmap = {
+ &ntfs_file_operations_nommap,
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL, /* permission */
+ NULL, /* smap */
+ NULL, /* updatepage */
+ NULL, /* revalidate */
+};
+
+#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=get_empty_inode();
+ if(!r){
+ error=ENOMEM;
+ goto fail;
+ }
+
+ ntfs_debug(DEBUG_OTHER, "ntfs_create %s\n",d->d_name.name);
+ vol=NTFS_INO2VOL(dir);
+#ifdef NTFS_IN_LINUX_KERNEL
+ ino=NTFS_LINO2NINO(r);
+#else
+ ino=ntfs_malloc(sizeof(ntfs_inode));
+ if(!ino){
+ error=ENOMEM;
+ goto fail;
+ }
+ r->u.generic_ip=ino;
+#endif
+ error=ntfs_alloc_inode(NTFS_LINO2NINO(dir),ino,(char*)d->d_name.name,
+ d->d_name.len);
+ if(error)goto fail;
+ 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;
+ r->i_nlink=1;
+ r->i_sb=dir->i_sb;
+ /* 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_nobmap;
+ r->i_mode=S_IFREG|S_IRUGO;
+#ifdef CONFIG_NTFS_RW
+ r->i_mode|=S_IWUGO;
+#endif
+ r->i_mode &= ~vol->umask;
+
+ d_instantiate(d,r);
+ return 0;
+ fail:
+ #ifndef NTFS_IN_LINUX_KERNEL
+ if(ino)ntfs_free(ino);
+ #endif
+ if(r)iput(r);
+ return -error;
+}
+#endif
+
+static int
+ntfs_bmap(struct inode *ino,int block)
+{
+ int ret=ntfs_vcn_to_lcn(NTFS_LINO2NINO(ino),block);
+ ntfs_debug(DEBUG_OTHER, "bmap of %lx,block %x is %x\n",
+ ino->i_ino,block,ret);
+ return (ret==-1) ? 0:ret;
+}
+
+struct file_operations ntfs_file_operations = {
+ NULL, /* lseek */
+ ntfs_read,
+#ifdef CONFIG_NTFS_RW
+ ntfs_write,
+#else
+ NULL,
+#endif
+ NULL, /* readdir */
+ NULL, /* select */
+ NULL, /* ioctl */
+ generic_file_mmap,
+ NULL, /* open */
+ NULL, /* release */
+ NULL, /* fsync */
+ NULL, /* fasync */
+ NULL, /* check_media_change */
+ NULL, /* revalidate */
+ NULL, /* lock */
+};
+
+struct inode_operations ntfs_inode_operations = {
+ &ntfs_file_operations,
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ generic_readpage,
+ NULL, /* writepage */
+ ntfs_bmap,
+ NULL, /* truncate */
+ NULL, /* permission */
+ NULL, /* smap */
+ NULL, /* updatepage */
+ NULL, /* revalidate */
+};
+
+struct file_operations ntfs_dir_operations = {
+ NULL, /* lseek */
+ NULL, /* read */
+ NULL, /* write */
+ ntfs_readdir, /* readdir */
+ NULL, /* poll */
+ NULL, /* ioctl */
+ NULL, /* mmap */
+ NULL, /* open */
+ NULL, /* release */
+ NULL, /* fsync */
+ NULL, /* fasync */
+ NULL, /* check_media_change */
+ NULL, /* revalidate */
+ NULL, /* lock */
+};
+
+struct inode_operations ntfs_dir_inode_operations = {
+ &ntfs_dir_operations,
+#ifdef CONFIG_NTFS_RW
+ ntfs_create, /* create */
+#else
+ NULL,
+#endif
+ ntfs_lookup, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL, /* permission */
+ NULL, /* smap */
+ NULL, /* updatepage */
+ NULL, /* revalidate */
+};
+
+/* 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;
+ int can_mmap=0;
+ ntfs_inode *ino;
+ ntfs_attribute *data;
+ ntfs_attribute *si;
+
+ vol=NTFS_INO2VOL(inode);
+ inode->i_op=NULL;
+ inode->i_mode=0;
+ ntfs_debug(DEBUG_OTHER, "ntfs_read_inode %x\n",(unsigned)inode->i_ino);
+
+ switch(inode->i_ino)
+ {
+ /* those are loaded special files */
+ case FILE_MFT:
+ ntfs_error("Trying to open MFT\n");return;
+ default:
+ #ifdef NTFS_IN_LINUX_KERNEL
+ ino=&inode->u.ntfs_i;
+ #else
+ ino=(ntfs_inode*)ntfs_malloc(sizeof(ntfs_inode));
+ inode->u.generic_ip=ino;
+ #endif
+ if(!ino || ntfs_init_inode(ino,
+ NTFS_INO2VOL(inode),inode->i_ino))
+ {
+ ntfs_debug(DEBUG_OTHER, "NTFS:Error loading inode %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;
+ can_mmap=0;
+ }
+ else
+ {
+ inode->i_size=data->size;
+ can_mmap=!data->resident && !data->compressed;
+ }
+ /* 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_mode=S_IFDIR|S_IRUGO|S_IXUGO;
+ }
+ else
+ {
+ inode->i_op=can_mmap ? &ntfs_inode_operations :
+ &ntfs_inode_operations_nobmap;
+ inode->i_mode=S_IFREG|S_IRUGO;
+ }
+#ifdef CONFIG_NTFS_RW
+ if(!data || !data->compressed)
+ inode->i_mode|=S_IWUGO;
+#endif
+ inode->i_mode &= ~vol->umask;
+}
+
+static void ntfs_put_inode(struct inode *ino)
+{
+}
+
+static void _ntfs_clear_inode(struct inode *ino)
+{
+ ntfs_debug(DEBUG_OTHER, "ntfs_clear_inode %lx\n",ino->i_ino);
+#ifdef NTFS_IN_LINUX_KERNEL
+ if(ino->i_ino!=FILE_MFT)
+ ntfs_clear_inode(&ino->u.ntfs_i);
+#else
+ if(ino->i_ino!=FILE_MFT && ino->u.generic_ip)
+ {
+ ntfs_clear_inode(ino->u.generic_ip);
+ ntfs_free(ino->u.generic_ip);
+ ino->u.generic_ip=0;
+ }
+#endif
+ 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");
+ /* Ensure that nobody uses the super block anymore */
+ lock_super(sb);
+ vol=NTFS_SB2VOL(sb);
+ /* Tell the kernel that the super block is no more used */
+ sb->s_dev = 0;
+ unlock_super(sb);
+ ntfs_release_volume(vol);
+ if(vol->nls_map)
+ unload_nls(vol->nls_map);
+#ifndef NTFS_IN_LINUX_KERNEL
+ ntfs_free(vol);
+#endif
+ ntfs_debug(DEBUG_OTHER, "ntfs_put_super: done\n");
+ MOD_DEC_USE_COUNT;
+}
+
+/* Called by the kernel when asking for stats */
+static int ntfs_statfs(struct super_block *sb, struct statfs *sf, int bufsize)
+{
+ struct statfs fs;
+ struct inode *mft;
+ ntfs_volume *vol;
+
+ ntfs_debug(DEBUG_OTHER, "ntfs_statfs\n");
+ vol=NTFS_SB2VOL(sb);
+ memset(&fs,0,sizeof(fs));
+ fs.f_type=NTFS_SUPER_MAGIC;
+ fs.f_bsize=vol->clustersize;
+
+ fs.f_blocks=ntfs_get_volumesize(NTFS_SB2VOL(sb));
+ fs.f_bfree=ntfs_get_free_cluster_count(vol->bitmap);
+ fs.f_bavail=fs.f_bfree;
+
+ /* Number of files is limited by free space only, so we lie here */
+ fs.f_ffree=0;
+ mft=iget(sb,FILE_MFT);
+ fs.f_files=mft->i_size/vol->mft_recordsize;
+ iput(mft);
+
+ /* should be read from volume */
+ fs.f_namelen=255;
+ copy_to_user(sf,&fs,bufsize);
+ 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 */
+struct super_operations ntfs_super_operations = {
+ ntfs_read_inode,
+ NULL, /* write_inode */
+ ntfs_put_inode,
+ NULL, /* delete_inode */
+ NULL, /* notify_change */
+ ntfs_put_super,
+ NULL, /* write_super */
+ ntfs_statfs,
+ ntfs_remount_fs, /* remount */
+ _ntfs_clear_inode, /* clear_inode */
+};
+
+/* 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;
+
+ /* When the driver is compiled as a module, kerneld must know when it
+ * can safely remove it from memory. To do this, each module owns a
+ * reference counter.
+ */
+ MOD_INC_USE_COUNT;
+ /* Don't put ntfs_debug() before MOD_INC_USE_COUNT, printk() can block
+ * so this could lead to a race condition with kerneld.
+ */
+ ntfs_debug(DEBUG_OTHER, "ntfs_read_super\n");
+
+#ifdef NTFS_IN_LINUX_KERNEL
+ vol = NTFS_SB2VOL(sb);
+#else
+ if(!(vol = ntfs_malloc(sizeof(ntfs_volume))))
+ goto ntfs_read_super_dec;
+ NTFS_SB2VOL(sb)=vol;
+#endif
+
+ if(!parse_options(vol,(char*)options))
+ goto ntfs_read_super_vol;
+
+ /* Ensure that the super block won't be used until it is completed */
+ lock_super(sb);
+ ntfs_debug(DEBUG_OTHER, "lock_super\n");
+#if 0
+ /* Set to read only, user option might reset it */
+ sb->s_flags |= MS_RDONLY;
+#endif
+
+ /* Assume a 512 bytes block device for now */
+ set_blocksize(sb->s_dev, 512);
+ /* Read the super block (boot block) */
+ if(!(bh=bread(sb->s_dev,0,512))) {
+ ntfs_error("Reading super block failed\n");
+ goto ntfs_read_super_unl;
+ }
+ ntfs_debug(DEBUG_OTHER, "Done reading boot block\n");
+
+ /* Check for 'NTFS' magic number */
+ if(!IS_NTFS_VOLUME(bh->b_data)){
+ ntfs_debug(DEBUG_OTHER, "Not a NTFS volume\n");
+ brelse(bh);
+ goto ntfs_read_super_unl;
+ }
+
+ ntfs_debug(DEBUG_OTHER, "Going to init volume\n");
+ ntfs_init_volume(vol,bh->b_data);
+ ntfs_debug(DEBUG_OTHER, "MFT record at cluster 0x%X\n",vol->mft_cluster);
+ brelse(bh);
+ NTFS_SB(vol)=sb;
+ ntfs_debug(DEBUG_OTHER, "Done to init volume\n");
+
+ /* Inform the kernel that a device block is a NTFS cluster */
+ sb->s_blocksize=vol->clustersize;
+ for(i=sb->s_blocksize,sb->s_blocksize_bits=0;i;i>>=1)
+ sb->s_blocksize_bits++;
+ set_blocksize(sb->s_dev,sb->s_blocksize);
+ ntfs_debug(DEBUG_OTHER, "set_blocksize\n");
+
+ /* Allocate a MFT record (MFT record can be smaller than a cluster) */
+ if(!(vol->mft=ntfs_malloc(max(vol->mft_recordsize,vol->clustersize))))
+ goto ntfs_read_super_unl;
+
+ /* Read at least the MFT record for $MFT */
+ for(i=0;i<max(vol->mft_clusters_per_record,1);i++){
+ if(!(bh=bread(sb->s_dev,vol->mft_cluster+i,vol->clustersize))) {
+ ntfs_error("Could not read MFT record 0\n");
+ goto ntfs_read_super_mft;
+ }
+ ntfs_memcpy(vol->mft+i*vol->clustersize,bh->b_data,vol->clustersize);
+ brelse(bh);
+ ntfs_debug(DEBUG_OTHER, "Read cluster %x\n",vol->mft_cluster+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;
+
+ 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),NULL))){
+ ntfs_error("Could not get root dir inode\n");
+ goto ntfs_read_super_mft;
+ }
+ unlock_super(sb);
+ ntfs_debug(DEBUG_OTHER, "unlock_super\n");
+ ntfs_debug(DEBUG_OTHER, "read_super: done\n");
+ return sb;
+
+ntfs_read_super_mft:
+ ntfs_free(vol->mft);
+ntfs_read_super_unl:
+ sb->s_dev = 0;
+ unlock_super(sb);
+ ntfs_debug(DEBUG_OTHER, "unlock_super\n");
+ntfs_read_super_vol:
+ #ifndef NTFS_IN_LINUX_KERNEL
+ ntfs_free(vol);
+ntfs_read_super_dec:
+ #endif
+ ntfs_debug(DEBUG_OTHER, "read_super: done\n");
+ MOD_DEC_USE_COUNT;
+ return NULL;
+}
+
+/* Define the filesystem
+ *
+ * Define SECOND if you cannot unload ntfs, and want to avoid rebooting
+ * for just one more test
+ */
+struct file_system_type ntfs_fs_type = {
+/* Filesystem name, as used after mount -t */
+#ifndef SECOND
+ "ntfs",
+#else
+ "ntfs2",
+#endif
+/* This filesystem requires a device (a hard disk)
+ * May want to add FS_IBASKET when it works
+ */
+ FS_REQUIRES_DEV,
+/* Entry point of the filesystem */
+ ntfs_read_super,
+/* Will point to the next filesystem in the kernel table */
+ NULL
+};
+
+/* When this code is not compiled as a module, this is the main entry point,
+ * called by do_sys_setup() in fs/filesystems.c
+ *
+ * NOTE : __initfunc() is a macro used to remove this function from memory
+ * once initialization is done
+ */
+__initfunc(int init_ntfs_fs(void))
+{
+ /* Comment this if you trust klogd. There are reasons not to trust it
+ */
+#if defined(DEBUG) && !defined(MODULE)
+ extern int console_loglevel;
+ console_loglevel=15;
+#endif
+ printk(KERN_NOTICE "NTFS version " NTFS_VERSION "\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);
+}
+
+#ifdef MODULE
+/* A module is a piece of code which can be inserted in and removed
+ * from the running kernel whenever you want using lsmod, or on demand using
+ * kerneld
+ */
+
+/* No function of this module is needed by another module */
+EXPORT_NO_SYMBOLS;
+/* Only used for documentation purposes at the moment,
+ * see include/linux/module.h
+ */
+MODULE_AUTHOR("Martin von Löwis");
+MODULE_DESCRIPTION("NTFS driver");
+/* no MODULE_SUPPORTED_DEVICE() */
+/* Load-time parameter */
+MODULE_PARM(ntdebug, "i");
+MODULE_PARM_DESC(ntdebug, "Debug level");
+
+/* When this code is compiled as a module, if you use mount -t ntfs when no
+ * ntfs filesystem is registered (see /proc/filesystems), get_fs_type() in
+ * fs/super.c asks kerneld to load the module named ntfs in memory.
+ *
+ * Therefore, this function is the main entry point in this case
+ */
+int init_module(void)
+{
+ return init_ntfs_fs();
+}
+
+/* Called by kerneld just before the kernel removes the module from memory */
+void cleanup_module(void)
+{
+ SYSCTL(0);
+ ntfs_debug(DEBUG_OTHER, "unregistering %s\n",ntfs_fs_type.name);
+ unregister_filesystem(&ntfs_fs_type);
+}
+#endif
+
+/*
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */