/** * ntfs_file_fsync - sync a file to disk * @filp: file to be synced * @dentry: dentry describing the file to sync * @datasync: if non-zero only flush user data and not metadata * * Data integrity sync of a file to disk. Used for fsync, fdatasync, and msync * system calls. This function is inspired by fs/buffer.c::file_fsync(). * * If @datasync is false, write the mft record and all associated extent mft * records as well as the $DATA attribute and then sync the block device. * * If @datasync is true and the attribute is non-resident, we skip the writing * of the mft record and all associated extent mft records (this might still * happen due to the write_inode_now() call). * * Also, if @datasync is true, we do not wait on the inode to be written out * but we always wait on the page cache pages to be written out. * * Note: In the past @filp could be NULL so we ignore it as we don't need it * anyway. * * Locking: Caller must hold i_sem on the inode. * * TODO: We should probably also write all attribute/index inodes associated * with this inode but since we have no simple way of getting to them we ignore * this problem for now. */ static int ntfs_file_fsync(struct file *filp, struct dentry *dentry, int datasync) { struct inode *vi = dentry->d_inode; int err, ret = 0; ntfs_debug("Entering for inode 0x%lx.", vi->i_ino); BUG_ON(S_ISDIR(vi->i_mode)); if (!datasync || !NInoNonResident(NTFS_I(vi))) ret = ntfs_write_inode(vi, 1); write_inode_now(vi, !datasync); /* * NOTE: If we were to use mapping->private_list (see ext2 and * fs/buffer.c) for dirty blocks then we could optimize the below to be * sync_mapping_buffers(vi->i_mapping). */ err = sync_blockdev(vi->i_sb->s_bdev); if (unlikely(err && !ret)) ret = err; if (likely(!ret)) ntfs_debug("Done."); else ntfs_warning(vi->i_sb, "Failed to f%ssync inode 0x%lx. Error " "%u.", datasync ? "data" : "", vi->i_ino, -ret); return ret; }
/** * ntfs_get_dentry - find a dentry for the inode from a file handle sub-fragment * @sb: super block identifying the mounted ntfs volume * @fh: the file handle sub-fragment * * Find a dentry for the inode given a file handle sub-fragment. This function * is called from fs/exportfs/expfs.c::find_exported_dentry() which in turn is * called from the default ->decode_fh() which is export_decode_fh() in the * same file. The code is closely based on the default ->get_dentry() helper * fs/exportfs/expfs.c::get_object(). * * The @fh contains two 32-bit unsigned values, the first one is the inode * number and the second one is the inode generation. * * Return the dentry on success or the error code on error (IS_ERR() is true). */ static struct dentry *ntfs_get_dentry(struct super_block *sb, void *fh) { struct inode *vi; struct dentry *dent; unsigned long ino = ((u32 *)fh)[0]; u32 gen = ((u32 *)fh)[1]; ntfs_debug("Entering for inode 0x%lx, generation 0x%x.", ino, gen); vi = ntfs_iget(sb, ino); if (IS_ERR(vi)) { ntfs_error(sb, "Failed to get inode 0x%lx.", ino); return (struct dentry *)vi; } if (unlikely(is_bad_inode(vi) || vi->i_generation != gen)) { /* We didn't find the right inode. */ ntfs_error(sb, "Inode 0x%lx, bad count: %d %d or version 0x%x " "0x%x.", vi->i_ino, vi->i_nlink, atomic_read(&vi->i_count), vi->i_generation, gen); iput(vi); return ERR_PTR(-ESTALE); } /* Now find a dentry. If possible, get a well-connected one. */ dent = d_alloc_anon(vi); if (unlikely(!dent)) { iput(vi); return ERR_PTR(-ENOMEM); } ntfs_debug("Done for inode 0x%lx, generation 0x%x.", ino, gen); return dent; }
/* 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; }
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"); }
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 = get_empty_inode(); if (!r) goto out; vol = NTFS_INO2VOL(dir); #ifdef NTFS_IN_LINUX_KERNEL ino = NTFS_LINO2NINO(r); #else ino = ntfs_malloc(sizeof(ntfs_inode)); error = ENOMEM; if(!ino) goto out; r->u.generic_ip = ino; #endif error = ntfs_mkdir(NTFS_LINO2NINO(dir), d->d_name.name, d->d_name.len, ino); if(error) goto out; r->i_uid = vol->uid; r->i_gid = vol->gid; r->i_nlink = 1; r->i_sb = dir->i_sb; 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_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; }
/** * ntfs_usnjrnl_stamp - stamp the transaction log ($UsnJrnl) on an ntfs volume * @vol: ntfs volume on which to stamp the transaction log * * Stamp the transaction log ($UsnJrnl) on the ntfs volume @vol and return 0 * on success and errno on error. * * This function assumes that the transaction log has already been loaded and * consistency checked by a call to ntfs_vfsops.c::ntfs_usnjrnl_load(). */ errno_t ntfs_usnjrnl_stamp(ntfs_volume *vol) { ntfs_debug("Entering."); if (!NVolUsnJrnlStamped(vol)) { sle64 j_size, stamp; upl_t upl; upl_page_info_array_t pl; USN_HEADER *uh; ntfs_inode *max_ni; errno_t err; mtx_lock_spin(&vol->usnjrnl_j_ni->size_lock); j_size = vol->usnjrnl_j_ni->data_size; mtx_unlock_spin(&vol->usnjrnl_j_ni->size_lock); max_ni = vol->usnjrnl_max_ni; /* * FIXME: Next If statement always false because of * replacing vnode_get() with vhold() */ vhold(max_ni->vn); if (0) { ntfs_error(vol->mp, "Failed to get vnode for " "$UsnJrnl/$DATA/$Max."); return err; } sx_slock(&max_ni->lock); err = ntfs_page_map(max_ni, 0, &upl, &pl, (u8**)&uh, TRUE); if (err) { ntfs_error(vol->mp, "Failed to read from " "$UsnJrnl/$DATA/$Max attribute."); vdrop(max_ni->vn); return err; } stamp = ntfs_current_time(); ntfs_debug("Stamping transaction log ($UsnJrnl): old " "journal_id 0x%llx, old lowest_valid_usn " "0x%llx, new journal_id 0x%llx, new " "lowest_valid_usn 0x%llx.", (unsigned long long) sle64_to_cpu(uh->journal_id), (unsigned long long) sle64_to_cpu(uh->lowest_valid_usn), (unsigned long long)sle64_to_cpu(stamp), (unsigned long long)j_size); uh->lowest_valid_usn = cpu_to_sle64(j_size); uh->journal_id = stamp; ntfs_page_unmap(max_ni, upl, pl, TRUE); sx_sunlock(&max_ni->lock); vdrop(max_ni->vn); /* Set the flag so we do not have to do it again on remount. */ NVolSetUsnJrnlStamped(vol); // TODO: Should we mark any times on the base inode $UsnJrnl // for update here? } ntfs_debug("Done."); return 0; }
/* 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"); }
/** * ntfs_inode_hash_lookup - find and return a loaded ntfs inode * * Search the ntfs inode hash for the ntfs inode matching @na and if present * return it. If not present return NULL. * * If the found ntfs inode has a vnode attached, then get an iocount reference * on the vnode. */ ntfs_inode *ntfs_inode_hash_lookup(ntfs_volume *vol, const ntfs_attr *na) { ntfs_inode_list_head *list; ntfs_inode *ni; ntfs_debug("Entering for mft_no 0x%llx, type 0x%x, name_len 0x%x.", (unsigned long long)na->mft_no, (unsigned)le32_to_cpu(na->type), na->name_len); list = ntfs_inode_hash_list(vol, na->mft_no); ni = ntfs_inode_hash_list_find(vol, list, na); ntfs_debug("Done (ntfs_inode %sfound in cache).", ni ? "" : "not "); return ni; }
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; }
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; }
/** * ntfs_page_unmap - unmap a page belonging to a vnode from memory * @ni: ntfs inode to which the page belongs * @upl: page list of the page * @pl: array of pages containing the page itself * @mark_dirty: mark the page dirty * * Unmap the page belonging to the ntfs inode @ni from memory releasing it back * to the vm. * * The page is described by the page list @upl, the array of pages containing * the page @pl and the address of the mapped page contents @kaddr. * * If @mark_dirty is TRUE, tell the vm to mark the page dirty when releasing * the page. * * Locking: Caller must hold an iocount reference on the vnode of @ni. */ void ntfs_page_unmap(ntfs_inode *ni, upl_t upl, upl_page_info_array_t pl, const BOOL mark_dirty) { kern_return_t kerr; BOOL was_valid, was_dirty; was_valid = upl_valid_page(pl, 0); /* The page dirty bit is only valid if the page was valid. */ was_dirty = (was_valid && upl_dirty_page(pl, 0)); ntfs_debug("Entering for inode 0x%llx, page was %svalid %s %sdirty%s.", (unsigned long long)ni->mft_no, was_valid ? "" : "not ", (int)was_valid ^ (int)was_dirty ? "but" : "and", was_dirty ? "" : "not ", mark_dirty ? ", marking it dirty" : ""); /* Unmap the page from the kernel's address space. */ kerr = ubc_upl_unmap(upl); if (kerr != KERN_SUCCESS) ntfs_warning(ni->vol->mp, "ubc_upl_unmap() failed (error %d).", (int)kerr); /* * If the page was valid and dirty or is being made dirty or if caching * for the vnode is enabled (as it will usually be the case for all * metadata files), commit it thus releasing it into the vm taking care * to preserve the dirty state and marking the page dirty if requested * when committing the page. * * If the page was not valid or was valid but not dirty, it is not * being marked dirty, and caching is disabled on the vnode, dump the * page. */ if (was_dirty || mark_dirty || !vnode_isnocache(ni->vn)) { int commit_flags; commit_flags = UPL_COMMIT_FREE_ON_EMPTY | UPL_COMMIT_INACTIVATE; if (!was_valid && !mark_dirty) commit_flags |= UPL_COMMIT_CLEAR_DIRTY; else if (was_dirty || mark_dirty) commit_flags |= UPL_COMMIT_SET_DIRTY; ubc_upl_commit_range(upl, 0, PAGE_SIZE, commit_flags); ntfs_debug("Done (committed page)."); } else { ubc_upl_abort_range(upl, 0, PAGE_SIZE, UPL_ABORT_DUMP_PAGES | UPL_ABORT_FREE_ON_EMPTY); ntfs_debug("Done (dumped page)."); } }
static struct dentry *ntfs_lookup(struct inode *dir, struct dentry *d) { struct inode *res=0; char *item=0; ntfs_iterate_s walk; int error; ntfs_debug(DEBUG_NAME1, "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 ERR_PTR(-error); item=ntfs_malloc(ITEM_SIZE); if( !item ) return ERR_PTR(-ENOMEM); /* 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; }
/* 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; int error; 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; error = ntfs_get_volumesize( NTFS_SB2VOL( sb ), &fs.f_blocks ); if( error ) return -error; 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; }
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 && !io.size)return -error; *off+=io.size; return io.size; }
/* 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; ntfs_u64 size; int error; ntfs_debug(DEBUG_OTHER, "ntfs_statfs\n"); vol=NTFS_SB2VOL(sb); sf->f_type=NTFS_SUPER_MAGIC; sf->f_bsize=vol->clustersize; error = ntfs_get_volumesize( NTFS_SB2VOL( sb ), &size ); if( error ) return -error; sf->f_blocks = size; /* volumesize is in clusters */ sf->f_bfree=ntfs_get_free_cluster_count(vol->bitmap); sf->f_bavail=sf->f_bfree; mft=iget(sb,FILE_MFT); if (!mft) return -EIO; /* So ... we lie... thus this following cast of loff_t value is ok here.. */ sf->f_files = (unsigned long)mft->i_size / vol->mft_recordsize; iput(mft); /* should be read from volume */ sf->f_namelen=255; return 0; }
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(); }
/* 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); #ifndef NTFS_IN_LINUX_KERNEL ntfs_free(vol); #endif ntfs_debug(DEBUG_OTHER, "ntfs_put_super: done\n"); MOD_DEC_USE_COUNT; }
/* 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); ntfs_debug(DEBUG_OTHER, "readdir: Looking for file %x dircount %d\n", (unsigned)filp->f_pos,atomic_read(&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,DT_DIR); filp->f_pos=0xFFFF0001; return 0; /* FIXME: parent directory */ case 1: filldir(dirent,"..",2,filp->f_pos,0,DT_DIR); 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; }
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; }
static int ntfs_collate_binary(ntfs_volume *vol, const void *data1, const int data1_len, const void *data2, const int data2_len) { int rc; ntfs_debug("Entering."); rc = memcmp(data1, data2, min(data1_len, data2_len)); if (!rc && (data1_len != data2_len)) { if (data1_len < data2_len) rc = -1; else rc = 1; } ntfs_debug("Done, returning %i", rc); return rc; }
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; }
/** * ntfs_page_dump - discard a page belonging to a vnode from memory * @ni: ntfs inode to which the page belongs * @upl: page list of the page * @pl: array of pages containing the page itself * * Unmap the page belonging to the ntfs inode @ni from memory throwing it away. * Note that if the page is dirty all changes to the page will be lost as it * will be discarded so use this function with extreme caution. * * The page is described by the page list @upl, the array of pages containing * the page @pl and the address of the mapped page contents @kaddr. * * Locking: Caller must hold an iocount reference on the vnode of @ni. */ void ntfs_page_dump(ntfs_inode *ni, upl_t upl, upl_page_info_array_t pl __unused) { kern_return_t kerr; ntfs_debug("Entering for inode 0x%llx, page is %svalid, %sdirty.", (unsigned long long)ni->mft_no, upl_valid_page(pl, 0) ? "" : "not ", upl_dirty_page(pl, 0) ? "" : "not "); /* Unmap the page from the kernel's address space. */ kerr = ubc_upl_unmap(upl); if (kerr != KERN_SUCCESS) ntfs_warning(ni->vol->mp, "ubc_upl_unmap() failed (error %d).", (int)kerr); /* Dump the page. */ ubc_upl_abort_range(upl, 0, PAGE_SIZE, UPL_ABORT_DUMP_PAGES | UPL_ABORT_FREE_ON_EMPTY); ntfs_debug("Done."); }
/** * ntfs_file_fsync - sync a file to disk * @filp: file to be synced * @dentry: dentry describing the file to sync * @datasync: if non-zero only flush user data and not metadata * * Data integrity sync of a file to disk. Used for fsync, fdatasync, and msync * system calls. This function is inspired by fs/buffer.c::file_fsync(). * * If @datasync is false, write the mft record and all associated extent mft * records as well as the $DATA attribute and then sync the block device. * * If @datasync is true and the attribute is non-resident, we skip the writing * of the mft record and all associated extent mft records (this might still * happen due to the write_inode_now() call). * * Also, if @datasync is true, we do not wait on the inode to be written out * but we always wait on the page cache pages to be written out. * * Note: In the past @filp could be NULL so we ignore it as we don't need it * anyway. * * Locking: Caller must hold i_sem on the inode. * * TODO: We should probably also write all attribute/index inodes associated * with this inode but since we have no simple way of getting to them we ignore * this problem for now. */ static int ntfs_file_fsync(struct file *filp, struct dentry *dentry, int datasync) { struct inode *vi = dentry->d_inode; int err, ret = 0; ntfs_debug("Entering for inode 0x%lx.", vi->i_ino); BUG_ON(S_ISDIR(vi->i_mode)); if (!datasync || !NInoNonResident(NTFS_I(vi))) ret = ntfs_write_inode(vi, 1); write_inode_now(vi, !datasync); err = sync_blockdev(vi->i_sb->s_bdev); if (unlikely(err && !ret)) ret = err; if (likely(!ret)) ntfs_debug("Done."); else ntfs_warning(vi->i_sb, "Failed to f%ssync inode 0x%lx. Error " "%u.", datasync ? "data" : "", vi->i_ino, -ret); return ret; }
static ssize_t ntfs_write(struct file *filp,const char* buf,size_t count,loff_t *pos) { int ret; ntfs_io io; struct inode *inode = filp->f_dentry->d_inode; ntfs_inode *ino = NTFS_LINO2NINO(inode); struct ntfs_getuser_update_vm_s param; if (!ino) return -EINVAL; ntfs_debug (DEBUG_LINUX, "ntfs_write %x,%x,%x ->\n", (unsigned)ino->i_number, (unsigned)*pos, (unsigned)count); /* Allows to lock fs ro at any time */ if (inode->i_sb->s_flags & MS_RDONLY) return -ENOSPC; if (!ntfs_find_attr(ino,ino->vol->at_data,NULL)) return -EINVAL; /* Evaluating O_APPEND is the file system's job... */ if (filp->f_flags & O_APPEND) *pos = inode->i_size; param.user = buf; param.ino = inode; param.off = *pos; io.fn_put = 0; io.fn_get = ntfs_getuser_update_vm; io.param = ¶m; io.size = count; ret = ntfs_write_attr (ino, ino->vol->at_data, NULL, *pos, &io); ntfs_debug (DEBUG_LINUX, "write -> %x\n", ret); if(ret<0) return -EINVAL; *pos += io.size; if (*pos > inode->i_size) inode->i_size = *pos; mark_inode_dirty (filp->f_dentry->d_inode); return io.size; }
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 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); }
static struct dentry *ntfs_get_parent(struct dentry *child_dent) { struct inode *vi = child_dent->d_inode; ntfs_inode *ni = NTFS_I(vi); MFT_RECORD *mrec; ntfs_attr_search_ctx *ctx; ATTR_RECORD *attr; FILE_NAME_ATTR *fn; unsigned long parent_ino; int err; ntfs_debug("Entering for inode 0x%lx.", vi->i_ino); mrec = map_mft_record(ni); if (IS_ERR(mrec)) return (struct dentry *)mrec; ctx = ntfs_attr_get_search_ctx(ni, mrec); if (unlikely(!ctx)) { unmap_mft_record(ni); return ERR_PTR(-ENOMEM); } try_next: err = ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, CASE_SENSITIVE, 0, NULL, 0, ctx); if (unlikely(err)) { ntfs_attr_put_search_ctx(ctx); unmap_mft_record(ni); if (err == -ENOENT) ntfs_error(vi->i_sb, "Inode 0x%lx does not have a " "file name attribute. Run chkdsk.", vi->i_ino); return ERR_PTR(err); } attr = ctx->attr; if (unlikely(attr->non_resident)) goto try_next; fn = (FILE_NAME_ATTR *)((u8 *)attr + le16_to_cpu(attr->data.resident.value_offset)); if (unlikely((u8 *)fn + le32_to_cpu(attr->data.resident.value_length) > (u8*)attr + le32_to_cpu(attr->length))) goto try_next; parent_ino = MREF_LE(fn->parent_directory); ntfs_attr_put_search_ctx(ctx); unmap_mft_record(ni); return d_obtain_alias(ntfs_iget(vi->i_sb, parent_ino)); }
/** * ntfs_inode_hash_init - initialize the ntfs inode hash * * Initialize the ntfs inode hash. */ errno_t ntfs_inode_hash_init(void) { /* Create the ntfs inode hash. */ ntfs_inode_hash_table = hashinit(desiredvnodes, M_TEMP, &ntfs_inode_hash_mask); if (!ntfs_inode_hash_table) { ntfs_error(NULL, "Failed to allocate ntfs inode hash table."); return ENOMEM; } ntfs_debug("ntfs_inode_hash_mask 0x%lx.", ntfs_inode_hash_mask); /* Initialize the ntfs inode hash lock. */ mtx_init(&ntfs_inode_hash_lock, "ntfs inode hash lock", NULL, MTX_DEF); return 0; }
/* 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; }
static int ntfs_collate_ntofs_ulong(ntfs_volume *vol, const void *data1, const int data1_len, const void *data2, const int data2_len) { int rc; u32 d1, d2; ntfs_debug("Entering."); // FIXME: We don't really want to bug here. BUG_ON(data1_len != data2_len); BUG_ON(data1_len != 4); d1 = le32_to_cpup(data1); d2 = le32_to_cpup(data2); if (d1 < d2) rc = -1; else { if (d1 == d2) rc = 0; else rc = 1; } ntfs_debug("Done, returning %i", rc); return rc; }
/** * ntfs_stamp_usnjrnl - stamp the transaction log ($UsnJrnl) on an ntfs volume * @vol: ntfs volume on which to stamp the transaction log * * Stamp the transaction log ($UsnJrnl) on the ntfs volume @vol and return * 'true' on success and 'false' on error. * * This function assumes that the transaction log has already been loaded and * consistency checked by a call to fs/ntfs/super.c::load_and_init_usnjrnl(). */ bool ntfs_stamp_usnjrnl(ntfs_volume *vol) { ntfs_debug("Entering."); if (likely(!NVolUsnJrnlStamped(vol))) { sle64 stamp; struct page *page; USN_HEADER *uh; page = ntfs_map_page(vol->usnjrnl_max_ino->i_mapping, 0); if (IS_ERR(page)) { ntfs_error(vol->sb, "Failed to read from " "$UsnJrnl/$DATA/$Max attribute."); return false; } uh = (USN_HEADER*)page_address(page); stamp = get_current_ntfs_time(); ntfs_debug("Stamping transaction log ($UsnJrnl): old " "journal_id 0x%llx, old lowest_valid_usn " "0x%llx, new journal_id 0x%llx, new " "lowest_valid_usn 0x%llx.", (long long)sle64_to_cpu(uh->journal_id), (long long)sle64_to_cpu(uh->lowest_valid_usn), (long long)sle64_to_cpu(stamp), i_size_read(vol->usnjrnl_j_ino)); uh->lowest_valid_usn = cpu_to_sle64(i_size_read(vol->usnjrnl_j_ino)); uh->journal_id = stamp; flush_dcache_page(page); set_page_dirty(page); ntfs_unmap_page(page); /* Set the flag so we do not have to do it again on remount. */ NVolSetUsnJrnlStamped(vol); } ntfs_debug("Done."); return true; }