[PATCH] add simple create and unlink(aka rm)

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

[PATCH] add simple create and unlink(aka rm)

高志刚
From: Zhigang Gao <gzg1984@icloud .com>

---
 fs/ntfs/Makefile |   25 +-
 fs/ntfs/aops.h   |    2 +
 fs/ntfs/attrib.c |   26 +-
 fs/ntfs/dir.c    |  602 ++++++++++++++++++++++++
 fs/ntfs/file.c   |    1 +
 fs/ntfs/index.h  |    1 +
 fs/ntfs/mft.c    |   80 ++++
 fs/ntfs/mft.h    |    1 +
 fs/ntfs/namei.c  | 1364 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 9 files changed, 2097 insertions(+), 5 deletions(-)

diff --git a/fs/ntfs/Makefile b/fs/ntfs/Makefile
index 2ff263e..3edb4e2 100644
--- a/fs/ntfs/Makefile
+++ b/fs/ntfs/Makefile
@@ -1,14 +1,33 @@
 # Rules for making the NTFS driver.
-
+ifneq ($(KERNELRELEASE),)
 obj-$(CONFIG_NTFS_FS) += ntfs.o
 
 ntfs-y := aops.o attrib.o collate.o compress.o debug.o dir.o file.o \
-  index.o inode.o mft.o mst.o namei.o runlist.o super.o sysctl.o \
-  unistr.o upcase.o
+  index.o inode.o mft.o mst.o namei.o runlist.o super.o sysctl.o \
+  unistr.o upcase.o
 
 ntfs-$(CONFIG_NTFS_RW) += bitmap.o lcnalloc.o logfile.o quota.o usnjrnl.o
 
 ccflags-y := -DNTFS_VERSION=\"2.1.32\"
 ccflags-$(CONFIG_NTFS_DEBUG) += -DDEBUG
 ccflags-$(CONFIG_NTFS_RW) += -DNTFS_RW
+else
+
+KERNEL ?= /lib/modules/`uname -r`/build
+
+default:
+ CONFIG_NTFS_FS=m CONFIG_NTFS_RW=y CONFIG_NTFS_DEBUG=y $(MAKE) -C $(KERNEL) M=$$PWD
+
+
+.PHONY : install help clean
+help:
+ $(MAKE) -C $(KERNEL) M=$$PWD help
+
+install : default
+ $(MAKE) -C $(KERNEL) M=$$PWD modules_install
+ depmod -A
+
+clean:
+ make -C $(KERNEL) M=`pwd` clean
 
+endif
diff --git a/fs/ntfs/aops.h b/fs/ntfs/aops.h
index 820d6ea..4805986 100644
--- a/fs/ntfs/aops.h
+++ b/fs/ntfs/aops.h
@@ -39,8 +39,10 @@
  */
 static inline void ntfs_unmap_page(struct page *page)
 {
+ ntfs_debug("Entering .");
  kunmap(page);
  put_page(page);
+ ntfs_debug("done .");
 }
 
 /**
diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c
index 44a39a0..0092d14 100644
--- a/fs/ntfs/attrib.c
+++ b/fs/ntfs/attrib.c
@@ -599,20 +599,41 @@ static int ntfs_attr_find(const ATTR_TYPE type, const ntfschar *name,
  * Iterate over attributes in mft record starting at @ctx->attr, or the
  * attribute following that, if @ctx->is_first is 'true'.
  */
+ ntfs_debug("Entering. type=[0x%x] is_first[%d]", type,ctx->is_first);
  if (ctx->is_first) {
  a = ctx->attr;
  ctx->is_first = false;
- } else
+ }
+ else
+ {
  a = (ATTR_RECORD*)((u8*)ctx->attr +
  le32_to_cpu(ctx->attr->length));
+ }
+
  for (;; a = (ATTR_RECORD*)((u8*)a + le32_to_cpu(a->length))) {
  if ((u8*)a < (u8*)ctx->mrec || (u8*)a > (u8*)ctx->mrec +
  le32_to_cpu(ctx->mrec->bytes_allocated))
+ {
+ ntfs_error(vol->sb, "IN LOOP breaker."
+ "type=[0x%x],a->type[0x%x],a->length[%d],"
+ "a[%p],ctx->mrec[%p],ctx->mrec->bytes_allocated[%d]"
+ ,type,a->type,a->length,
+ a,ctx->mrec,le32_to_cpu(ctx->mrec->bytes_allocated));
  break;
+ }
  ctx->attr = a;
+ if (type != AT_UNUSED)
+ {
  if (unlikely(le32_to_cpu(a->type) > le32_to_cpu(type) ||
  a->type == AT_END))
  return -ENOENT;
+ }
+ else
+ {
+ if ( a->type == AT_END)
+ return -ENOENT;
+ }
+
  if (unlikely(!a->length))
  break;
  if (a->type != type)
@@ -1434,6 +1455,8 @@ int ntfs_attr_can_be_resident(const ntfs_volume *vol, const ATTR_TYPE type)
  */
 int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size)
 {
+ BUG_ON(!m);
+ BUG_ON(!a);
  ntfs_debug("Entering for new_size %u.", new_size);
  /* Align to 8 bytes if it is not already done. */
  if (new_size & 7)
@@ -1455,6 +1478,7 @@ int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size)
  if (new_size >= offsetof(ATTR_REC, length) + sizeof(a->length))
  a->length = cpu_to_le32(new_size);
  }
+ ntfs_debug("Done.");
  return 0;
 }
 
diff --git a/fs/ntfs/dir.c b/fs/ntfs/dir.c
index 0ee19ec..caf57c15 100644
--- a/fs/ntfs/dir.c
+++ b/fs/ntfs/dir.c
@@ -77,6 +77,608 @@ ntfschar I30[5] = { cpu_to_le16('$'), cpu_to_le16('I'),
  *       removes them again after the write is complete after which it
  *       unlocks the page.
  */
+#include "index.h"
+#include "layout.h"
+#include "types.h"
+
+/* Author: Gzged
+ * Caller is in namei.c
+ */
+int ntfs_lookup_inode_by_key (const void *key, const int key_len, ntfs_index_context *ictx)
+{
+ ntfs_inode* dir_ni = ictx->idx_ni ;
+ const ntfschar* uname = ((FILE_NAME_ATTR *)key)->file_name ;
+ const int uname_len = ((FILE_NAME_ATTR *)key)->file_name_length;
+
+ ntfs_volume *vol = dir_ni->vol;
+ struct super_block *sb = vol->sb;
+ MFT_RECORD *m;
+ INDEX_ROOT *ir;
+ INDEX_ENTRY *ie;
+ INDEX_ALLOCATION *ia;
+ u8 *index_end;
+ /*
+ u64 mref;
+ */
+ ntfs_attr_search_ctx *ctx;
+ int err, rc;
+ VCN vcn, old_vcn;
+ struct address_space *ia_mapping;
+ struct page *page;
+ u8 *kaddr;
+
+ ntfs_debug("Entering.");
+ BUG_ON(!S_ISDIR(VFS_I(dir_ni)->i_mode));
+ BUG_ON(NInoAttr(dir_ni));
+ /* Get hold of the mft record for the directory. */
+ m = map_mft_record(dir_ni);
+ if (IS_ERR(m))
+ {
+ ntfs_error(sb, "map_mft_record() failed with error code %ld.",
+ -PTR_ERR(m));
+ return ERR_MREF(PTR_ERR(m));
+ }
+ ctx = ntfs_attr_get_search_ctx(dir_ni, m);
+ if (unlikely(!ctx)) {
+ err = -ENOMEM;
+ goto err_out;
+ }
+ /* Find the index root attribute in the mft record. */
+ err = ntfs_attr_lookup(AT_INDEX_ROOT, I30, 4, CASE_SENSITIVE, 0, NULL,
+ 0, ctx);
+ if (unlikely(err)) {
+ if (err == -ENOENT) {
+ ntfs_error(sb, "Index root attribute missing in "
+ "directory inode 0x%lx.",
+ dir_ni->mft_no);
+ err = -EIO;
+ }
+ goto err_out;
+ }
+ /* Get to the index root value (it's been verified in read_inode). */
+ ir = (INDEX_ROOT*)((u8*)ctx->attr +
+ le16_to_cpu(ctx->attr->data.resident.value_offset));
+ index_end = (u8*)&ir->index + le32_to_cpu(ir->index.index_length);
+ /* The first index entry. */
+ ie = (INDEX_ENTRY*)((u8*)&ir->index +
+ le32_to_cpu(ir->index.entries_offset));
+ /*
+ * Loop until we exceed valid memory (corruption case) or until we
+ * reach the last entry.
+ */
+ for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) {
+ /* Bounds checks. */
+ ictx->is_in_root = true;
+ ictx->ir = ir;
+ ictx->entry = ie;
+ ictx->base_ni = dir_ni;
+ ictx->actx = ctx;
+ ictx->ia = NULL;
+ ictx->page = NULL;
+
+ if ((u8*)ie < (u8*)ctx->mrec || (u8*)ie +
+ sizeof(INDEX_ENTRY_HEADER) > index_end ||
+ (u8*)ie + le16_to_cpu(ie->key_length) >
+ index_end)
+ goto dir_err_out;
+ /*
+ * The last entry cannot contain a name. It can however contain
+ * a pointer to a child node in the B+tree so we just break out.
+ */
+ if (ie->flags & INDEX_ENTRY_END)
+ break;
+ /*
+ * We perform a case sensitive comparison and if that matches
+ * we are done and return the mft reference of the inode (i.e.
+ * the inode number together with the sequence number for
+ * consistency checking). We convert it to cpu format before
+ * returning.
+ */
+
+ if (ntfs_are_names_equal(uname, uname_len,
+ (ntfschar*)&ie->key.file_name.file_name,
+ ie->key.file_name.file_name_length,
+ CASE_SENSITIVE, vol->upcase, vol->upcase_len)) {
+found_it:
+ /*
+ * We have a perfect match, so we don't need to care
+ * about having matched imperfectly before, so we can
+ * free name and set *res to NULL.
+ * However, if the perfect match is a short file name,
+ * we need to signal this through *res, so that
+ * ntfs_lookup() can fix dcache aliasing issues.
+ * As an optimization we just reuse an existing
+ * allocation of *res.
+ */
+ /*FIXME:Shut for now and maybe work in future. Author:Gzged
+ if (ie->key.file_name.file_name_type == FILE_NAME_DOS) {
+ if (!name) {
+ name = kmalloc(sizeof(ntfs_name),
+ GFP_NOFS);
+ if (!name) {
+ err = -ENOMEM;
+ goto err_out;
+ }
+ }
+ name->mref = le64_to_cpu(
+ ie->data.dir.indexed_file);
+ name->type = FILE_NAME_DOS;
+ name->len = 0;
+ *res = name;
+ } else {
+ kfree(name);
+ *res = NULL;
+ }
+ */
+ /*FIXME: Gzged mod
+ mref = le64_to_cpu(ie->data.dir.indexed_file);
+ ntfs_attr_put_search_ctx(ctx);
+ unmap_mft_record(dir_ni);
+ return mref;
+ */
+
+done:
+            ictx->data = (u8*)ie +
+                    le16_to_cpu(ie->data.vi.data_offset);
+            ictx->data_len = le16_to_cpu(ie->data.vi.data_length);
+            ntfs_debug("Done.");
+            return err;
+
+ }
+ /*
+ * For a case insensitive mount, we also perform a case
+ * insensitive comparison (provided the file name is not in the
+ * POSIX namespace). If the comparison matches, and the name is
+ * in the WIN32 namespace, we cache the filename in *res so
+ * that the caller, ntfs_lookup(), can work on it. If the
+ * comparison matches, and the name is in the DOS namespace, we
+ * only cache the mft reference and the file name type (we set
+ * the name length to zero for simplicity).
+ */
+// if (!NVolCaseSensitive(vol) &&
+// ie->key.file_name.file_name_type &&
+// ntfs_are_names_equal(uname, uname_len,
+// (ntfschar*)&ie->key.file_name.file_name,
+// ie->key.file_name.file_name_length,
+// IGNORE_CASE, vol->upcase, vol->upcase_len)) {
+// int name_size = sizeof(ntfs_name);
+// u8 type = ie->key.file_name.file_name_type;
+// u8 len = ie->key.file_name.file_name_length;
+//
+// /* Only one case insensitive matching name allowed. */
+// if (name) {
+// ntfs_error(sb, "Found already allocated name "
+// "in phase 1. Please run chkdsk "
+// "and if that doesn't find any "
+// "errors please report you saw "
+// "this message to "
+// "linux-ntfs-dev@lists."
+// "sourceforge.net.");
+// goto dir_err_out;
+// }
+//
+// if (type != FILE_NAME_DOS)
+// name_size += len * sizeof(ntfschar);
+// name = kmalloc(name_size, GFP_NOFS);
+// if (!name) {
+// err = -ENOMEM;
+// goto err_out;
+// }
+// name->mref = le64_to_cpu(ie->data.dir.indexed_file);
+// name->type = type;
+// if (type != FILE_NAME_DOS) {
+// name->len = len;
+// memcpy(name->name, ie->key.file_name.file_name,
+// len * sizeof(ntfschar));
+// } else
+// name->len = 0;
+// *res = name;
+// }
+ /*
+ * Not a perfect match, need to do full blown collation so we
+ * know which way in the B+tree we have to go.
+ */
+ rc = ntfs_collate_names(uname, uname_len,
+ (ntfschar*)&ie->key.file_name.file_name,
+ ie->key.file_name.file_name_length, 1,
+ IGNORE_CASE, vol->upcase, vol->upcase_len);
+ /*
+ * If uname collates before the name of the current entry, there
+ * is definitely no such name in this index but we might need to
+ * descend into the B+tree so we just break out of the loop.
+ */
+ if (rc == -1)
+ break;
+ /* The names are not equal, continue the search. */
+ if (rc)
+ continue;
+ /*
+ * Names match with case insensitive comparison, now try the
+ * case sensitive comparison, which is required for proper
+ * collation.
+ */
+ rc = ntfs_collate_names(uname, uname_len,
+ (ntfschar*)&ie->key.file_name.file_name,
+ ie->key.file_name.file_name_length, 1,
+ CASE_SENSITIVE, vol->upcase, vol->upcase_len);
+ if (rc == -1)
+ break;
+ if (rc)
+ continue;
+ /*
+ * Perfect match, this will never happen as the
+ * ntfs_are_names_equal() call will have gotten a match but we
+ * still treat it correctly.
+ */
+ goto found_it;
+ }
+ /*
+ * We have finished with this index without success. Check for the
+ * presence of a child node and if not present return -ENOENT, unless
+ * we have got a matching name cached in name in which case return the
+ * mft reference associated with it.
+ */
+ if (!(ie->flags & INDEX_ENTRY_NODE)) {
+ /* FIXME:Gzged shut
+ if (name) {
+ ntfs_attr_put_search_ctx(ctx);
+ unmap_mft_record(dir_ni);
+ return name->mref;
+ }
+ ********/
+ ntfs_debug("Entry not found. ictx->is_in_root[%d]",ictx->is_in_root);
+
+ /*FIXME:Gzged set
+ ictx->is_in_root = true;
+ ictx->ir = ir;
+ ictx->entry = ie;
+ ictx->base_ni = dir_ni;
+ ictx->actx = ctx;
+ ictx->ia = NULL;
+ ictx->page = NULL;
+ */
+ /*FIXME: Gzged mod ; do not release actx ....and so on
+ goto err_out;
+ */
+ return -ENOENT;
+ } /* Child node present, descend into it. */
+ /* Consistency check: Verify that an index allocation exists. */
+ if (!NInoIndexAllocPresent(dir_ni)) {
+ ntfs_error(sb, "No index allocation attribute but index entry "
+ "requires one. Directory inode 0x%lx is "
+ "corrupt or driver bug.", dir_ni->mft_no);
+ goto err_out;
+ }
+ /* Get the starting vcn of the index_block holding the child node. */
+ vcn = sle64_to_cpup((sle64*)((u8*)ie + le16_to_cpu(ie->length) - 8));
+ ia_mapping = VFS_I(dir_ni)->i_mapping;
+ /*
+ * We are done with the index root and the mft record. Release them,
+ * otherwise we deadlock with ntfs_map_page().
+ */
+ ntfs_attr_put_search_ctx(ctx);
+ unmap_mft_record(dir_ni);
+ m = NULL;
+ ctx = NULL;
+descend_into_child_node:
+ /*
+ * Convert vcn to index into the index allocation attribute in units
+ * of PAGE_SIZE and map the page cache page, reading it from
+ * disk if necessary.
+ */
+ page = ntfs_map_page(ia_mapping, vcn <<
+ dir_ni->itype.index.vcn_size_bits >> PAGE_SHIFT);
+ if (IS_ERR(page)) {
+ ntfs_error(sb, "Failed to map directory index page, error %ld.",
+ -PTR_ERR(page));
+ err = PTR_ERR(page);
+ goto err_out;
+ }
+ lock_page(page);
+ kaddr = (u8*)page_address(page);
+fast_descend_into_child_node:
+ /* Get to the index allocation block. */
+ ia = (INDEX_ALLOCATION*)(kaddr + ((vcn <<
+ dir_ni->itype.index.vcn_size_bits) & ~PAGE_MASK));
+ /* Bounds checks. */
+ if ((u8*)ia < kaddr || (u8*)ia > kaddr + PAGE_SIZE) {
+ ntfs_error(sb, "Out of bounds check failed. Corrupt directory "
+ "inode 0x%lx or driver bug.", dir_ni->mft_no);
+ goto unm_err_out;
+ }
+ /* Catch multi sector transfer fixup errors. */
+ if (unlikely(!ntfs_is_indx_record(ia->magic))) {
+ ntfs_error(sb, "Directory index record with vcn 0x%llx is "
+ "corrupt.  Corrupt inode 0x%lx.  Run chkdsk.",
+ (unsigned long long)vcn, dir_ni->mft_no);
+ goto unm_err_out;
+ }
+ if (sle64_to_cpu(ia->index_block_vcn) != vcn) {
+ ntfs_error(sb, "Actual VCN (0x%llx) of index buffer is "
+ "different from expected VCN (0x%llx). "
+ "Directory inode 0x%lx is corrupt or driver "
+ "bug.", (unsigned long long)
+ sle64_to_cpu(ia->index_block_vcn),
+ (unsigned long long)vcn, dir_ni->mft_no);
+ goto unm_err_out;
+ }
+ if (le32_to_cpu(ia->index.allocated_size) + 0x18 !=
+ dir_ni->itype.index.block_size) {
+ ntfs_error(sb, "Index buffer (VCN 0x%llx) of directory inode "
+ "0x%lx has a size (%u) differing from the "
+ "directory specified size (%u). Directory "
+ "inode is corrupt or driver bug.",
+ (unsigned long long)vcn, dir_ni->mft_no,
+ le32_to_cpu(ia->index.allocated_size) + 0x18,
+ dir_ni->itype.index.block_size);
+ goto unm_err_out;
+ }
+ index_end = (u8*)ia + dir_ni->itype.index.block_size;
+ if (index_end > kaddr + PAGE_SIZE) {
+ ntfs_error(sb, "Index buffer (VCN 0x%llx) of directory inode "
+ "0x%lx crosses page boundary. Impossible! "
+ "Cannot access! This is probably a bug in the "
+ "driver.", (unsigned long long)vcn,
+ dir_ni->mft_no);
+ goto unm_err_out;
+ }
+ index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length);
+ if (index_end > (u8*)ia + dir_ni->itype.index.block_size) {
+ ntfs_error(sb, "Size of index buffer (VCN 0x%llx) of directory "
+ "inode 0x%lx exceeds maximum size.",
+ (unsigned long long)vcn, dir_ni->mft_no);
+ goto unm_err_out;
+ }
+ /* The first index entry. */
+ ie = (INDEX_ENTRY*)((u8*)&ia->index +
+ le32_to_cpu(ia->index.entries_offset));
+ /*
+ * Iterate similar to above big loop but applied to index buffer, thus
+ * loop until we exceed valid memory (corruption case) or until we
+ * reach the last entry.
+ */
+ for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) {
+ /* Bounds check. */
+ if ((u8*)ie < (u8*)ia || (u8*)ie +
+ sizeof(INDEX_ENTRY_HEADER) > index_end ||
+ (u8*)ie + le16_to_cpu(ie->key_length) >
+ index_end) {
+ ntfs_error(sb, "Index entry out of bounds in "
+ "directory inode 0x%lx.",
+ dir_ni->mft_no);
+ goto unm_err_out;
+ }
+ /*FIXME:Gzged set */
+ ictx->is_in_root = false;
+ ictx->ia = ia;
+ ictx->entry = ie;
+ ictx->actx = NULL;
+ ictx->base_ni = NULL;
+ ictx->page = page;
+ /*
+ * The last entry cannot contain a name. It can however contain
+ * a pointer to a child node in the B+tree so we just break out.
+ */
+ if (ie->flags & INDEX_ENTRY_END)
+ break;
+ /*
+ * We perform a case sensitive comparison and if that matches
+ * we are done and return the mft reference of the inode (i.e.
+ * the inode number together with the sequence number for
+ * consistency checking). We convert it to cpu format before
+ * returning.
+ */
+ BUG_ON(!PageLocked(ictx->page));
+
+ if (ntfs_are_names_equal(uname, uname_len,
+ (ntfschar*)&ie->key.file_name.file_name,
+ ie->key.file_name.file_name_length,
+ CASE_SENSITIVE, vol->upcase, vol->upcase_len)) {
+found_it2:
+ /*
+ * We have a perfect match, so we don't need to care
+ * about having matched imperfectly before, so we can
+ * free name and set *res to NULL.
+ * However, if the perfect match is a short file name,
+ * we need to signal this through *res, so that
+ * ntfs_lookup() can fix dcache aliasing issues.
+ * As an optimization we just reuse an existing
+ * allocation of *res.
+ */
+ /* Gzged shut
+ if (ie->key.file_name.file_name_type == FILE_NAME_DOS) {
+ if (!name) {
+ name = kmalloc(sizeof(ntfs_name),
+ GFP_NOFS);
+ if (!name) {
+ err = -ENOMEM;
+ goto unm_err_out;
+ }
+ }
+ name->mref = le64_to_cpu(
+ ie->data.dir.indexed_file);
+ name->type = FILE_NAME_DOS;
+ name->len = 0;
+ *res = name;
+ } else {
+ kfree(name);
+ *res = NULL;
+ }
+ */
+ /*Gzged shut
+ mref = le64_to_cpu(ie->data.dir.indexed_file);
+ unlock_page(page);
+ ntfs_unmap_page(page);
+ * return mref;
+ * */
+            goto done;
+ }
+ /*
+ * For a case insensitive mount, we also perform a case
+ * insensitive comparison (provided the file name is not in the
+ * POSIX namespace). If the comparison matches, and the name is
+ * in the WIN32 namespace, we cache the filename in *res so
+ * that the caller, ntfs_lookup(), can work on it. If the
+ * comparison matches, and the name is in the DOS namespace, we
+ * only cache the mft reference and the file name type (we set
+ * the name length to zero for simplicity).
+// */
+// if (!NVolCaseSensitive(vol) &&
+// ie->key.file_name.file_name_type &&
+// ntfs_are_names_equal(uname, uname_len,
+// (ntfschar*)&ie->key.file_name.file_name,
+// ie->key.file_name.file_name_length,
+// IGNORE_CASE, vol->upcase, vol->upcase_len)) {
+// int name_size = sizeof(ntfs_name);
+// u8 type = ie->key.file_name.file_name_type;
+// u8 len = ie->key.file_name.file_name_length;
+//
+// /* Only one case insensitive matching name allowed. */
+// if (name) {
+// ntfs_error(sb, "Found already allocated name "
+// "in phase 2. Please run chkdsk "
+// "and if that doesn't find any "
+// "errors please report you saw "
+// "this message to "
+// "linux-ntfs-dev@lists."
+// "sourceforge.net.");
+// unlock_page(page);
+// ntfs_unmap_page(page);
+// goto dir_err_out;
+// }
+//
+// if (type != FILE_NAME_DOS)
+// name_size += len * sizeof(ntfschar);
+// name = kmalloc(name_size, GFP_NOFS);
+// if (!name) {
+// err = -ENOMEM;
+// goto unm_err_out;
+// }
+// name->mref = le64_to_cpu(ie->data.dir.indexed_file);
+// name->type = type;
+// if (type != FILE_NAME_DOS) {
+// name->len = len;
+// memcpy(name->name, ie->key.file_name.file_name,
+// len * sizeof(ntfschar));
+// } else
+// name->len = 0;
+// *res = name;
+// }
+ /*
+ * Not a perfect match, need to do full blown collation so we
+ * know which way in the B+tree we have to go.
+ */
+ rc = ntfs_collate_names(uname, uname_len,
+ (ntfschar*)&ie->key.file_name.file_name,
+ ie->key.file_name.file_name_length, 1,
+ IGNORE_CASE, vol->upcase, vol->upcase_len);
+ /*
+ * If uname collates before the name of the current entry, there
+ * is definitely no such name in this index but we might need to
+ * descend into the B+tree so we just break out of the loop.
+ */
+ if (rc == -1)
+ break;
+ /* The names are not equal, continue the search. */
+ if (rc)
+ continue;
+ /*
+ * Names match with case insensitive comparison, now try the
+ * case sensitive comparison, which is required for proper
+ * collation.
+ */
+ rc = ntfs_collate_names(uname, uname_len,
+ (ntfschar*)&ie->key.file_name.file_name,
+ ie->key.file_name.file_name_length, 1,
+ CASE_SENSITIVE, vol->upcase, vol->upcase_len);
+ if (rc == -1)
+ break;
+ if (rc)
+ continue;
+ /*
+ * Perfect match, this will never happen as the
+ * ntfs_are_names_equal() call will have gotten a match but we
+ * still treat it correctly.
+ */
+ goto found_it2;
+ }
+ /*
+ * We have finished with this index buffer without success. Check for
+ * the presence of a child node.
+ */
+ if (ie->flags & INDEX_ENTRY_NODE) {
+ if ((ia->index.flags & NODE_MASK) == LEAF_NODE) {
+ ntfs_error(sb, "Index entry with child node found in "
+ "a leaf node in directory inode 0x%lx.",
+ dir_ni->mft_no);
+ goto unm_err_out;
+ }
+ /* Child node present, descend into it. */
+ old_vcn = vcn;
+ vcn = sle64_to_cpup((sle64*)((u8*)ie +
+ le16_to_cpu(ie->length) - 8));
+ if (vcn >= 0) {
+ /* If vcn is in the same page cache page as old_vcn we
+ * recycle the mapped page. */
+ if (old_vcn << vol->cluster_size_bits >>
+ PAGE_SHIFT == vcn <<
+ vol->cluster_size_bits >>
+ PAGE_SHIFT)
+ goto fast_descend_into_child_node;
+ unlock_page(page);
+ ntfs_unmap_page(page);
+ goto descend_into_child_node;
+ }
+ ntfs_error(sb, "Negative child node vcn in directory inode "
+ "0x%lx.", dir_ni->mft_no);
+ goto unm_err_out;
+ }
+ /*
+ * No child node present, return -ENOENT, unless we have got a matching
+ * name cached in name in which case return the mft reference
+ * associated with it.
+ */
+/********
+ if (name) {
+ unlock_page(page);
+ ntfs_unmap_page(page);
+ return name->mref;
+ }
+ *******/
+ ntfs_debug("Entry not found.");
+ err = -ENOENT;
+/*
+ if (!err)
+ err = -EIO;
+ if (ctx)
+ ntfs_attr_put_search_ctx(ctx);
+ if (m)
+ unmap_mft_record(dir_ni);
+ */
+ return ERR_MREF(err);
+
+unm_err_out:
+ unlock_page(page);
+ ntfs_unmap_page(page);
+err_out:
+ if (!err)
+ err = -EIO;
+ if (ctx)
+ ntfs_attr_put_search_ctx(ctx);
+ if (m)
+ unmap_mft_record(dir_ni);
+ /*Gzged shut
+ if (name) {
+ kfree(name);
+ *res = NULL;
+ }
+ */
+ ntfs_debug("done.");
+ return ERR_MREF(err);
+dir_err_out:
+ ntfs_error(sb, "Corrupt directory.  Aborting lookup.");
+ goto err_out;
+}
 MFT_REF ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname,
  const int uname_len, ntfs_name **res)
 {
diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c
index c4f68c3..688ec8f 100644
--- a/fs/ntfs/file.c
+++ b/fs/ntfs/file.c
@@ -24,6 +24,7 @@
 #include <linux/gfp.h>
 #include <linux/pagemap.h>
 #include <linux/pagevec.h>
+#include <linux/sched.h>
 #include <linux/sched/signal.h>
 #include <linux/swap.h>
 #include <linux/uio.h>
diff --git a/fs/ntfs/index.h b/fs/ntfs/index.h
index 8745469..b7d75ce 100644
--- a/fs/ntfs/index.h
+++ b/fs/ntfs/index.h
@@ -88,6 +88,7 @@ typedef struct {
 extern ntfs_index_context *ntfs_index_ctx_get(ntfs_inode *idx_ni);
 extern void ntfs_index_ctx_put(ntfs_index_context *ictx);
 
+extern int ntfs_lookup_inode_by_key (const void *key, const int key_len, ntfs_index_context *ictx);
 extern int ntfs_index_lookup(const void *key, const int key_len,
  ntfs_index_context *ictx);
 
diff --git a/fs/ntfs/mft.c b/fs/ntfs/mft.c
index b6f4021..dbe7f21 100644
--- a/fs/ntfs/mft.c
+++ b/fs/ntfs/mft.c
@@ -2915,4 +2915,84 @@ int ntfs_extent_mft_record_free(ntfs_inode *ni, MFT_RECORD *m)
  mark_mft_record_dirty(ni);
  return err;
 }
+/**
+ * ntfs_mft_record_free - free an mft record on an ntfs volume
+ * @vol: volume on which to free the mft record
+ * @ni: open ntfs inode of the mft record to free
+ *
+ * Free the mft record of the open inode @ni on the mounted ntfs volume @vol.
+ * Note that this function calls ntfs_inode_close() internally and hence you
+ * cannot use the pointer @ni any more after this function returns success.
+ *
+ * On success return 0 and on error return -1 with errno set to the error code.
+ */
+int ntfs_mft_record_free(ntfs_volume *vol, ntfs_inode *ni,MFT_RECORD* mrec)
+{
+ u64 mft_no;
+ int err;
+ u16 seq_no, old_seq_no;
+
+ ntfs_debug("Entering for inode 0x%llx.\n", (long long) ni->mft_no);
+
+ if (!vol || !vol->mftbmp_ino || !ni)
+ {
+ return -EINVAL;
+ }
+
+ /* Cache the mft reference for later. */
+ mft_no = ni->mft_no;
+
+ /* Mark the mft record as not in use. */
+ mrec->flags &= ~MFT_RECORD_IN_USE;
+
+ /* Increment the sequence number, skipping zero, if it is not zero. */
+ old_seq_no = mrec->sequence_number;
+
+ seq_no = le16_to_cpu(old_seq_no);
+ if (seq_no == 0xffff)
+ seq_no = 1;
+ else if (seq_no)
+ seq_no++;
+ mrec->sequence_number = cpu_to_le16(seq_no);
+
+ /* Set the inode dirty and write it out. */
+ flush_dcache_mft_record_page(ni);
+ mark_mft_record_dirty(ni);
+
+ /* Clear the bit in the $MFT/$BITMAP corresponding to this record. */
+ ntfs_debug("before down_write...");
+ //down_write(&vol->mftbmp_lock);
+ if ( (err = ntfs_bitmap_clear_bit(vol->mftbmp_ino, mft_no )))
+ //up_write(&vol->mftbmp_lock);
+ {
+ // FIXME: If ntfs_bitmap_clear_run() guarantees rollback on
+ //  error, this could be changed to goto sync_rollback;
+ goto bitmap_rollback;
+ }
+
+ /* Throw away the now freed inode.
+ if (!ntfs_inode_close(ni)) {
+ vol->free_mft_records++;
+ return 0;
+ }
+ err = errno;*/
+ flush_dcache_mft_record_page(ni);
+ mark_mft_record_dirty(ni);
+ return 0;
+
+ /* Rollback what we did... */
+bitmap_rollback:
+ down_write(&vol->mftbmp_lock);
+ if (ntfs_bitmap_set_bit(vol->mftbmp_ino, mft_no))
+ up_write(&vol->mftbmp_lock);
+ {
+ ntfs_debug("Eeek! Rollback failed in ntfs_mft_record_free().  "
+ "Leaving inconsistent metadata!\n");
+ }
+ mrec->flags |= MFT_RECORD_IN_USE;
+ mrec->sequence_number = old_seq_no;
+ flush_dcache_mft_record_page(ni);
+ mark_mft_record_dirty(ni);
+ return err;
+}
 #endif /* NTFS_RW */
diff --git a/fs/ntfs/mft.h b/fs/ntfs/mft.h
index b52bf87..f6a95ba 100644
--- a/fs/ntfs/mft.h
+++ b/fs/ntfs/mft.h
@@ -118,6 +118,7 @@ extern bool ntfs_may_write_mft_record(ntfs_volume *vol,
 extern ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, const int mode,
  ntfs_inode *base_ni, MFT_RECORD **mrec);
 extern int ntfs_extent_mft_record_free(ntfs_inode *ni, MFT_RECORD *m);
+extern int ntfs_mft_record_free(ntfs_volume *vol, ntfs_inode *ni,MFT_RECORD* mrec);
 
 #endif /* NTFS_RW */
 
diff --git a/fs/ntfs/namei.c b/fs/ntfs/namei.c
index 4690cd7..a737cf5 100644
--- a/fs/ntfs/namei.c
+++ b/fs/ntfs/namei.c
@@ -159,7 +159,7 @@ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent,
  PTR_ERR(dent_inode));
  kfree(name);
  /* Return the error code. */
- return ERR_CAST(dent_inode);
+ return (struct dentry *)dent_inode;
  }
  /* It is guaranteed that @name is no longer allocated at this point. */
  if (MREF_ERR(mref) == -ENOENT) {
@@ -273,11 +273,1373 @@ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent,
    }
 }
 
+
+#include "index.h"
+
+#define STATUS_OK               (0)
+#define STATUS_ERROR                (-1)
+
+/**
+ *  Insert @ie index entry at @pos entry. Used @ih values should be ok already.
+ */
+static void ntfs_ie_insert(INDEX_HEADER *ih, INDEX_ENTRY *ie, INDEX_ENTRY *pos)
+{
+ int ie_size = le16_to_cpu(ie->length);
+ ntfs_debug("Entering");
+ ih->index_length = cpu_to_le32(le32_to_cpu(ih->index_length) + ie_size);
+ memmove((u8 *)pos + ie_size, pos, le32_to_cpu(ih->index_length) - ((u8 *)pos - (u8 *)ih) - ie_size);
+ memcpy(pos, ie, ie_size);
+ ntfs_debug("done");
+}
+/**
+ * ntfs_ir_truncate - Truncate index root attribute
+ *
+ * Returns STATUS_OK, STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT or STATUS_ERROR.
+ */
+static int ntfs_ir_truncate(ntfs_index_context *icx, int data_size)
+{  
+/*
+ ntfs_attr *na;
+ */
+ int ret;
+
+ ntfs_debug("Entering");
+
+/**
+ na = ntfs_attr_open(icx->ni, AT_INDEX_ROOT, icx->name, icx->name_len);
+ if (!na) {
+ ntfs_log_perror("Failed to open INDEX_ROOT");
+ return STATUS_ERROR;
+ }
+ */
+ /*
+ *  INDEX_ROOT must be resident and its entries can be moved to
+ *  INDEX_BLOCK, so ENOSPC isn't a real error.
+ */
+ ret = ntfs_resident_attr_value_resize(icx->actx->mrec, icx->actx->attr, data_size + offsetof(INDEX_ROOT, index) );
+ /*Gzged changed
+ ret = ntfs_attr_truncate(na, data_size + offsetof(INDEX_ROOT, index));
+ */
+ if (ret == STATUS_OK)
+ {
+ /*
+ icx->ir = ntfs_ir_lookup2(icx->ni, icx->name, icx->name_len);
+ if (!icx->ir)
+ return STATUS_ERROR;
+ */
+
+ icx->ir->index.allocated_size = cpu_to_le32(data_size);
+
+ } else if (ret == -EPERM)
+ {
+ ntfs_debug("Failed to truncate INDEX_ROOT");
+ }
+
+/**
+ ntfs_attr_close(na);
+ */
+ return ret;
+}
+
+/**
+ * ntfs_ir_make_space - Make more space for the index root attribute
+ *
+ * On success return STATUS_OK or STATUS_KEEP_SEARCHING.
+ * On error return STATUS_ERROR.
+ */
+static int ntfs_ir_make_space(ntfs_index_context *icx, int data_size)
+{  
+ int ret;
+ ntfs_debug("Entering");
+ ret = ntfs_ir_truncate(icx, data_size);
+ /* TODO
+ if (ret == STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT)
+ {
+ ret = ntfs_ir_reparent(icx);
+ if (ret == STATUS_OK)
+ ret = STATUS_KEEP_SEARCHING;
+ else
+ ntfs_log_perror("Failed to nodify INDEX_ROOT");
+ }
+ */
+ ntfs_debug("Done ");
+ return ret;
+}
+
+
+static int ntfs_ie_add(ntfs_index_context *icx, INDEX_ENTRY *ie)
+{
+ INDEX_HEADER *ih;
+ int allocated_size, new_size;
+ int ret = STATUS_ERROR;
+ ntfs_inode *idx_ni = icx->idx_ni;
+ ntfs_debug("Entering. ");
+ while (1)
+ {
+ /* ret = ntfs_index_lookup(&ie->key, le16_to_cpu(ie->key_length), icx) ; */
+ ntfs_debug("new loop ");
+ ret = ntfs_lookup_inode_by_key(&ie->key, le16_to_cpu(ie->key_length), icx) ;
+ ntfs_debug("ntfs_lookup_inode_by_key ret[%d]",ret);
+ if (!ret)
+ {
+ ntfs_debug("Index already have such entry");
+ goto err_out;
+ }
+ if (ret != -ENOENT)
+ {
+ ntfs_debug("Failed to find place for new entry");
+ goto err_out;
+ }
+ /*
+ ntfs_debug("here icx[%p] icx->is_in_root[%d]",icx,icx->is_in_root);
+ */
+ if (icx->is_in_root)
+ {
+ BUG_ON(!icx->ir);
+ ih = &(icx->ir->index);
+ }
+ else
+ {
+ BUG_ON(!icx->ia);
+ ih = &(icx->ia->index);
+ }
+
+ allocated_size = le32_to_cpu(ih->allocated_size);
+ new_size = le32_to_cpu(ih->index_length) + le16_to_cpu(ie->length);
+
+ ntfs_debug("index block sizes: allocated: %d  needed: %d", allocated_size, new_size);
+ if (new_size <= allocated_size)
+ {
+ break;
+ }
+ /** else  it will make space for new index entry **/
+
+ if (icx->is_in_root)
+ {
+ if ( (ret = ntfs_ir_make_space(icx, new_size) ) )
+ {
+ ntfs_debug("ntfs_ir_make_space err ");
+ goto err_out;
+ }
+ else
+ {
+ ntfs_debug("ntfs_ir_make_space done ");
+ }
+ }
+ else
+ {
+ ntfs_debug("should run ntfs_ib_split ");
+ ret = -ENOSPC;
+ goto err_out;
+
+ /* Gzged shut
+ if (ntfs_ib_split(icx, icx->ib) == STATUS_ERROR)
+ {
+ ntfs_debug("ntfs_ib_split err ");
+ ret = -ENOMEM;
+ goto err_out;
+ }
+ **/
+ }
+
+ /*FIXME: Gzged mod
+ ntfs_inode_mark_dirty(icx->actx->ntfs_ino);
+ ***/
+ /*FIXME: Gzged will fix these in furture */
+ ntfs_debug("Before flush_dcache_mft_record_page ");/*****die here *****/
+ flush_dcache_mft_record_page(icx->actx->ntfs_ino);
+
+ ntfs_debug("Before mark_mft_record_dirty ");
+ mark_mft_record_dirty(icx->actx->ntfs_ino);
+
+ /*FIXME: Gzged mod ntfs_index_ctx_reinit(icx); ***/
+ ntfs_index_ctx_put(icx);
+ ntfs_index_ctx_get(idx_ni);
+ }
+
+ ntfs_ie_insert(ih, ie, icx->entry);
+ ntfs_index_entry_flush_dcache_page(icx);
+ ntfs_index_entry_mark_dirty(icx);
+
+ ret = STATUS_OK;
+err_out:
+ ntfs_debug("%s", ret ? "Failed" : "Done");
+ return ret;
+}
+/**
+ * ntfs_index_add_filename - add filename to directory index
+ * @ni: ntfs inode describing directory to which index add filename
+ * @fn: FILE_NAME attribute to add
+ * @mref: reference of the inode which @fn describes
+ *
+ * Return 0 on success or -1 on error with errno set to the error code.
+ */
+ntfs_index_context * ntfs_index_ctx_get_I30(ntfs_inode *idx_ni)
+{
+    static ntfschar I30[5] = { cpu_to_le16('$'),
+            cpu_to_le16('I'),
+            cpu_to_le16('3'),
+            cpu_to_le16('0'),
+ 0 };
+
+    struct inode * tmp_ino = ntfs_index_iget(VFS_I(idx_ni), I30, 4);
+    if (IS_ERR(tmp_ino)) {
+        ntfs_debug("Failed to load $I30 index.");
+        return NULL;
+    }
+
+ return ntfs_index_ctx_get(NTFS_I(tmp_ino));
+}
+void ntfs_index_ctx_put_I30(ntfs_index_context *ictx)
+{
+ ntfs_commit_inode(VFS_I(ictx->idx_ni));
+ iput(VFS_I(ictx->idx_ni));
+ ntfs_index_ctx_put(ictx);
+}
+static int ntfs_index_add_filename(ntfs_inode *dir_ni, FILE_NAME_ATTR *fn, MFT_REF mref)
+{
+ INDEX_ENTRY *ie;
+ ntfs_index_context *icx;
+ int ret = -1;
+
+ ntfs_debug("Entering");
+
+ if (!dir_ni || !fn)
+ {
+ ntfs_error((VFS_I(dir_ni))->i_sb,"Invalid arguments.");
+ return -EINVAL;
+ }
+
+ {/** create and set INDEX_entry **/
+ int fn_size, ie_size;
+ fn_size = (fn->file_name_length * sizeof(ntfschar)) + sizeof(FILE_NAME_ATTR);
+ ie_size = (sizeof(INDEX_ENTRY_HEADER) + fn_size + 7) & ~7;
+
+ ie = kcalloc(1,ie_size,GFP_KERNEL);
+ if (!ie)
+ {
+ return -ENOMEM;
+ }
+
+ ie->data.dir.indexed_file = cpu_to_le64(mref);
+ ie->length = cpu_to_le16(ie_size);
+ ie->key_length = cpu_to_le16(fn_size);
+ ie->flags = cpu_to_le16(0);
+ memcpy(&(ie->key.file_name), fn, fn_size);
+ }/**  END of create and set INDEX_entry **/
+
+ icx =  ntfs_index_ctx_get(dir_ni);
+ if (!icx)
+ {
+ ret = PTR_ERR(icx);
+ goto out;
+ }
+
+ ret = ntfs_ie_add(icx, ie);
+out:
+ if(icx)
+ {
+ ntfs_index_ctx_put(icx);
+ }
+ if(ie)
+ {
+ kfree(ie);
+ }
+ ntfs_debug("done");
+ return ret;
+}
+
+
+/**
+ * __ntfs_create - create object on ntfs volume
+ * @dir_ni: ntfs inode for directory in which create new object
+ * @name: unicode name of new object
+ * @name_len: length of the name in unicode characters
+ * @type: type of the object to create
+ *
+ * Internal, use ntfs_create{,_device,_symlink} wrappers instead.
+ *
+ * @type can be:
+ * S_IFREG to create regular file
+ *
+ * Return opened ntfs inode that describes created object on success
+ * or ERR_PTR(errno) on error
+ */
+
+static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni,
+ ntfschar *name, u8 name_len, dev_t type )
+{
+ ntfs_inode *ni =NULL;/** this is for new inode **/
+ FILE_NAME_ATTR *fn = NULL;
+ STANDARD_INFORMATION *si = NULL;
+ SECURITY_DESCRIPTOR_ATTR *sd =NULL;
+ int err;
+
+ /*Author:Gzged */
+ MFT_RECORD* mrec;
+ int new_temp_offset ;
+ char* temp_new_record;
+
+ ntfs_debug("Entering.");
+
+ /* Sanity checks. */
+ if (!dir_ni || !name || !name_len)
+ {
+ ntfs_error((VFS_I(dir_ni))->i_sb,"Invalid arguments.");
+ return ERR_PTR(-EINVAL);
+ }
+
+ /* TODO : not support REPARSE_POINT **/
+
+ /** alloc new mft record for new file **/
+ ni = ntfs_mft_record_alloc(dir_ni->vol, type,NULL,&mrec);
+ if (IS_ERR(ni))
+ {
+ ntfs_debug("ntfs_mft_record_alloc error [%ld]",PTR_ERR(ni));
+ return ni;
+ }
+ else
+ {
+ new_temp_offset = mrec->attrs_offset ;
+ /** ntfs_mft_record_alloc{} had map ni to mrec */
+ temp_new_record=(char*)mrec;
+ ntfs_debug("mrec->mft_record_number [%d]",mrec->mft_record_number);
+ }
+
+
+/**************** Begin from here , error must goto err_out *****************/
+ { /************************ STANDARD_INFORMATION start ******************/
+ /*
+ * Create STANDARD_INFORMATION attribute. Write STANDARD_INFORMATION
+ * version 1.2, windows will upgrade it to version 3 if needed.
+ */
+ ATTR_REC attr_si;
+ int si_len,attr_si_len;
+
+ /*** $STANDARD_INFORMATION (0x10)  **/
+ si_len = offsetof(STANDARD_INFORMATION, ver) + sizeof(si->ver.v1.reserved12);
+ si = kcalloc(1,si_len,GFP_KERNEL );
+ if (!si)
+ {
+ err = -ENOMEM;
+ goto err_out;
+ }
+#define NTFS_TIME_OFFSET ((s64)(369 * 365 + 89) * 24 * 3600 * 10000000)
+ si->creation_time = NTFS_TIME_OFFSET ;
+ si->last_data_change_time = NTFS_TIME_OFFSET ;
+ si->last_mft_change_time = NTFS_TIME_OFFSET ;
+ si->last_access_time = NTFS_TIME_OFFSET ;
+
+ /** set attr **/
+ attr_si_len = offsetof(ATTR_REC, data) + sizeof(attr_si.data.resident) ;
+ attr_si= (ATTR_REC )
+ {
+ .type = AT_STANDARD_INFORMATION ,
+ .length = attr_si_len +  si_len ,
+ .non_resident = 0,
+ .name_length = 0,
+ .name_offset = 0,
+ .flags =  0 ,
+ .instance = (mrec->next_attr_instance) ++ ,
+ .data=
+ {
+ .resident=
+ {
+ .value_length = si_len,
+ .value_offset = attr_si_len  ,
+ .flags = 0 ,
+ }
+ },
+ };
+ /*
+ attr_si.data.resident.value_length=si_len
+ attr_si.data.resident.flags = 0;
+ */
+
+ /* Add STANDARD_INFORMATION to inode. */
+ memcpy(&(temp_new_record[new_temp_offset]),&attr_si, attr_si_len  );
+ new_temp_offset += attr_si_len;
+ memcpy(&(temp_new_record[new_temp_offset]),  si,  si_len);
+ new_temp_offset += si_len;
+
+ ntfs_debug("new_temp_offset [%d]",new_temp_offset);
+
+ kfree(si);
+ si=NULL;
+ } /****************************** end of STANDARD_INFORMATION *************/
+
+ /*
+ if (ntfs_sd_add_everyone(ni)) {
+ err = errno;
+ goto err_out;
+ }
+ rollback_sd = 1;
+ */
+
+
+
+ { /****************************** start of FILE_NAME *************/
+
+ ATTR_REC attr_fna;
+ int fn_len , attr_fna_len;
+ /** create FILE_NAME_ATTR **/
+ fn_len = sizeof(FILE_NAME_ATTR) + name_len * sizeof(ntfschar);
+ fn = kcalloc(1,fn_len,GFP_KERNEL);
+ if (!fn)
+ {
+ err = -ENOMEM;
+ goto err_out;
+ }
+ fn->parent_directory = MK_LE_MREF(dir_ni->mft_no, le16_to_cpu(dir_ni->seq_no));
+ fn->file_name_length = name_len;
+ fn->file_name_type = FILE_NAME_POSIX;
+ fn->creation_time = NTFS_TIME_OFFSET;
+ fn->last_data_change_time = NTFS_TIME_OFFSET;
+ fn->last_mft_change_time = NTFS_TIME_OFFSET;
+ fn->last_access_time = NTFS_TIME_OFFSET;
+ fn->data_size = cpu_to_sle64(ni->initialized_size);
+ fn->allocated_size = cpu_to_sle64(ni->allocated_size);
+ memcpy(fn->file_name, name, name_len * sizeof(ntfschar));
+
+ /* Create FILE_NAME attribute. */
+ attr_fna_len = offsetof(ATTR_REC, data) + sizeof(attr_fna.data.resident) ;
+ attr_fna=(ATTR_REC)
+ {
+ .type = AT_FILE_NAME ,
+ .length = ( attr_fna_len + fn_len + 7 ) & ~7 ,
+ .non_resident = 0,
+ .name_length = 0,
+ .name_offset = 0,
+ .flags =  RESIDENT_ATTR_IS_INDEXED ,
+ .instance = (mrec->next_attr_instance) ++ ,
+ .data=
+ {
+ .resident=
+ {
+ .value_length = fn_len,
+ .value_offset = attr_fna_len  ,
+ .flags = 0 ,
+ }
+ },
+ };
+
+ /** insert FILE_NAME into new_file_record **/
+ memcpy(&(temp_new_record[new_temp_offset]) , &attr_fna,  attr_fna_len);
+ memcpy(&(temp_new_record[new_temp_offset + attr_fna_len]),fn,fn_len);
+ new_temp_offset += attr_fna.length;
+
+ ntfs_debug("new_temp_offset [%d]",new_temp_offset);
+
+/**********add to index ********************************************/
+ /* Add FILE_NAME attribute to index. */
+ if ((err = ntfs_index_add_filename(dir_ni, fn, MK_MREF(ni->mft_no, le16_to_cpu(ni->seq_no))) ))
+ {
+ ntfs_error((VFS_I(dir_ni))->i_sb,"Failed to add entry to the index");
+ goto err_out;
+ }
+/*********************************************************/
+
+ kfree(fn);
+ fn=NULL;
+ } /****************************** end of FILE_NAME *************/
+
+ { /****************************** start of SECURITY_DESCRIPTOR *************/
+ ACL *acl;
+ ACCESS_ALLOWED_ACE *ace;
+ SID *sid;
+ ATTR_REC attr_sd;
+ int sd_len, attr_sd_len;
+
+ /* Create SECURITY_DESCRIPTOR attribute (everyone has full access). */
+ /*
+ * Calculate security descriptor length. We have 2 sub-authorities in
+ * owner and group SIDs, but structure SID contain only one, so add
+ * 4 bytes to every SID.
+ */
+ sd_len = sizeof(SECURITY_DESCRIPTOR_ATTR) + 2 * (sizeof(SID) + 4) +
+ sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE);
+ sd = kcalloc(1,sd_len,GFP_KERNEL);
+ if (!sd)
+ {
+ err = -ENOMEM;
+ goto err_out;
+ }
+
+ sd->revision = 1;
+ sd->control = SE_DACL_PRESENT | SE_SELF_RELATIVE;
+
+ sid = (SID *)((u8 *)sd + sizeof(SECURITY_DESCRIPTOR_ATTR));
+ sid->revision = 1;
+ sid->sub_authority_count = 2;
+ sid->sub_authority[0] = cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID);
+ sid->sub_authority[1] = cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS);
+ sid->identifier_authority.value[5] = 5;
+ sd->owner = cpu_to_le32((u8 *)sid - (u8 *)sd);
+
+ sid = (SID *)((u8 *)sid + sizeof(SID) + 4);
+ sid->revision = 1;
+ sid->sub_authority_count = 2;
+ sid->sub_authority[0] = cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID);
+ sid->sub_authority[1] = cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS);
+ sid->identifier_authority.value[5] = 5;
+ sd->group = cpu_to_le32((u8 *)sid - (u8 *)sd);
+
+ acl = (ACL *)((u8 *)sid + sizeof(SID) + 4);
+ acl->revision = 2;
+ acl->size = cpu_to_le16(sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE));
+ acl->ace_count = cpu_to_le16(1);
+ sd->dacl = cpu_to_le32((u8 *)acl - (u8 *)sd);
+
+ ace = (ACCESS_ALLOWED_ACE *)((u8 *)acl + sizeof(ACL));
+ ace->type = ACCESS_ALLOWED_ACE_TYPE;
+ ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE;
+ ace->size = cpu_to_le16(sizeof(ACCESS_ALLOWED_ACE));
+ ace->mask = cpu_to_le32(0x1f01ff); /* FIXME */
+ ace->sid.revision = 1;
+ ace->sid.sub_authority_count = 1;
+ ace->sid.sub_authority[0] = 0;
+ ace->sid.identifier_authority.value[5] = 1;
+
+ /* Create the attribute */
+ attr_sd_len = offsetof(ATTR_REC, data) + sizeof(attr_sd.data.resident) ;
+ attr_sd=(ATTR_REC)
+ {
+ .type = AT_SECURITY_DESCRIPTOR  ,
+ .length = ( attr_sd_len + sd_len + 7 ) & ~7 ,
+ .non_resident = 0,
+ .name_length = 0,
+ .name_offset = 0,
+ .flags =  0 ,
+ .instance = (mrec->next_attr_instance) ++ ,
+ .data=
+ {
+ .resident=
+ {
+ .value_length = sd_len,
+ .value_offset = attr_sd_len  ,
+ .flags = 0 ,
+ }
+ },
+ };
+
+ /** insert FILE_NAME into new_file_record **/
+ memcpy(&(temp_new_record[new_temp_offset]) , &attr_sd,  attr_sd_len);
+ memcpy(&(temp_new_record[new_temp_offset + attr_sd_len]), sd ,sd_len);
+ new_temp_offset += attr_sd.length;
+ ntfs_debug("new_temp_offset [%d]",new_temp_offset);
+/************************/
+
+ kfree(sd);
+ sd=NULL;
+ } /****************************** end of SECURITY_DESCRIPTOR *************/
+
+ { /****************************** start of DATA *************/
+ /***  $DATA (0x80)   **/
+ ATTR_REC attr_data;
+ int attr_data_len= offsetof(ATTR_REC, data) + sizeof(attr_data.data.resident) ;
+ attr_data=(ATTR_REC)
+ {
+ .type = AT_DATA ,
+ .length = attr_data_len,
+ .non_resident = 0,
+ .name_length = 0,
+ .name_offset = 0,
+ .flags =  0 ,
+ .instance = (mrec->next_attr_instance) ++ ,
+ .data=
+ {
+ .resident=
+ {
+ .value_length = 0,
+ .value_offset = attr_data_len  ,
+ .flags = 0 ,
+ }
+ },
+ };
+ /** insert DATA into new_file_record **/
+ memcpy(&(temp_new_record[new_temp_offset]),&attr_data, attr_data_len );
+ new_temp_offset += attr_data_len ;
+ ntfs_debug("new_temp_offset [%d]",new_temp_offset);
+
+ } /****************************** end of DATA *************/
+
+ { /****************************** start of $END *************/
+ /***  $AT_END              = cpu_to_le32(0xffffffff) */
+ ATTR_REC attr_end ;
+ int attr_end_len= offsetof(ATTR_REC, data) + sizeof(attr_end.data.resident) ;
+ attr_end=(ATTR_REC)
+ {
+ .type = AT_END ,
+ .length = attr_end_len ,
+ .non_resident = 0,
+ .name_length = 0,
+ .name_offset = 0,
+ .flags =  0 ,
+ .instance = (mrec->next_attr_instance) ++ ,
+ .data=
+ {
+ .resident=
+ {
+ .value_length = 0,
+ .value_offset = attr_end_len  ,
+ .flags = 0 ,
+ }
+ },
+ };
+ /** insert END into new_file_record **/
+ memcpy(&(temp_new_record[new_temp_offset]),&attr_end, attr_end_len);
+ new_temp_offset += attr_end_len ;
+ ntfs_debug("new_temp_offset [%d]",new_temp_offset);
+
+ } /****************************** end of $END *************/
+
+
+ /**FIXME : it do not support hard link **/
+ mrec->link_count = cpu_to_le16(1);
+ /** MUST set this **/
+ mrec->bytes_in_use = new_temp_offset;
+
+ flush_dcache_mft_record_page(ni);
+ mark_mft_record_dirty(ni);  /**ntfs_inode_mark_dirty(ni); int ntfs-3g**/
+ unmap_mft_record(ni);
+
+ (VFS_I(ni))->i_op = &ntfs_file_inode_ops;
+ (VFS_I(ni))->i_fop = &ntfs_file_ops;
+
+ if (NInoMstProtected(ni))
+ {
+ (VFS_I(ni))->i_mapping->a_ops = &ntfs_mst_aops;
+ }
+ else
+ {
+ (VFS_I(ni))->i_mapping->a_ops = &ntfs_normal_aops;
+ }
+
+ (VFS_I(ni))->i_blocks = ni->allocated_size >> 9;
+
+
+ ntfs_debug("Done.");
+ return ni;
+err_out:
+ ntfs_debug("Failed.");
+
+ /* TODO : if ni->nr_extents had been set  should been clear here **/
+
+ if(fn)
+ {
+ kfree(fn);
+ fn=NULL;
+ }
+ if(si)
+ {
+ kfree(si);
+ si=NULL;
+ }
+ if(sd)
+ {
+ kfree(sd);
+ sd=NULL;
+ }
+
+ if(mrec)
+ {
+ if (ntfs_mft_record_free(ni->vol, ni,mrec))
+ {
+ ntfs_debug("Failed to free MFT record.  "
+ "Leaving inconsistent metadata. Run chkdsk.\n");
+ }
+ inode_dec_link_count(VFS_I(ni)); //(VFS_I(ni))->i_nlink--;
+ unmap_mft_record(ni);
+ atomic_dec(&(VFS_I(ni))->i_count);
+ mrec=NULL;
+ }
+ return ERR_PTR(err);
+}
+
+/**
+ * Some wrappers around __ntfs_create() ...
+ */
+ntfs_inode *ntfs_create(ntfs_inode *dir_ni, ntfschar *name, u8 name_len,
+ dev_t type)
+{
+ /*TODO : type could be { S_IFREG S_IFDIR  S_IFIFO  S_IFSOCK } */
+ if (type != S_IFREG )
+ {
+ ntfs_error((VFS_I(dir_ni))->i_sb,"Invalid arguments.");
+ return ERR_PTR(-EOPNOTSUPP);
+ }
+ return __ntfs_create(dir_ni, name, name_len, type);
+}
+
+static int ntfs_create_inode(struct inode *dir,
+ struct dentry *dent,
+ umode_t mode,
+ bool pn )
+{
+ ntfschar *uname;
+ int uname_len;
+ ntfs_inode* ni ;
+
+ /* Convert the name of the dentry to Unicode. */
+ uname_len = ntfs_nlstoucs(NTFS_SB(dir->i_sb), dent->d_name.name, dent->d_name.len, &uname);
+ if (uname_len < 0)
+ {
+ if (uname_len != -ENAMETOOLONG)
+ {
+ ntfs_error(dir->i_sb, "Failed to convert name to Unicode.");
+ }
+ return uname_len;
+ }
+
+
+ /* create file and inode */
+ ni = ntfs_create(NTFS_I(dir), uname, uname_len , mode & S_IFMT  );
+ kmem_cache_free(ntfs_name_cache, uname);
+ if(likely(!IS_ERR(ni)))
+ {
+ d_instantiate(dent,VFS_I(ni));
+ /* TODO : modify    dir->i_mtime  to CURRENT_TIME */
+ ntfs_debug("Done.");
+ return 0;
+ }
+ else
+ {
+ ntfs_error(dir->i_sb, "ntfs_create error! dentry->d_name.name[%s]", dent->d_name.name);
+ return  PTR_ERR(ni);
+ }
+
+}
+
+/************************* unlink support **********/
+
+/*
+static int ntfs_index_rm_node(ntfs_index_context *icx)
+{
+ int entry_pos, pindex;
+ VCN vcn;
+ INDEX_BLOCK *ib = NULL;
+ INDEX_ENTRY *ie_succ, *ie, *entry = icx->entry;
+ INDEX_HEADER *ih;
+ u32 new_size;
+ int delta, ret = STATUS_ERROR;
+
+ ntfs_debug("Entering");
+
+ if (!icx->ia_na) {
+ icx->ia_na = ntfs_ia_open(icx, icx->ni);
+ if (!icx->ia_na)
+ return STATUS_ERROR;
+ }
+
+ ib = ntfs_malloc(icx->block_size);
+ if (!ib)
+ return STATUS_ERROR;
+
+ ie_succ = ntfs_ie_get_next(icx->entry);
+ entry_pos = icx->parent_pos[icx->pindex]++;
+ pindex = icx->pindex;
+descend:
+ vcn = ntfs_ie_get_vcn(ie_succ);
+ if (ntfs_ib_read(icx, vcn, ib))
+ goto out;
+
+ ie_succ = ntfs_ie_get_first(&ib->index);
+
+ if (ntfs_icx_parent_inc(icx))
+ goto out;
+
+ icx->parent_vcn[icx->pindex] = vcn;
+ icx->parent_pos[icx->pindex] = 0;
+
+ if ((ib->index.ih_flags & NODE_MASK) == INDEX_NODE)
+ goto descend;
+
+ if (ntfs_ih_zero_entry(&ib->index)) {
+ errno = EIO;
+ ntfs_log_perror("Empty index block");
+ goto out;
+ }
+
+ ie = ntfs_ie_dup(ie_succ);
+ if (!ie)
+ goto out;
+
+ if (ntfs_ie_add_vcn(&ie))
+ goto out2;
+
+ ntfs_ie_set_vcn(ie, ntfs_ie_get_vcn(icx->entry));
+
+ if (icx->is_in_root)
+ ih = &icx->ir->index;
+ else
+ ih = &icx->ib->index;
+
+ delta = le16_to_cpu(ie->length) - le16_to_cpu(icx->entry->length);
+ new_size = le32_to_cpu(ih->index_length) + delta;
+ if (delta > 0) {
+ if (icx->is_in_root) {
+ ret = ntfs_ir_make_space(icx, new_size);
+ if (ret != STATUS_OK)
+ goto out2;
+
+ ih = &icx->ir->index;
+ entry = ntfs_ie_get_by_pos(ih, entry_pos);
+
+ } else if (new_size > le32_to_cpu(ih->allocated_size)) {
+ icx->pindex = pindex;
+ ret = ntfs_ib_split(icx, icx->ib);
+ if (ret == STATUS_OK)
+ ret = STATUS_KEEP_SEARCHING;
+ goto out2;
+ }
+ }
+
+ ntfs_ie_delete(ih, entry);
+ ntfs_ie_insert(ih, ie, entry);
+
+ if (icx->is_in_root) {
+ if (ntfs_ir_truncate(icx, new_size))
+ goto out2;
+ } else
+ if (ntfs_icx_ib_write(icx))
+ goto out2;
+
+ ntfs_ie_delete(&ib->index, ie_succ);
+
+ if (ntfs_ih_zero_entry(&ib->index)) {
+ if (ntfs_index_rm_leaf(icx))
+ goto out2;
+ } else
+ if (ntfs_ib_write(icx, ib))
+ goto out2;
+
+ ret = STATUS_OK;
+out2:
+ free(ie);
+out:
+ free(ib);
+ return ret;
+}
+*/
+
+static int ntfs_ie_end(INDEX_ENTRY *ie)
+{
+    return ie->flags & INDEX_ENTRY_END || !ie->length;
+}
+
+static INDEX_ENTRY *ntfs_ie_get_first(INDEX_HEADER *ih)
+{
+    return (INDEX_ENTRY *)((u8 *)ih + le32_to_cpu(ih->entries_offset));
+}
+static INDEX_ENTRY *ntfs_ie_get_next(INDEX_ENTRY *ie)
+{
+    return (INDEX_ENTRY *)((char *)ie + le16_to_cpu(ie->length));
+}
+
+
+static void ntfs_ie_delete(INDEX_HEADER *ih, INDEX_ENTRY *ie)
+{
+ u32 new_size;
+ ntfs_debug("Entering");
+ new_size = le32_to_cpu(ih->index_length) - le16_to_cpu(ie->length);
+ ih->index_length = cpu_to_le32(new_size);
+ memmove(ie, (u8 *)ie + le16_to_cpu(ie->length), new_size - ((u8 *)ie - (u8 *)ih));
+ ntfs_debug("Done");
+}
+
+
+
+static u8 *ntfs_ie_get_end(INDEX_HEADER *ih)
+{
+ /* FIXME: check if it isn't overflowing the index block size */
+ return (u8 *)ih + le32_to_cpu(ih->index_length);
+}
+
+
+static int ntfs_ih_numof_entries(INDEX_HEADER *ih)
+{
+ int n;
+ INDEX_ENTRY *ie;
+ u8 *end;
+
+ ntfs_debug("Entering");
+
+ end = ntfs_ie_get_end(ih);
+ ie = ntfs_ie_get_first(ih);
+ for (n = 0; !ntfs_ie_end(ie) && (u8 *)ie < end; n++)
+ ie = ntfs_ie_get_next(ie);
+ return n;
+}
+
+static int ntfs_ih_one_entry(INDEX_HEADER *ih)
+{
+ return (ntfs_ih_numof_entries(ih) == 1);
+}
+/**
+ * ntfs_index_rm - remove entry from the index
+ * @icx: index context describing entry to delete
+ *
+ * Delete entry described by @icx from the index. Index context is always
+ * reinitialized after use of this function, so it can be used for index
+ * lookup once again.
+ *
+ * Return 0 on success or -1 on error with errno set to the error code.
+ */
+static int ntfs_index_rm(ntfs_index_context *icx)
+{
+ INDEX_HEADER *ih;
+ int ret = STATUS_OK;
+
+ ntfs_debug("Entering");
+
+ if (!icx || (!icx->ia && !icx->ir) || ntfs_ie_end(icx->entry))
+ {
+ ntfs_debug("Invalid arguments.");
+ ret = -EINVAL;
+ goto err_out;
+ }
+ if (icx->is_in_root)
+ {
+ ih = &icx->ir->index;
+ }
+ else
+ {
+ ih = &icx->ia->index;
+ }
+
+ if (icx->entry->flags & INDEX_ENTRY_NODE)
+ {
+ /* not support
+ ret = ntfs_index_rm_node(icx);
+ */
+ ret =  -EOPNOTSUPP ;
+ goto err_out;
+ }
+ else if (icx->is_in_root || !ntfs_ih_one_entry(ih))
+ {
+ ntfs_ie_delete(ih, icx->entry);
+
+ if (icx->is_in_root)
+ {
+ ret = ntfs_ir_truncate(icx, le32_to_cpu(ih->index_length));
+ if (ret != STATUS_OK)
+ {
+ goto err_out;
+ }
+ ntfs_debug("Before flush_dcache_mft_record_page ");
+ flush_dcache_mft_record_page(icx->actx->ntfs_ino);
+
+ ntfs_debug("Before mark_mft_record_dirty ");
+ mark_mft_record_dirty(icx->actx->ntfs_ino);
+ }
+ else
+ {
+ /* shut by Gzged
+ if (ntfs_icx_ib_write(icx))
+ {
+ goto err_out;
+ }
+ */
+ ntfs_debug("Before ntfs_index_entry_flush_dcache_page ");
+ ntfs_index_entry_flush_dcache_page(icx);
+ ntfs_debug("Before ntfs_index_entry_mark_dirty ");
+ ntfs_index_entry_mark_dirty(icx);
+ }
+ }
+ else
+ {
+ ret =  -EOPNOTSUPP ;
+ goto err_out;
+ /** not support yet
+ if (ntfs_index_rm_leaf(icx))
+ {
+ goto err_out;
+ }
+ **/
+ }
+
+
+err_out:
+ ntfs_debug("Done ");
+ return ret;
+}
+/** 20091014 **/
+int ntfs_index_remove(ntfs_inode *ni, const void *key, const int keylen)
+{
+ int ret = STATUS_ERROR;
+ ntfs_index_context *icx;
+
+ icx = ntfs_index_ctx_get(ni);
+ if (!icx)
+ {
+ return -1;
+ }
+
+ while (1)
+ {
+ /** use my func
+ if (ntfs_index_lookup(key, keylen, icx))
+ */
+ if ( (ret = ntfs_lookup_inode_by_key (key, keylen, icx) ) )
+ {
+ ntfs_debug("ntfs_lookup_inode_by_key faild ...");
+ goto err_out;
+ }
+/*********debug print
+ {
+ struct qstr nls_name;
+ nls_name.name = NULL;
+ nls_name.len = (unsigned)ntfs_ucstonls(ni->vol,
+                ((FILE_NAME_ATTR *)icx->data)->file_name , icx->data_len,
+                (unsigned char**)&nls_name.name, 0);
+ ntfs_debug("icx data name=[%s]",nls_name.name);
+ kfree(nls_name.name);
+ }
+
+ if (((FILE_NAME_ATTR *)icx->data)->file_attributes & FILE_ATTR_REPARSE_POINT)
+ {
+ ntfs_debug("not support faild ");
+ ret = -EOPNOTSUPP;
+ goto err_out;
+ } ********/
+/*********debug print ********/
+
+ ret = ntfs_index_rm(icx);
+ if (ret == STATUS_OK)
+ {
+ ntfs_debug("ntfs_index_rm Done");
+ break;
+ }
+ else
+ {
+ ntfs_debug("ntfs_index_rm faild");
+ goto err_out;
+ }
+ /*
+ flush_dcache_mft_record_page(icx->actx->ntfs_ino);
+ mark_mft_record_dirty(icx->actx->ntfs_ino);
+ */
+ /*FIXME:Gzged change
+ ntfs_inode_mark_dirty(icx->actx->ntfs_ino);
+ ntfs_index_ctx_reinit(icx);
+ ***************/
+        ntfs_index_ctx_put(icx);
+ ntfs_index_ctx_get(ni);
+ }
+
+ /*
+ ntfs_debug("Before flush_dcache_mft_record_page ");
+ flush_dcache_mft_record_page(icx->actx->ntfs_ino);
+ ntfs_debug("Before mark_mft_record_dirty ");
+ mark_mft_record_dirty(icx->actx->ntfs_ino);
+ */
+ /*
+ ntfs_debug("Before ntfs_index_entry_flush_dcache_page ");
+ ntfs_index_entry_flush_dcache_page(icx);
+ ntfs_debug("Before ntfs_index_entry_mark_dirty ");
+ ntfs_index_entry_mark_dirty(icx);
+ */
+
+err_out:
+ ntfs_debug("Delete Done");
+ if(icx)
+ {
+ ntfs_index_ctx_put(icx);
+ }
+ return ret;
+}
+
+static __inline__ int ntfs_attrs_walk(ntfs_attr_search_ctx *ctx)
+{
+ /** case all attr **/
+    return ntfs_attr_lookup(0, NULL, 0, CASE_SENSITIVE, 0, NULL, 0, ctx);
+}
+
+
+//static const char *es = "  Leaving inconsistent metadata.  Unmount and run chkdsk.";
+typedef bool BOOL;
+#include "lcnalloc.h"
+int ntfs_delete(ntfs_inode *ni, ntfs_inode *dir_ni )
+{
+ ntfs_attr_search_ctx *actx = NULL;
+ MFT_RECORD* mrec;
+ FILE_NAME_ATTR *fn = NULL;
+ ntfs_volume* vol=ni->vol;
+ /*
+ BOOL looking_for_dos_name = FALSE, looking_for_win32_name = FALSE;
+ BOOL case_sensitive_match = TRUE;
+ */
+ int err = 0;
+
+ ntfs_debug("Entering");
+
+ mrec = map_mft_record(ni);
+ if (IS_ERR(mrec)) {
+ err = PTR_ERR(mrec);
+ mrec = NULL;
+ goto err_out;
+ }
+
+    if ( (mrec->flags & MFT_RECORD_IS_DIRECTORY) )
+ {
+ ntfs_debug("Invalid arguments.");
+ err=  -EINVAL;
+ goto err_out;
+ }
+
+ if (!ni || !dir_ni )
+ {
+ ntfs_debug("Invalid arguments.");
+ err=  -EINVAL;
+ goto err_out;
+ }
+
+ if (ni->nr_extents == -1)
+ ni = ni->ext.base_ntfs_ino;
+ if (dir_ni->nr_extents == -1)
+ dir_ni = dir_ni->ext.base_ntfs_ino;
+
+
+/******************************************* get fn ******************/
+ /*
+ * Search for FILE_NAME attribute with such name. If it's in POSIX or
+ * WIN32_AND_DOS namespace, then simply remove it from index and inode.
+ * If filename in DOS or in WIN32 namespace, then remove DOS name first,
+ * only then remove WIN32 name.
+ */
+ actx = ntfs_attr_get_search_ctx(ni, mrec);
+ if (!actx)
+ {
+ goto err_out;
+ }
+ while (!ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, CASE_SENSITIVE,
+ 0, NULL, 0, actx)) {
+/*debuger need...
+ char *s;
+ **/
+ BOOL case_sensitive = IGNORE_CASE;
+
+ fn = (FILE_NAME_ATTR*)((u8*)actx->attr +
+ le16_to_cpu(actx->attr->data.resident.value_offset));
+/*debuger need...
+ s = ntfs_attr_name_get(fn->file_name, fn->file_name_length);
+ ntfs_debug("name: '%s'  type: %d  dos: %d  win32: %d  "
+       "case: %d\n", s, fn->file_name_type,
+       looking_for_dos_name, looking_for_win32_name,
+       case_sensitive_match);
+ ntfs_attr_name_free(&s);
+ */
+/** all ways use posix name
+ if (looking_for_dos_name) {
+ if (fn->file_name_type == FILE_NAME_DOS)
+ break;
+ else
+ continue;
+ }
+ if (looking_for_win32_name) {
+ if  (fn->file_name_type == FILE_NAME_WIN32)
+ break;
+ else
+ continue;
+ }
+ **/
+
+ /* Ignore hard links from other directories */
+ if (dir_ni->mft_no != MREF_LE(fn->parent_directory)) {
+ ntfs_debug("MFT record numbers don't match "
+       "(%llu != %llu)",
+       (long long unsigned)dir_ni->mft_no,
+       (long long unsigned)MREF_LE(fn->parent_directory));
+ continue;
+ }
+    
+/****all ways use posix case
+ if (fn->file_name_type == FILE_NAME_POSIX || case_sensitive_match)
+ */
+ case_sensitive = CASE_SENSITIVE;
+
+ /** all ways think name is equal
+ if (ntfs_names_are_equal(fn->file_name, fn->file_name_length,
+ name, name_len, case_sensitive,
+ ni->vol->upcase, ni->vol->upcase_len))*/ {
+
+ /**  all ways think it`s posix name...
+ if (fn->file_name_type == FILE_NAME_WIN32) {
+ looking_for_dos_name = TRUE;
+ ntfs_attr_reinit_search_ctx(actx);
+ continue;
+ }
+ if (fn->file_name_type == FILE_NAME_DOS)
+ looking_for_dos_name = TRUE;
+ */
+ break;
+ }
+ }
+/******************************************* get fn ******************/
+
+/*********** cut the entry down *****************/
+ if ( (err = ntfs_index_remove(dir_ni, fn, le32_to_cpu(actx->attr->data.resident.value_length)) ) )
+ {
+ ntfs_debug("ntfs_index_remove error.");
+ goto err_out;
+ }
+
+ mrec->link_count = cpu_to_le16(le16_to_cpu( mrec->link_count) - 1);
+/*********** cut the entry down *****************/
+
+
+/********************flush ***************/
+ flush_dcache_mft_record_page(ni);
+ mark_mft_record_dirty(ni);
+/********************flush end ***************/
+
+/********************cut down all run list ***************/
+ ntfs_attr_put_search_ctx(actx);
+ actx = ntfs_attr_get_search_ctx(ni, mrec);
+
+ err=  STATUS_OK ;
+ ntfs_debug("Before while ");
+ while (!ntfs_attrs_walk(actx))
+ {
+ ntfs_debug("new loop ");
+ if (actx->attr->non_resident)
+ {
+ ntfs_debug("Inner case ");
+ /*
+ runlist *rl;
+ rl = ntfs_mapping_pairs_decompress(ni->vol, actx->attr,
+ NULL);
+ if (!rl) {
+ err = -EOPNOTSUPP ;
+ ntfs_debug("Failed to decompress runlist.  "
+ "Leaving inconsistent metadata.\n");
+ continue;
+ }
+ if (ntfs_cluster_free_from_rl(ni->vol, rl)) {
+ err = -EOPNOTSUPP ;
+ ntfs_debug("Failed to free clusters.  "
+ "Leaving inconsistent metadata.\n");
+ continue;
+ }
+ free(rl);
+ */
+
+ /* alloc_change < 0 */
+ /* Free the clusters.
+ err = ntfs_cluster_free(ni, 0, -1, actx);*/
+
+ ntfs_debug("before __ntfs_cluster_free ");
+ err = __ntfs_cluster_free(ni, 0, -1, actx, false);
+ if (unlikely(err < 0))
+ {
+ ntfs_error(vol->sb, "Failed to release cluster(s) (error code "
+ "%lli).  Unmount and run chkdsk to recover "
+ "the lost cluster(s).", (long long)err);
+ NVolSetErrors(vol);
+ err = 0;
+ }
+ /* Truncate the runlist.  NO NEED
+ err = ntfs_rl_truncate_nolock(vol, &ni->runlist, 0);
+ if (unlikely(err || IS_ERR(actx->mrec)))
+ {
+ ntfs_error(vol->sb, "Failed to %s (error code %li).%s",
+ IS_ERR(actx->mrec) ?
+ "restore attribute search context" :
+ "truncate attribute runlist",
+ IS_ERR(actx->mrec) ? PTR_ERR(actx->mrec) : err, es);
+ err = -EIO;
+ goto err_out;
+ }*/
+ ntfs_debug("before flush_dcache_mft_record_page actx->ntfs_ino[%p]",actx->ntfs_ino);
+ flush_dcache_mft_record_page(actx->ntfs_ino);
+ ntfs_debug("before mark_mft_record_dirty actx->ntfs_ino[%p]",actx->ntfs_ino);
+ mark_mft_record_dirty(actx->ntfs_ino);
+ }
+ }
+ if (err )
+ {
+ ntfs_debug("Attribute enumeration failed.  "
+ "Probably leaving inconsistent metadata.\n");
+ }
+ /* All extents should be attached after attribute walk. */
+ while (ni->nr_extents)
+ {
+ ntfs_error(vol->sb,"need use ntfs_extent_mft_record_free. not support now ");
+ /**FIXME
+ if ( ( err = ntfs_mft_record_free(ni->vol, *(ni->extent_nis) )))
+ {
+ ntfs_debug("Failed to free extent MFT record.  "
+ "Leaving inconsistent metadata.\n");
+ }
+ **/
+ }
+
+ if (ntfs_mft_record_free(ni->vol, ni,mrec))
+ {
+ err = -EIO;
+ ntfs_debug("Failed to free base MFT record.  "
+ "Leaving inconsistent metadata.\n");
+ }
+ /* FIXME */
+
+ flush_dcache_mft_record_page(ni);
+ mark_mft_record_dirty(ni);
+ /*FIXME:Gzged add */
+ ntfs_debug("before unmap_mft_record.");
+ unmap_mft_record(ni);
+ ni = NULL;
+ mrec=NULL;
+
+
+ /** Gzged shut now
+ ntfs_inode_update_times(dir_ni, NTFS_UPDATE_MCTIME);
+ */
+err_out:
+ if (actx)
+ ntfs_attr_put_search_ctx(actx);
+ if (mrec)
+ unmap_mft_record(ni);
+ if (err)
+ {
+ ntfs_debug("Could not delete file");
+ return err;
+ }
+ else
+ {
+ ntfs_debug("Done.");
+ return 0;
+ }
+}
+
+static int ntfs_unlink_inode(struct inode *pi,struct dentry *pd)
+{
+ ntfs_inode* ni = NTFS_I(pd->d_inode);
+ int err = -ENOENT;
+
+ ntfs_debug("Entering");
+ BUG_ON(!ni);
+
+ err =   ntfs_delete(ni,NTFS_I(pi));
+ if(err)
+ {
+ ntfs_debug("Faile");
+ return err;
+ }
+ else
+ {
+ (VFS_I(ni))->i_ctime = pi->i_ctime;
+ inode_dec_link_count(VFS_I(ni)); //(VFS_I(ni))->i_nlink--;
+ ntfs_debug("Done");
+ return  err;
+ }
+}
 /**
  * Inode operations for directories.
  */
 const struct inode_operations ntfs_dir_inode_ops = {
  .lookup = ntfs_lookup, /* VFS: Lookup directory. */
+ .create = ntfs_create_inode,
+ .unlink = ntfs_unlink_inode,
 };
 
 /**
--
2.9.3


------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Linux-NTFS-Dev mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/linux-ntfs-dev
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: [PATCH] add simple create and unlink(aka rm)

高志刚
Hi Anton:
    Though here are only little new features, this patch still contain thousands lines.  So I prefer to send it to NTFS team first, not the big kernel mail list.
    I am rather new for submitting patch to kernel, any suggestion is welcome.  On the other side, this patch comes from the work what I did about 7 years ago. I am rather surprised when I found that the kernel still doesn't have this feature today.


Best regard
Gordon

> 在 2017年7月3日,10:53,[hidden email] 写道:
>
> From: Zhigang Gao <gzg1984@icloud .com>
>
> ---
> fs/ntfs/Makefile |   25 +-
> fs/ntfs/aops.h   |    2 +
> fs/ntfs/attrib.c |   26 +-
> fs/ntfs/dir.c    |  602 ++++++++++++++++++++++++
> fs/ntfs/file.c   |    1 +
> fs/ntfs/index.h  |    1 +
> fs/ntfs/mft.c    |   80 ++++
> fs/ntfs/mft.h    |    1 +
> fs/ntfs/namei.c  | 1364 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
> 9 files changed, 2097 insertions(+), 5 deletions(-)
>
> diff --git a/fs/ntfs/Makefile b/fs/ntfs/Makefile
> index 2ff263e..3edb4e2 100644
> --- a/fs/ntfs/Makefile
> +++ b/fs/ntfs/Makefile
> @@ -1,14 +1,33 @@
> # Rules for making the NTFS driver.
> -
> +ifneq ($(KERNELRELEASE),)
> obj-$(CONFIG_NTFS_FS) += ntfs.o
>
> ntfs-y := aops.o attrib.o collate.o compress.o debug.o dir.o file.o \
> -      index.o inode.o mft.o mst.o namei.o runlist.o super.o sysctl.o \
> -      unistr.o upcase.o
> +          index.o inode.o mft.o mst.o namei.o runlist.o super.o sysctl.o \
> +          unistr.o upcase.o
>
> ntfs-$(CONFIG_NTFS_RW) += bitmap.o lcnalloc.o logfile.o quota.o usnjrnl.o
>
> ccflags-y := -DNTFS_VERSION=\"2.1.32\"
> ccflags-$(CONFIG_NTFS_DEBUG)    += -DDEBUG
> ccflags-$(CONFIG_NTFS_RW)    += -DNTFS_RW
> +else
> +
> +KERNEL ?= /lib/modules/`uname -r`/build
> +
> +default:
> +    CONFIG_NTFS_FS=m CONFIG_NTFS_RW=y CONFIG_NTFS_DEBUG=y $(MAKE) -C $(KERNEL) M=$$PWD
> +
> +
> +.PHONY : install help clean
> +help:
> +    $(MAKE) -C $(KERNEL) M=$$PWD help
> +
> +install : default
> +    $(MAKE) -C $(KERNEL) M=$$PWD modules_install
> +    depmod -A
> +
> +clean:
> +    make -C $(KERNEL) M=`pwd` clean
>
> +endif
> diff --git a/fs/ntfs/aops.h b/fs/ntfs/aops.h
> index 820d6ea..4805986 100644
> --- a/fs/ntfs/aops.h
> +++ b/fs/ntfs/aops.h
> @@ -39,8 +39,10 @@
>  */
> static inline void ntfs_unmap_page(struct page *page)
> {
> +    ntfs_debug("Entering .");
>    kunmap(page);
>    put_page(page);
> +    ntfs_debug("done .");
> }
>
> /**
> diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c
> index 44a39a0..0092d14 100644
> --- a/fs/ntfs/attrib.c
> +++ b/fs/ntfs/attrib.c
> @@ -599,20 +599,41 @@ static int ntfs_attr_find(const ATTR_TYPE type, const ntfschar *name,
>     * Iterate over attributes in mft record starting at @ctx->attr, or the
>     * attribute following that, if @ctx->is_first is 'true'.
>     */
> +    ntfs_debug("Entering. type=[0x%x] is_first[%d]", type,ctx->is_first);
>    if (ctx->is_first) {
>        a = ctx->attr;
>        ctx->is_first = false;
> -    } else
> +    }
> +    else
> +    {
>        a = (ATTR_RECORD*)((u8*)ctx->attr +
>                le32_to_cpu(ctx->attr->length));
> +    }
> +
>    for (;;    a = (ATTR_RECORD*)((u8*)a + le32_to_cpu(a->length))) {
>        if ((u8*)a < (u8*)ctx->mrec || (u8*)a > (u8*)ctx->mrec +
>                le32_to_cpu(ctx->mrec->bytes_allocated))
> +        {
> +            ntfs_error(vol->sb, "IN LOOP breaker."
> +                        "type=[0x%x],a->type[0x%x],a->length[%d],"
> +                        "a[%p],ctx->mrec[%p],ctx->mrec->bytes_allocated[%d]"
> +                        ,type,a->type,a->length,
> +                        a,ctx->mrec,le32_to_cpu(ctx->mrec->bytes_allocated));
>            break;
> +        }
>        ctx->attr = a;
> +        if (type != AT_UNUSED)
> +        {
>        if (unlikely(le32_to_cpu(a->type) > le32_to_cpu(type) ||
>                a->type == AT_END))
>            return -ENOENT;
> +        }
> +        else
> +        {
> +            if ( a->type == AT_END)
> +                return -ENOENT;
> +        }
> +
>        if (unlikely(!a->length))
>            break;
>        if (a->type != type)
> @@ -1434,6 +1455,8 @@ int ntfs_attr_can_be_resident(const ntfs_volume *vol, const ATTR_TYPE type)
>  */
> int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size)
> {
> +    BUG_ON(!m);
> +    BUG_ON(!a);
>    ntfs_debug("Entering for new_size %u.", new_size);
>    /* Align to 8 bytes if it is not already done. */
>    if (new_size & 7)
> @@ -1455,6 +1478,7 @@ int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size)
>        if (new_size >= offsetof(ATTR_REC, length) + sizeof(a->length))
>            a->length = cpu_to_le32(new_size);
>    }
> +    ntfs_debug("Done.");
>    return 0;
> }
>
> diff --git a/fs/ntfs/dir.c b/fs/ntfs/dir.c
> index 0ee19ec..caf57c15 100644
> --- a/fs/ntfs/dir.c
> +++ b/fs/ntfs/dir.c
> @@ -77,6 +77,608 @@ ntfschar I30[5] = { cpu_to_le16('$'), cpu_to_le16('I'),
>  *           removes them again after the write is complete after which it
>  *           unlocks the page.
>  */
> +#include "index.h"
> +#include "layout.h"
> +#include "types.h"
> +
> +/* Author: Gzged
> + * Caller is in namei.c
> + */
> +int ntfs_lookup_inode_by_key (const void *key, const int key_len, ntfs_index_context *ictx)
> +{
> +    ntfs_inode* dir_ni = ictx->idx_ni ;
> +    const ntfschar* uname = ((FILE_NAME_ATTR *)key)->file_name ;
> +    const int uname_len = ((FILE_NAME_ATTR *)key)->file_name_length;
> +    
> +    ntfs_volume *vol = dir_ni->vol;
> +    struct super_block *sb = vol->sb;
> +    MFT_RECORD *m;
> +    INDEX_ROOT *ir;
> +    INDEX_ENTRY *ie;
> +    INDEX_ALLOCATION *ia;
> +    u8 *index_end;
> +    /*
> +    u64 mref;
> +    */
> +    ntfs_attr_search_ctx *ctx;
> +    int err, rc;
> +    VCN vcn, old_vcn;
> +    struct address_space *ia_mapping;
> +    struct page *page;
> +    u8 *kaddr;
> +
> +    ntfs_debug("Entering.");
> +    BUG_ON(!S_ISDIR(VFS_I(dir_ni)->i_mode));
> +    BUG_ON(NInoAttr(dir_ni));
> +    /* Get hold of the mft record for the directory. */
> +    m = map_mft_record(dir_ni);
> +    if (IS_ERR(m))
> +    {
> +        ntfs_error(sb, "map_mft_record() failed with error code %ld.",
> +                -PTR_ERR(m));
> +        return ERR_MREF(PTR_ERR(m));
> +    }
> +    ctx = ntfs_attr_get_search_ctx(dir_ni, m);
> +    if (unlikely(!ctx)) {
> +        err = -ENOMEM;
> +        goto err_out;
> +    }
> +    /* Find the index root attribute in the mft record. */
> +    err = ntfs_attr_lookup(AT_INDEX_ROOT, I30, 4, CASE_SENSITIVE, 0, NULL,
> +            0, ctx);
> +    if (unlikely(err)) {
> +        if (err == -ENOENT) {
> +            ntfs_error(sb, "Index root attribute missing in "
> +                    "directory inode 0x%lx.",
> +                    dir_ni->mft_no);
> +            err = -EIO;
> +        }
> +        goto err_out;
> +    }
> +    /* Get to the index root value (it's been verified in read_inode). */
> +    ir = (INDEX_ROOT*)((u8*)ctx->attr +
> +            le16_to_cpu(ctx->attr->data.resident.value_offset));
> +    index_end = (u8*)&ir->index + le32_to_cpu(ir->index.index_length);
> +    /* The first index entry. */
> +    ie = (INDEX_ENTRY*)((u8*)&ir->index +
> +            le32_to_cpu(ir->index.entries_offset));
> +    /*
> +     * Loop until we exceed valid memory (corruption case) or until we
> +     * reach the last entry.
> +     */
> +    for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) {
> +        /* Bounds checks. */
> +        ictx->is_in_root = true;
> +        ictx->ir = ir;
> +        ictx->entry = ie;
> +        ictx->base_ni = dir_ni;
> +        ictx->actx = ctx;
> +        ictx->ia = NULL;
> +        ictx->page = NULL;
> +
> +        if ((u8*)ie < (u8*)ctx->mrec || (u8*)ie +
> +                sizeof(INDEX_ENTRY_HEADER) > index_end ||
> +                (u8*)ie + le16_to_cpu(ie->key_length) >
> +                index_end)
> +            goto dir_err_out;
> +        /*
> +         * The last entry cannot contain a name. It can however contain
> +         * a pointer to a child node in the B+tree so we just break out.
> +         */
> +        if (ie->flags & INDEX_ENTRY_END)
> +            break;
> +        /*
> +         * We perform a case sensitive comparison and if that matches
> +         * we are done and return the mft reference of the inode (i.e.
> +         * the inode number together with the sequence number for
> +         * consistency checking). We convert it to cpu format before
> +         * returning.
> +         */
> +
> +        if (ntfs_are_names_equal(uname, uname_len,
> +                (ntfschar*)&ie->key.file_name.file_name,
> +                ie->key.file_name.file_name_length,
> +                CASE_SENSITIVE, vol->upcase, vol->upcase_len)) {
> +found_it:
> +            /*
> +             * We have a perfect match, so we don't need to care
> +             * about having matched imperfectly before, so we can
> +             * free name and set *res to NULL.
> +             * However, if the perfect match is a short file name,
> +             * we need to signal this through *res, so that
> +             * ntfs_lookup() can fix dcache aliasing issues.
> +             * As an optimization we just reuse an existing
> +             * allocation of *res.
> +             */
> +            /*FIXME:Shut for now and maybe work in future. Author:Gzged
> +            if (ie->key.file_name.file_name_type == FILE_NAME_DOS) {
> +                if (!name) {
> +                    name = kmalloc(sizeof(ntfs_name),
> +                            GFP_NOFS);
> +                    if (!name) {
> +                        err = -ENOMEM;
> +                        goto err_out;
> +                    }
> +                }
> +                name->mref = le64_to_cpu(
> +                        ie->data.dir.indexed_file);
> +                name->type = FILE_NAME_DOS;
> +                name->len = 0;
> +                *res = name;
> +            } else {
> +                kfree(name);
> +                *res = NULL;
> +            }
> +            */
> +            /*FIXME: Gzged mod
> +            mref = le64_to_cpu(ie->data.dir.indexed_file);
> +            ntfs_attr_put_search_ctx(ctx);
> +            unmap_mft_record(dir_ni);
> +            return mref;
> +            */
> +
> +done:
> +            ictx->data = (u8*)ie +
> +                    le16_to_cpu(ie->data.vi.data_offset);
> +            ictx->data_len = le16_to_cpu(ie->data.vi.data_length);
> +            ntfs_debug("Done.");
> +            return err;
> +
> +        }
> +        /*
> +         * For a case insensitive mount, we also perform a case
> +         * insensitive comparison (provided the file name is not in the
> +         * POSIX namespace). If the comparison matches, and the name is
> +         * in the WIN32 namespace, we cache the filename in *res so
> +         * that the caller, ntfs_lookup(), can work on it. If the
> +         * comparison matches, and the name is in the DOS namespace, we
> +         * only cache the mft reference and the file name type (we set
> +         * the name length to zero for simplicity).
> +         */
> +//        if (!NVolCaseSensitive(vol) &&
> +//                ie->key.file_name.file_name_type &&
> +//                ntfs_are_names_equal(uname, uname_len,
> +//                (ntfschar*)&ie->key.file_name.file_name,
> +//                ie->key.file_name.file_name_length,
> +//                IGNORE_CASE, vol->upcase, vol->upcase_len)) {
> +//            int name_size = sizeof(ntfs_name);
> +//            u8 type = ie->key.file_name.file_name_type;
> +//            u8 len = ie->key.file_name.file_name_length;
> +//
> +//            /* Only one case insensitive matching name allowed. */
> +//            if (name) {
> +//                ntfs_error(sb, "Found already allocated name "
> +//                        "in phase 1. Please run chkdsk "
> +//                        "and if that doesn't find any "
> +//                        "errors please report you saw "
> +//                        "this message to "
> +//                        "linux-ntfs-dev@lists."
> +//                        "sourceforge.net.");
> +//                goto dir_err_out;
> +//            }
> +//
> +//            if (type != FILE_NAME_DOS)
> +//                name_size += len * sizeof(ntfschar);
> +//            name = kmalloc(name_size, GFP_NOFS);
> +//            if (!name) {
> +//                err = -ENOMEM;
> +//                goto err_out;
> +//            }
> +//            name->mref = le64_to_cpu(ie->data.dir.indexed_file);
> +//            name->type = type;
> +//            if (type != FILE_NAME_DOS) {
> +//                name->len = len;
> +//                memcpy(name->name, ie->key.file_name.file_name,
> +//                        len * sizeof(ntfschar));
> +//            } else
> +//                name->len = 0;
> +//            *res = name;
> +//        }
> +        /*
> +         * Not a perfect match, need to do full blown collation so we
> +         * know which way in the B+tree we have to go.
> +         */
> +        rc = ntfs_collate_names(uname, uname_len,
> +                (ntfschar*)&ie->key.file_name.file_name,
> +                ie->key.file_name.file_name_length, 1,
> +                IGNORE_CASE, vol->upcase, vol->upcase_len);
> +        /*
> +         * If uname collates before the name of the current entry, there
> +         * is definitely no such name in this index but we might need to
> +         * descend into the B+tree so we just break out of the loop.
> +         */
> +        if (rc == -1)
> +            break;
> +        /* The names are not equal, continue the search. */
> +        if (rc)
> +            continue;
> +        /*
> +         * Names match with case insensitive comparison, now try the
> +         * case sensitive comparison, which is required for proper
> +         * collation.
> +         */
> +        rc = ntfs_collate_names(uname, uname_len,
> +                (ntfschar*)&ie->key.file_name.file_name,
> +                ie->key.file_name.file_name_length, 1,
> +                CASE_SENSITIVE, vol->upcase, vol->upcase_len);
> +        if (rc == -1)
> +            break;
> +        if (rc)
> +            continue;
> +        /*
> +         * Perfect match, this will never happen as the
> +         * ntfs_are_names_equal() call will have gotten a match but we
> +         * still treat it correctly.
> +         */
> +        goto found_it;
> +    }
> +    /*
> +     * We have finished with this index without success. Check for the
> +     * presence of a child node and if not present return -ENOENT, unless
> +     * we have got a matching name cached in name in which case return the
> +     * mft reference associated with it.
> +     */
> +    if (!(ie->flags & INDEX_ENTRY_NODE)) {
> +        /* FIXME:Gzged shut
> +        if (name) {
> +            ntfs_attr_put_search_ctx(ctx);
> +            unmap_mft_record(dir_ni);
> +            return name->mref;
> +        }
> +        ********/
> +        ntfs_debug("Entry not found. ictx->is_in_root[%d]",ictx->is_in_root);
> +
> +        /*FIXME:Gzged set
> +        ictx->is_in_root = true;
> +        ictx->ir = ir;
> +        ictx->entry = ie;
> +        ictx->base_ni = dir_ni;
> +        ictx->actx = ctx;
> +        ictx->ia = NULL;
> +        ictx->page = NULL;
> +        */
> +        /*FIXME: Gzged mod ; do not release actx ....and so on
> +        goto err_out;
> +        */
> +        return -ENOENT;
> +    } /* Child node present, descend into it. */
> +    /* Consistency check: Verify that an index allocation exists. */
> +    if (!NInoIndexAllocPresent(dir_ni)) {
> +        ntfs_error(sb, "No index allocation attribute but index entry "
> +                "requires one. Directory inode 0x%lx is "
> +                "corrupt or driver bug.", dir_ni->mft_no);
> +        goto err_out;
> +    }
> +    /* Get the starting vcn of the index_block holding the child node. */
> +    vcn = sle64_to_cpup((sle64*)((u8*)ie + le16_to_cpu(ie->length) - 8));
> +    ia_mapping = VFS_I(dir_ni)->i_mapping;
> +    /*
> +     * We are done with the index root and the mft record. Release them,
> +     * otherwise we deadlock with ntfs_map_page().
> +     */
> +    ntfs_attr_put_search_ctx(ctx);
> +    unmap_mft_record(dir_ni);
> +    m = NULL;
> +    ctx = NULL;
> +descend_into_child_node:
> +    /*
> +     * Convert vcn to index into the index allocation attribute in units
> +     * of PAGE_SIZE and map the page cache page, reading it from
> +     * disk if necessary.
> +     */
> +    page = ntfs_map_page(ia_mapping, vcn <<
> +            dir_ni->itype.index.vcn_size_bits >> PAGE_SHIFT);
> +    if (IS_ERR(page)) {
> +        ntfs_error(sb, "Failed to map directory index page, error %ld.",
> +                -PTR_ERR(page));
> +        err = PTR_ERR(page);
> +        goto err_out;
> +    }
> +    lock_page(page);
> +    kaddr = (u8*)page_address(page);
> +fast_descend_into_child_node:
> +    /* Get to the index allocation block. */
> +    ia = (INDEX_ALLOCATION*)(kaddr + ((vcn <<
> +            dir_ni->itype.index.vcn_size_bits) & ~PAGE_MASK));
> +    /* Bounds checks. */
> +    if ((u8*)ia < kaddr || (u8*)ia > kaddr + PAGE_SIZE) {
> +        ntfs_error(sb, "Out of bounds check failed. Corrupt directory "
> +                "inode 0x%lx or driver bug.", dir_ni->mft_no);
> +        goto unm_err_out;
> +    }
> +    /* Catch multi sector transfer fixup errors. */
> +    if (unlikely(!ntfs_is_indx_record(ia->magic))) {
> +        ntfs_error(sb, "Directory index record with vcn 0x%llx is "
> +                "corrupt.  Corrupt inode 0x%lx.  Run chkdsk.",
> +                (unsigned long long)vcn, dir_ni->mft_no);
> +        goto unm_err_out;
> +    }
> +    if (sle64_to_cpu(ia->index_block_vcn) != vcn) {
> +        ntfs_error(sb, "Actual VCN (0x%llx) of index buffer is "
> +                "different from expected VCN (0x%llx). "
> +                "Directory inode 0x%lx is corrupt or driver "
> +                "bug.", (unsigned long long)
> +                sle64_to_cpu(ia->index_block_vcn),
> +                (unsigned long long)vcn, dir_ni->mft_no);
> +        goto unm_err_out;
> +    }
> +    if (le32_to_cpu(ia->index.allocated_size) + 0x18 !=
> +            dir_ni->itype.index.block_size) {
> +        ntfs_error(sb, "Index buffer (VCN 0x%llx) of directory inode "
> +                "0x%lx has a size (%u) differing from the "
> +                "directory specified size (%u). Directory "
> +                "inode is corrupt or driver bug.",
> +                (unsigned long long)vcn, dir_ni->mft_no,
> +                le32_to_cpu(ia->index.allocated_size) + 0x18,
> +                dir_ni->itype.index.block_size);
> +        goto unm_err_out;
> +    }
> +    index_end = (u8*)ia + dir_ni->itype.index.block_size;
> +    if (index_end > kaddr + PAGE_SIZE) {
> +        ntfs_error(sb, "Index buffer (VCN 0x%llx) of directory inode "
> +                "0x%lx crosses page boundary. Impossible! "
> +                "Cannot access! This is probably a bug in the "
> +                "driver.", (unsigned long long)vcn,
> +                dir_ni->mft_no);
> +        goto unm_err_out;
> +    }
> +    index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length);
> +    if (index_end > (u8*)ia + dir_ni->itype.index.block_size) {
> +        ntfs_error(sb, "Size of index buffer (VCN 0x%llx) of directory "
> +                "inode 0x%lx exceeds maximum size.",
> +                (unsigned long long)vcn, dir_ni->mft_no);
> +        goto unm_err_out;
> +    }
> +    /* The first index entry. */
> +    ie = (INDEX_ENTRY*)((u8*)&ia->index +
> +            le32_to_cpu(ia->index.entries_offset));
> +    /*
> +     * Iterate similar to above big loop but applied to index buffer, thus
> +     * loop until we exceed valid memory (corruption case) or until we
> +     * reach the last entry.
> +     */
> +    for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) {
> +        /* Bounds check. */
> +        if ((u8*)ie < (u8*)ia || (u8*)ie +
> +                sizeof(INDEX_ENTRY_HEADER) > index_end ||
> +                (u8*)ie + le16_to_cpu(ie->key_length) >
> +                index_end) {
> +            ntfs_error(sb, "Index entry out of bounds in "
> +                    "directory inode 0x%lx.",
> +                    dir_ni->mft_no);
> +            goto unm_err_out;
> +        }
> +        /*FIXME:Gzged set */
> +        ictx->is_in_root = false;
> +        ictx->ia = ia;
> +        ictx->entry = ie;
> +        ictx->actx = NULL;
> +        ictx->base_ni = NULL;
> +        ictx->page = page;
> +        /*
> +         * The last entry cannot contain a name. It can however contain
> +         * a pointer to a child node in the B+tree so we just break out.
> +         */
> +        if (ie->flags & INDEX_ENTRY_END)
> +            break;
> +        /*
> +         * We perform a case sensitive comparison and if that matches
> +         * we are done and return the mft reference of the inode (i.e.
> +         * the inode number together with the sequence number for
> +         * consistency checking). We convert it to cpu format before
> +         * returning.
> +         */
> +        BUG_ON(!PageLocked(ictx->page));
> +
> +        if (ntfs_are_names_equal(uname, uname_len,
> +                (ntfschar*)&ie->key.file_name.file_name,
> +                ie->key.file_name.file_name_length,
> +                CASE_SENSITIVE, vol->upcase, vol->upcase_len)) {
> +found_it2:
> +            /*
> +             * We have a perfect match, so we don't need to care
> +             * about having matched imperfectly before, so we can
> +             * free name and set *res to NULL.
> +             * However, if the perfect match is a short file name,
> +             * we need to signal this through *res, so that
> +             * ntfs_lookup() can fix dcache aliasing issues.
> +             * As an optimization we just reuse an existing
> +             * allocation of *res.
> +             */
> +            /* Gzged shut
> +            if (ie->key.file_name.file_name_type == FILE_NAME_DOS) {
> +                if (!name) {
> +                    name = kmalloc(sizeof(ntfs_name),
> +                            GFP_NOFS);
> +                    if (!name) {
> +                        err = -ENOMEM;
> +                        goto unm_err_out;
> +                    }
> +                }
> +                name->mref = le64_to_cpu(
> +                        ie->data.dir.indexed_file);
> +                name->type = FILE_NAME_DOS;
> +                name->len = 0;
> +                *res = name;
> +            } else {
> +                kfree(name);
> +                *res = NULL;
> +            }
> +            */
> +            /*Gzged shut    
> +            mref = le64_to_cpu(ie->data.dir.indexed_file);
> +            unlock_page(page);
> +            ntfs_unmap_page(page);
> +             * return mref;
> +             * */
> +            goto done;
> +        }
> +        /*
> +         * For a case insensitive mount, we also perform a case
> +         * insensitive comparison (provided the file name is not in the
> +         * POSIX namespace). If the comparison matches, and the name is
> +         * in the WIN32 namespace, we cache the filename in *res so
> +         * that the caller, ntfs_lookup(), can work on it. If the
> +         * comparison matches, and the name is in the DOS namespace, we
> +         * only cache the mft reference and the file name type (we set
> +         * the name length to zero for simplicity).
> +//         */
> +//        if (!NVolCaseSensitive(vol) &&
> +//                ie->key.file_name.file_name_type &&
> +//                ntfs_are_names_equal(uname, uname_len,
> +//                (ntfschar*)&ie->key.file_name.file_name,
> +//                ie->key.file_name.file_name_length,
> +//                IGNORE_CASE, vol->upcase, vol->upcase_len)) {
> +//            int name_size = sizeof(ntfs_name);
> +//            u8 type = ie->key.file_name.file_name_type;
> +//            u8 len = ie->key.file_name.file_name_length;
> +//
> +//            /* Only one case insensitive matching name allowed. */
> +//            if (name) {
> +//                ntfs_error(sb, "Found already allocated name "
> +//                        "in phase 2. Please run chkdsk "
> +//                        "and if that doesn't find any "
> +//                        "errors please report you saw "
> +//                        "this message to "
> +//                        "linux-ntfs-dev@lists."
> +//                        "sourceforge.net.");
> +//                unlock_page(page);
> +//                ntfs_unmap_page(page);
> +//                goto dir_err_out;
> +//            }
> +//
> +//            if (type != FILE_NAME_DOS)
> +//                name_size += len * sizeof(ntfschar);
> +//            name = kmalloc(name_size, GFP_NOFS);
> +//            if (!name) {
> +//                err = -ENOMEM;
> +//                goto unm_err_out;
> +//            }
> +//            name->mref = le64_to_cpu(ie->data.dir.indexed_file);
> +//            name->type = type;
> +//            if (type != FILE_NAME_DOS) {
> +//                name->len = len;
> +//                memcpy(name->name, ie->key.file_name.file_name,
> +//                        len * sizeof(ntfschar));
> +//            } else
> +//                name->len = 0;
> +//            *res = name;
> +//        }
> +        /*
> +         * Not a perfect match, need to do full blown collation so we
> +         * know which way in the B+tree we have to go.
> +         */
> +        rc = ntfs_collate_names(uname, uname_len,
> +                (ntfschar*)&ie->key.file_name.file_name,
> +                ie->key.file_name.file_name_length, 1,
> +                IGNORE_CASE, vol->upcase, vol->upcase_len);
> +        /*
> +         * If uname collates before the name of the current entry, there
> +         * is definitely no such name in this index but we might need to
> +         * descend into the B+tree so we just break out of the loop.
> +         */
> +        if (rc == -1)
> +            break;
> +        /* The names are not equal, continue the search. */
> +        if (rc)
> +            continue;
> +        /*
> +         * Names match with case insensitive comparison, now try the
> +         * case sensitive comparison, which is required for proper
> +         * collation.
> +         */
> +        rc = ntfs_collate_names(uname, uname_len,
> +                (ntfschar*)&ie->key.file_name.file_name,
> +                ie->key.file_name.file_name_length, 1,
> +                CASE_SENSITIVE, vol->upcase, vol->upcase_len);
> +        if (rc == -1)
> +            break;
> +        if (rc)
> +            continue;
> +        /*
> +         * Perfect match, this will never happen as the
> +         * ntfs_are_names_equal() call will have gotten a match but we
> +         * still treat it correctly.
> +         */
> +        goto found_it2;
> +    }
> +    /*
> +     * We have finished with this index buffer without success. Check for
> +     * the presence of a child node.
> +     */
> +    if (ie->flags & INDEX_ENTRY_NODE) {
> +        if ((ia->index.flags & NODE_MASK) == LEAF_NODE) {
> +            ntfs_error(sb, "Index entry with child node found in "
> +                    "a leaf node in directory inode 0x%lx.",
> +                    dir_ni->mft_no);
> +            goto unm_err_out;
> +        }
> +        /* Child node present, descend into it. */
> +        old_vcn = vcn;
> +        vcn = sle64_to_cpup((sle64*)((u8*)ie +
> +                le16_to_cpu(ie->length) - 8));
> +        if (vcn >= 0) {
> +            /* If vcn is in the same page cache page as old_vcn we
> +             * recycle the mapped page. */
> +            if (old_vcn << vol->cluster_size_bits >>
> +                    PAGE_SHIFT == vcn <<
> +                    vol->cluster_size_bits >>
> +                    PAGE_SHIFT)
> +                goto fast_descend_into_child_node;
> +            unlock_page(page);
> +            ntfs_unmap_page(page);
> +            goto descend_into_child_node;
> +        }
> +        ntfs_error(sb, "Negative child node vcn in directory inode "
> +                "0x%lx.", dir_ni->mft_no);
> +        goto unm_err_out;
> +    }
> +    /*
> +     * No child node present, return -ENOENT, unless we have got a matching
> +     * name cached in name in which case return the mft reference
> +     * associated with it.
> +     */
> +/********
> +    if (name) {
> +        unlock_page(page);
> +        ntfs_unmap_page(page);
> +        return name->mref;
> +    }
> +    *******/
> +    ntfs_debug("Entry not found.");
> +    err = -ENOENT;
> +/*
> +    if (!err)
> +        err = -EIO;
> +    if (ctx)
> +        ntfs_attr_put_search_ctx(ctx);
> +    if (m)
> +        unmap_mft_record(dir_ni);
> +        */
> +    return ERR_MREF(err);
> +
> +unm_err_out:
> +    unlock_page(page);
> +    ntfs_unmap_page(page);
> +err_out:
> +    if (!err)
> +        err = -EIO;
> +    if (ctx)
> +        ntfs_attr_put_search_ctx(ctx);
> +    if (m)
> +        unmap_mft_record(dir_ni);
> +    /*Gzged shut
> +    if (name) {
> +        kfree(name);
> +        *res = NULL;
> +    }
> +    */
> +    ntfs_debug("done.");
> +    return ERR_MREF(err);
> +dir_err_out:
> +    ntfs_error(sb, "Corrupt directory.  Aborting lookup.");
> +    goto err_out;
> +}
> MFT_REF ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname,
>        const int uname_len, ntfs_name **res)
> {
> diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c
> index c4f68c3..688ec8f 100644
> --- a/fs/ntfs/file.c
> +++ b/fs/ntfs/file.c
> @@ -24,6 +24,7 @@
> #include <linux/gfp.h>
> #include <linux/pagemap.h>
> #include <linux/pagevec.h>
> +#include <linux/sched.h>
> #include <linux/sched/signal.h>
> #include <linux/swap.h>
> #include <linux/uio.h>
> diff --git a/fs/ntfs/index.h b/fs/ntfs/index.h
> index 8745469..b7d75ce 100644
> --- a/fs/ntfs/index.h
> +++ b/fs/ntfs/index.h
> @@ -88,6 +88,7 @@ typedef struct {
> extern ntfs_index_context *ntfs_index_ctx_get(ntfs_inode *idx_ni);
> extern void ntfs_index_ctx_put(ntfs_index_context *ictx);
>
> +extern int ntfs_lookup_inode_by_key (const void *key, const int key_len, ntfs_index_context *ictx);
> extern int ntfs_index_lookup(const void *key, const int key_len,
>        ntfs_index_context *ictx);
>
> diff --git a/fs/ntfs/mft.c b/fs/ntfs/mft.c
> index b6f4021..dbe7f21 100644
> --- a/fs/ntfs/mft.c
> +++ b/fs/ntfs/mft.c
> @@ -2915,4 +2915,84 @@ int ntfs_extent_mft_record_free(ntfs_inode *ni, MFT_RECORD *m)
>    mark_mft_record_dirty(ni);
>    return err;
> }
> +/**
> + * ntfs_mft_record_free - free an mft record on an ntfs volume
> + * @vol:    volume on which to free the mft record
> + * @ni:        open ntfs inode of the mft record to free
> + *
> + * Free the mft record of the open inode @ni on the mounted ntfs volume @vol.
> + * Note that this function calls ntfs_inode_close() internally and hence you
> + * cannot use the pointer @ni any more after this function returns success.
> + *
> + * On success return 0 and on error return -1 with errno set to the error code.
> + */
> +int ntfs_mft_record_free(ntfs_volume *vol, ntfs_inode *ni,MFT_RECORD* mrec)
> +{
> +    u64 mft_no;
> +    int err;
> +    u16 seq_no, old_seq_no;
> +
> +    ntfs_debug("Entering for inode 0x%llx.\n", (long long) ni->mft_no);
> +
> +    if (!vol || !vol->mftbmp_ino || !ni)
> +    {
> +        return -EINVAL;
> +    }
> +
> +    /* Cache the mft reference for later. */
> +    mft_no = ni->mft_no;
> +
> +    /* Mark the mft record as not in use. */
> +    mrec->flags &= ~MFT_RECORD_IN_USE;
> +
> +    /* Increment the sequence number, skipping zero, if it is not zero. */
> +    old_seq_no = mrec->sequence_number;
> +
> +    seq_no = le16_to_cpu(old_seq_no);
> +    if (seq_no == 0xffff)
> +        seq_no = 1;
> +    else if (seq_no)
> +        seq_no++;
> +    mrec->sequence_number = cpu_to_le16(seq_no);
> +
> +    /* Set the inode dirty and write it out. */
> +    flush_dcache_mft_record_page(ni);
> +    mark_mft_record_dirty(ni);
> +
> +    /* Clear the bit in the $MFT/$BITMAP corresponding to this record. */
> +    ntfs_debug("before down_write...");
> +    //down_write(&vol->mftbmp_lock);
> +    if ( (err = ntfs_bitmap_clear_bit(vol->mftbmp_ino, mft_no )))
> +    //up_write(&vol->mftbmp_lock);
> +    {
> +        // FIXME: If ntfs_bitmap_clear_run() guarantees rollback on
> +        //      error, this could be changed to goto sync_rollback;
> +        goto bitmap_rollback;
> +    }
> +
> +    /* Throw away the now freed inode.
> +    if (!ntfs_inode_close(ni)) {
> +        vol->free_mft_records++;
> +        return 0;
> +    }
> +    err = errno;*/
> +    flush_dcache_mft_record_page(ni);
> +    mark_mft_record_dirty(ni);
> +    return 0;
> +
> +    /* Rollback what we did... */
> +bitmap_rollback:
> +    down_write(&vol->mftbmp_lock);
> +    if (ntfs_bitmap_set_bit(vol->mftbmp_ino, mft_no))
> +    up_write(&vol->mftbmp_lock);
> +    {
> +        ntfs_debug("Eeek! Rollback failed in ntfs_mft_record_free().  "
> +                "Leaving inconsistent metadata!\n");
> +    }
> +    mrec->flags |= MFT_RECORD_IN_USE;
> +    mrec->sequence_number = old_seq_no;
> +    flush_dcache_mft_record_page(ni);
> +    mark_mft_record_dirty(ni);
> +    return err;
> +}
> #endif /* NTFS_RW */
> diff --git a/fs/ntfs/mft.h b/fs/ntfs/mft.h
> index b52bf87..f6a95ba 100644
> --- a/fs/ntfs/mft.h
> +++ b/fs/ntfs/mft.h
> @@ -118,6 +118,7 @@ extern bool ntfs_may_write_mft_record(ntfs_volume *vol,
> extern ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, const int mode,
>        ntfs_inode *base_ni, MFT_RECORD **mrec);
> extern int ntfs_extent_mft_record_free(ntfs_inode *ni, MFT_RECORD *m);
> +extern int ntfs_mft_record_free(ntfs_volume *vol, ntfs_inode *ni,MFT_RECORD* mrec);
>
> #endif /* NTFS_RW */
>
> diff --git a/fs/ntfs/namei.c b/fs/ntfs/namei.c
> index 4690cd7..a737cf5 100644
> --- a/fs/ntfs/namei.c
> +++ b/fs/ntfs/namei.c
> @@ -159,7 +159,7 @@ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent,
>                    PTR_ERR(dent_inode));
>        kfree(name);
>        /* Return the error code. */
> -        return ERR_CAST(dent_inode);
> +        return (struct dentry *)dent_inode;
>    }
>    /* It is guaranteed that @name is no longer allocated at this point. */
>    if (MREF_ERR(mref) == -ENOENT) {
> @@ -273,11 +273,1373 @@ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent,
>    }
> }
>
> +
> +#include "index.h"
> +
> +#define STATUS_OK               (0)
> +#define STATUS_ERROR                (-1)
> +
> +/**
> + *  Insert @ie index entry at @pos entry. Used @ih values should be ok already.
> + */
> +static void ntfs_ie_insert(INDEX_HEADER *ih, INDEX_ENTRY *ie, INDEX_ENTRY *pos)
> +{
> +    int ie_size = le16_to_cpu(ie->length);
> +    ntfs_debug("Entering");
> +    ih->index_length = cpu_to_le32(le32_to_cpu(ih->index_length) + ie_size);
> +    memmove((u8 *)pos + ie_size, pos, le32_to_cpu(ih->index_length) - ((u8 *)pos - (u8 *)ih) - ie_size);
> +    memcpy(pos, ie, ie_size);
> +    ntfs_debug("done");
> +}
> +/**
> + * ntfs_ir_truncate - Truncate index root attribute
> + *
> + * Returns STATUS_OK, STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT or STATUS_ERROR.
> + */
> +static int ntfs_ir_truncate(ntfs_index_context *icx, int data_size)
> +{              
> +/*
> +    ntfs_attr *na;
> +    */
> +    int ret;
> +
> +    ntfs_debug("Entering");
> +    
> +/**
> +    na = ntfs_attr_open(icx->ni, AT_INDEX_ROOT, icx->name, icx->name_len);
> +    if (!na) {
> +        ntfs_log_perror("Failed to open INDEX_ROOT");
> +        return STATUS_ERROR;
> +    }
> +    */
> +    /*
> +     *  INDEX_ROOT must be resident and its entries can be moved to
> +     *  INDEX_BLOCK, so ENOSPC isn't a real error.
> +     */
> +    ret = ntfs_resident_attr_value_resize(icx->actx->mrec, icx->actx->attr, data_size + offsetof(INDEX_ROOT, index) );
> +    /*Gzged changed
> +    ret = ntfs_attr_truncate(na, data_size + offsetof(INDEX_ROOT, index));
> +    */
> +    if (ret == STATUS_OK)
> +    {
> +        /*
> +        icx->ir = ntfs_ir_lookup2(icx->ni, icx->name, icx->name_len);
> +        if (!icx->ir)
> +            return STATUS_ERROR;
> +            */
> +    
> +        icx->ir->index.allocated_size = cpu_to_le32(data_size);
> +        
> +    } else if (ret == -EPERM)
> +    {
> +        ntfs_debug("Failed to truncate INDEX_ROOT");
> +    }
> +    
> +/**
> +    ntfs_attr_close(na);
> +    */
> +    return ret;
> +}
> +        
> +/**
> + * ntfs_ir_make_space - Make more space for the index root attribute
> + *
> + * On success return STATUS_OK or STATUS_KEEP_SEARCHING.
> + * On error return STATUS_ERROR.
> + */
> +static int ntfs_ir_make_space(ntfs_index_context *icx, int data_size)
> +{              
> +    int ret;
> +    ntfs_debug("Entering");
> +    ret = ntfs_ir_truncate(icx, data_size);
> +    /* TODO
> +    if (ret == STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT)
> +    {
> +        ret = ntfs_ir_reparent(icx);
> +        if (ret == STATUS_OK)
> +            ret = STATUS_KEEP_SEARCHING;
> +        else
> +            ntfs_log_perror("Failed to nodify INDEX_ROOT");
> +    }
> +    */
> +    ntfs_debug("Done ");
> +    return ret;
> +}
> +
> +
> +static int ntfs_ie_add(ntfs_index_context *icx, INDEX_ENTRY *ie)
> +{
> +    INDEX_HEADER *ih;
> +    int allocated_size, new_size;
> +    int ret = STATUS_ERROR;
> +    ntfs_inode *idx_ni = icx->idx_ni;
> +    ntfs_debug("Entering. ");
> +    while (1)
> +    {
> +        /* ret = ntfs_index_lookup(&ie->key, le16_to_cpu(ie->key_length), icx) ; */
> +        ntfs_debug("new loop ");
> +        ret = ntfs_lookup_inode_by_key(&ie->key, le16_to_cpu(ie->key_length), icx) ;
> +        ntfs_debug("ntfs_lookup_inode_by_key ret[%d]",ret);
> +        if (!ret)
> +        {
> +            ntfs_debug("Index already have such entry");
> +            goto err_out;
> +        }
> +        if (ret != -ENOENT)
> +        {
> +            ntfs_debug("Failed to find place for new entry");
> +            goto err_out;
> +        }
> +        /*
> +        ntfs_debug("here icx[%p] icx->is_in_root[%d]",icx,icx->is_in_root);
> +        */
> +        if (icx->is_in_root)
> +        {
> +            BUG_ON(!icx->ir);
> +            ih = &(icx->ir->index);
> +        }
> +        else
> +        {
> +            BUG_ON(!icx->ia);
> +            ih = &(icx->ia->index);
> +        }
> +
> +        allocated_size = le32_to_cpu(ih->allocated_size);
> +        new_size = le32_to_cpu(ih->index_length) + le16_to_cpu(ie->length);
> +    
> +        ntfs_debug("index block sizes: allocated: %d  needed: %d", allocated_size, new_size);
> +        if (new_size <= allocated_size)
> +        {
> +            break;
> +        }
> +        /** else  it will make space for new index entry **/
> +        
> +        if (icx->is_in_root)
> +        {
> +            if ( (ret = ntfs_ir_make_space(icx, new_size) ) )
> +            {
> +                ntfs_debug("ntfs_ir_make_space err ");
> +                goto err_out;
> +            }
> +            else
> +            {
> +                ntfs_debug("ntfs_ir_make_space done ");
> +            }
> +        }
> +        else
> +        {
> +            ntfs_debug("should run ntfs_ib_split ");
> +            ret = -ENOSPC;
> +            goto err_out;
> +
> +            /* Gzged shut
> +            if (ntfs_ib_split(icx, icx->ib) == STATUS_ERROR)
> +            {
> +                ntfs_debug("ntfs_ib_split err ");
> +                ret = -ENOMEM;
> +                goto err_out;
> +            }
> +            **/
> +        }
> +        
> +        /*FIXME: Gzged mod
> +        ntfs_inode_mark_dirty(icx->actx->ntfs_ino);
> +        ***/
> +        /*FIXME: Gzged will fix these in furture */
> +        ntfs_debug("Before flush_dcache_mft_record_page ");/*****die here *****/
> +        flush_dcache_mft_record_page(icx->actx->ntfs_ino);
> +
> +        ntfs_debug("Before mark_mft_record_dirty ");
> +        mark_mft_record_dirty(icx->actx->ntfs_ino);
> +
> +        /*FIXME: Gzged mod ntfs_index_ctx_reinit(icx); ***/
> +        ntfs_index_ctx_put(icx);
> +        ntfs_index_ctx_get(idx_ni);
> +    }
> +    
> +    ntfs_ie_insert(ih, ie, icx->entry);
> +    ntfs_index_entry_flush_dcache_page(icx);
> +    ntfs_index_entry_mark_dirty(icx);
> +    
> +    ret = STATUS_OK;
> +err_out:
> +    ntfs_debug("%s", ret ? "Failed" : "Done");
> +    return ret;
> +}
> +/**
> + * ntfs_index_add_filename - add filename to directory index
> + * @ni:        ntfs inode describing directory to which index add filename
> + * @fn:        FILE_NAME attribute to add
> + * @mref:    reference of the inode which @fn describes
> + *
> + * Return 0 on success or -1 on error with errno set to the error code.
> + */
> +ntfs_index_context * ntfs_index_ctx_get_I30(ntfs_inode *idx_ni)
> +{
> +    static ntfschar I30[5] = { cpu_to_le16('$'),
> +                cpu_to_le16('I'),
> +                cpu_to_le16('3'),
> +                cpu_to_le16('0'),
> +                0 };
> +
> +    struct inode * tmp_ino = ntfs_index_iget(VFS_I(idx_ni), I30, 4);
> +    if (IS_ERR(tmp_ino)) {
> +        ntfs_debug("Failed to load $I30 index.");
> +        return NULL;
> +    }
> +
> +    return ntfs_index_ctx_get(NTFS_I(tmp_ino));
> +}
> +void ntfs_index_ctx_put_I30(ntfs_index_context *ictx)
> +{
> +    ntfs_commit_inode(VFS_I(ictx->idx_ni));
> +    iput(VFS_I(ictx->idx_ni));
> +    ntfs_index_ctx_put(ictx);
> +}
> +static int ntfs_index_add_filename(ntfs_inode *dir_ni, FILE_NAME_ATTR *fn, MFT_REF mref)
> +{
> +    INDEX_ENTRY *ie;
> +    ntfs_index_context *icx;
> +    int ret = -1;
> +
> +    ntfs_debug("Entering");
> +    
> +    if (!dir_ni || !fn)
> +    {
> +        ntfs_error((VFS_I(dir_ni))->i_sb,"Invalid arguments.");
> +        return -EINVAL;
> +    }
> +    
> +    {/** create and set INDEX_entry **/
> +        int fn_size, ie_size;
> +        fn_size = (fn->file_name_length * sizeof(ntfschar)) + sizeof(FILE_NAME_ATTR);
> +        ie_size = (sizeof(INDEX_ENTRY_HEADER) + fn_size + 7) & ~7;
> +        
> +        ie = kcalloc(1,ie_size,GFP_KERNEL);
> +        if (!ie)
> +        {
> +            return -ENOMEM;
> +        }
> +
> +        ie->data.dir.indexed_file = cpu_to_le64(mref);
> +        ie->length            = cpu_to_le16(ie_size);
> +        ie->key_length        = cpu_to_le16(fn_size);
> +        ie->flags            = cpu_to_le16(0);
> +        memcpy(&(ie->key.file_name), fn, fn_size);
> +    }/**  END of create and set INDEX_entry **/
> +    
> +    icx =  ntfs_index_ctx_get(dir_ni);
> +    if (!icx)
> +    {
> +        ret = PTR_ERR(icx);
> +        goto out;
> +    }
> +    
> +    ret = ntfs_ie_add(icx, ie);
> +out:
> +    if(icx)
> +    {
> +        ntfs_index_ctx_put(icx);
> +    }
> +    if(ie)
> +    {
> +        kfree(ie);
> +    }
> +    ntfs_debug("done");
> +    return ret;
> +}
> +
> +
> +/**
> + * __ntfs_create - create object on ntfs volume
> + * @dir_ni:    ntfs inode for directory in which create new object
> + * @name:    unicode name of new object
> + * @name_len:    length of the name in unicode characters
> + * @type:    type of the object to create
> + *
> + * Internal, use ntfs_create{,_device,_symlink} wrappers instead.
> + *
> + * @type can be:
> + *    S_IFREG        to create regular file
> + *
> + * Return opened ntfs inode that describes created object on success
> + * or ERR_PTR(errno) on error
> + */
> +
> +static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni,
> +        ntfschar *name, u8 name_len, dev_t type )
> +{
> +    ntfs_inode *ni =NULL;/** this is for new inode **/
> +    FILE_NAME_ATTR *fn = NULL;
> +    STANDARD_INFORMATION *si = NULL;
> +    SECURITY_DESCRIPTOR_ATTR *sd =NULL;
> +    int err;
> +
> +    /*Author:Gzged */
> +    MFT_RECORD* mrec;
> +    int new_temp_offset ;
> +    char* temp_new_record;
> +
> +    ntfs_debug("Entering.");
> +    
> +    /* Sanity checks. */
> +    if (!dir_ni || !name || !name_len)
> +    {
> +        ntfs_error((VFS_I(dir_ni))->i_sb,"Invalid arguments.");
> +        return ERR_PTR(-EINVAL);
> +    }
> +
> +    /* TODO : not support REPARSE_POINT **/
> +
> +    /** alloc new mft record for new file **/
> +    ni = ntfs_mft_record_alloc(dir_ni->vol, type,NULL,&mrec);
> +    if (IS_ERR(ni))
> +    {
> +        ntfs_debug("ntfs_mft_record_alloc error [%ld]",PTR_ERR(ni));
> +        return ni;
> +    }
> +    else
> +    {
> +        new_temp_offset = mrec->attrs_offset ;
> +        /** ntfs_mft_record_alloc{} had map ni to mrec */
> +        temp_new_record=(char*)mrec;
> +        ntfs_debug("mrec->mft_record_number [%d]",mrec->mft_record_number);
> +    }
> +
> +
> +/**************** Begin from here , error must goto err_out *****************/
> +    { /************************ STANDARD_INFORMATION start ******************/
> +        /*
> +         * Create STANDARD_INFORMATION attribute. Write STANDARD_INFORMATION
> +         * version 1.2, windows will upgrade it to version 3 if needed.
> +         */
> +        ATTR_REC attr_si;
> +        int si_len,attr_si_len;
> +
> +        /*** $STANDARD_INFORMATION (0x10)  **/
> +        si_len = offsetof(STANDARD_INFORMATION, ver) + sizeof(si->ver.v1.reserved12);
> +        si = kcalloc(1,si_len,GFP_KERNEL );
> +        if (!si)
> +        {
> +            err = -ENOMEM;
> +            goto err_out;
> +        }
> +#define NTFS_TIME_OFFSET ((s64)(369 * 365 + 89) * 24 * 3600 * 10000000)
> +        si->creation_time = NTFS_TIME_OFFSET ;
> +        si->last_data_change_time = NTFS_TIME_OFFSET ;
> +        si->last_mft_change_time = NTFS_TIME_OFFSET ;
> +        si->last_access_time = NTFS_TIME_OFFSET ;
> +
> +        /** set attr **/
> +        attr_si_len = offsetof(ATTR_REC, data) + sizeof(attr_si.data.resident) ;
> +        attr_si= (ATTR_REC )
> +        {
> +            .type = AT_STANDARD_INFORMATION ,
> +            .length = attr_si_len +  si_len ,
> +            .non_resident = 0,
> +            .name_length = 0,
> +            .name_offset = 0,
> +            .flags =  0 ,
> +            .instance = (mrec->next_attr_instance) ++ ,
> +            .data=
> +            {
> +                .resident=
> +                {
> +                    .value_length = si_len,
> +                    .value_offset = attr_si_len  ,
> +                    .flags = 0 ,
> +                }
> +            },
> +        };
> +        /*
> +        attr_si.data.resident.value_length=si_len
> +        attr_si.data.resident.flags = 0;
> +        */
> +
> +        /* Add STANDARD_INFORMATION to inode. */
> +        memcpy(&(temp_new_record[new_temp_offset]),&attr_si, attr_si_len  );
> +        new_temp_offset += attr_si_len;
> +        memcpy(&(temp_new_record[new_temp_offset]),  si,  si_len);
> +        new_temp_offset += si_len;
> +
> +        ntfs_debug("new_temp_offset [%d]",new_temp_offset);
> +
> +        kfree(si);
> +        si=NULL;
> +    } /****************************** end of STANDARD_INFORMATION *************/
> +    
> +    /*
> +    if (ntfs_sd_add_everyone(ni)) {
> +        err = errno;
> +        goto err_out;
> +    }
> +    rollback_sd = 1;
> +    */
> +
> +
> +
> +    { /****************************** start of FILE_NAME *************/
> +
> +        ATTR_REC attr_fna;
> +        int fn_len , attr_fna_len;
> +        /** create FILE_NAME_ATTR **/
> +        fn_len = sizeof(FILE_NAME_ATTR) + name_len * sizeof(ntfschar);
> +        fn = kcalloc(1,fn_len,GFP_KERNEL);
> +        if (!fn)
> +        {
> +            err = -ENOMEM;
> +            goto err_out;
> +        }
> +        fn->parent_directory = MK_LE_MREF(dir_ni->mft_no, le16_to_cpu(dir_ni->seq_no));
> +        fn->file_name_length = name_len;
> +        fn->file_name_type = FILE_NAME_POSIX;
> +        fn->creation_time = NTFS_TIME_OFFSET;
> +        fn->last_data_change_time = NTFS_TIME_OFFSET;
> +        fn->last_mft_change_time = NTFS_TIME_OFFSET;
> +        fn->last_access_time = NTFS_TIME_OFFSET;
> +        fn->data_size = cpu_to_sle64(ni->initialized_size);
> +        fn->allocated_size = cpu_to_sle64(ni->allocated_size);
> +        memcpy(fn->file_name, name, name_len * sizeof(ntfschar));
> +
> +        /* Create FILE_NAME attribute. */
> +        attr_fna_len = offsetof(ATTR_REC, data) + sizeof(attr_fna.data.resident) ;
> +        attr_fna=(ATTR_REC)
> +        {
> +            .type = AT_FILE_NAME ,
> +            .length = ( attr_fna_len + fn_len + 7 ) & ~7 ,
> +            .non_resident = 0,
> +            .name_length = 0,
> +            .name_offset = 0,
> +            .flags =  RESIDENT_ATTR_IS_INDEXED ,
> +            .instance = (mrec->next_attr_instance) ++ ,
> +            .data=
> +            {
> +                .resident=
> +                {
> +                    .value_length = fn_len,
> +                    .value_offset = attr_fna_len  ,
> +                    .flags = 0 ,
> +                }
> +            },
> +        };
> +
> +        /** insert FILE_NAME into new_file_record **/
> +        memcpy(&(temp_new_record[new_temp_offset]) , &attr_fna,  attr_fna_len);
> +        memcpy(&(temp_new_record[new_temp_offset + attr_fna_len]),fn,fn_len);
> +        new_temp_offset += attr_fna.length;
> +
> +        ntfs_debug("new_temp_offset [%d]",new_temp_offset);
> +
> +/**********add to index ********************************************/
> +        /* Add FILE_NAME attribute to index. */
> +        if ((err = ntfs_index_add_filename(dir_ni, fn, MK_MREF(ni->mft_no, le16_to_cpu(ni->seq_no))) ))
> +        {
> +            ntfs_error((VFS_I(dir_ni))->i_sb,"Failed to add entry to the index");
> +            goto err_out;
> +        }
> +/*********************************************************/
> +
> +        kfree(fn);
> +        fn=NULL;
> +    } /****************************** end of FILE_NAME *************/
> +
> +    { /****************************** start of SECURITY_DESCRIPTOR *************/
> +        ACL *acl;
> +        ACCESS_ALLOWED_ACE *ace;
> +        SID *sid;
> +        ATTR_REC attr_sd;
> +        int sd_len, attr_sd_len;
> +
> +        /* Create SECURITY_DESCRIPTOR attribute (everyone has full access). */
> +        /*
> +         * Calculate security descriptor length. We have 2 sub-authorities in
> +         * owner and group SIDs, but structure SID contain only one, so add
> +         * 4 bytes to every SID.
> +         */
> +        sd_len = sizeof(SECURITY_DESCRIPTOR_ATTR) + 2 * (sizeof(SID) + 4) +
> +            sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE);
> +        sd = kcalloc(1,sd_len,GFP_KERNEL);
> +        if (!sd)
> +        {
> +            err = -ENOMEM;
> +            goto err_out;
> +        }
> +
> +        sd->revision = 1;
> +        sd->control = SE_DACL_PRESENT | SE_SELF_RELATIVE;
> +
> +        sid = (SID *)((u8 *)sd + sizeof(SECURITY_DESCRIPTOR_ATTR));
> +        sid->revision = 1;
> +        sid->sub_authority_count = 2;
> +        sid->sub_authority[0] = cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID);
> +        sid->sub_authority[1] = cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS);
> +        sid->identifier_authority.value[5] = 5;
> +        sd->owner = cpu_to_le32((u8 *)sid - (u8 *)sd);
> +
> +        sid = (SID *)((u8 *)sid + sizeof(SID) + 4);
> +        sid->revision = 1;
> +        sid->sub_authority_count = 2;
> +        sid->sub_authority[0] = cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID);
> +        sid->sub_authority[1] = cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS);
> +        sid->identifier_authority.value[5] = 5;
> +        sd->group = cpu_to_le32((u8 *)sid - (u8 *)sd);
> +
> +        acl = (ACL *)((u8 *)sid + sizeof(SID) + 4);
> +        acl->revision = 2;
> +        acl->size = cpu_to_le16(sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE));
> +        acl->ace_count = cpu_to_le16(1);
> +        sd->dacl = cpu_to_le32((u8 *)acl - (u8 *)sd);
> +
> +        ace = (ACCESS_ALLOWED_ACE *)((u8 *)acl + sizeof(ACL));
> +        ace->type = ACCESS_ALLOWED_ACE_TYPE;
> +        ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE;
> +        ace->size = cpu_to_le16(sizeof(ACCESS_ALLOWED_ACE));
> +        ace->mask = cpu_to_le32(0x1f01ff); /* FIXME */
> +        ace->sid.revision = 1;
> +        ace->sid.sub_authority_count = 1;
> +        ace->sid.sub_authority[0] = 0;
> +        ace->sid.identifier_authority.value[5] = 1;
> +
> +        /* Create the attribute */
> +        attr_sd_len = offsetof(ATTR_REC, data) + sizeof(attr_sd.data.resident) ;
> +        attr_sd=(ATTR_REC)
> +        {
> +            .type = AT_SECURITY_DESCRIPTOR  ,
> +            .length = ( attr_sd_len + sd_len + 7 ) & ~7 ,
> +            .non_resident = 0,
> +            .name_length = 0,
> +            .name_offset = 0,
> +            .flags =  0 ,
> +            .instance = (mrec->next_attr_instance) ++ ,
> +            .data=
> +            {
> +                .resident=
> +                {
> +                    .value_length = sd_len,
> +                    .value_offset = attr_sd_len  ,
> +                    .flags = 0 ,
> +                }
> +            },
> +        };
> +
> +        /** insert FILE_NAME into new_file_record **/
> +        memcpy(&(temp_new_record[new_temp_offset]) , &attr_sd,  attr_sd_len);
> +        memcpy(&(temp_new_record[new_temp_offset + attr_sd_len]), sd ,sd_len);
> +        new_temp_offset += attr_sd.length;
> +        ntfs_debug("new_temp_offset [%d]",new_temp_offset);
> +/************************/
> +
> +        kfree(sd);
> +        sd=NULL;
> +    } /****************************** end of SECURITY_DESCRIPTOR *************/
> +
> +    { /****************************** start of DATA *************/
> +        /***  $DATA (0x80)   **/
> +        ATTR_REC attr_data;
> +        int attr_data_len= offsetof(ATTR_REC, data) + sizeof(attr_data.data.resident) ;
> +        attr_data=(ATTR_REC)
> +        {
> +            .type = AT_DATA ,
> +            .length = attr_data_len,
> +            .non_resident = 0,
> +            .name_length = 0,
> +            .name_offset = 0,
> +            .flags =  0 ,
> +            .instance = (mrec->next_attr_instance) ++ ,
> +            .data=
> +            {
> +                .resident=
> +                {
> +                    .value_length = 0,
> +                    .value_offset = attr_data_len  ,
> +                    .flags = 0 ,
> +                }
> +            },
> +        };
> +        /** insert DATA into new_file_record **/
> +        memcpy(&(temp_new_record[new_temp_offset]),&attr_data, attr_data_len );
> +        new_temp_offset += attr_data_len ;
> +        ntfs_debug("new_temp_offset [%d]",new_temp_offset);
> +
> +    } /****************************** end of DATA *************/
> +
> +    { /****************************** start of $END *************/
> +        /***  $AT_END              = cpu_to_le32(0xffffffff) */
> +        ATTR_REC attr_end ;
> +        int attr_end_len= offsetof(ATTR_REC, data) + sizeof(attr_end.data.resident) ;
> +        attr_end=(ATTR_REC)
> +        {
> +            .type = AT_END ,
> +            .length = attr_end_len ,
> +            .non_resident = 0,
> +            .name_length = 0,
> +            .name_offset = 0,
> +            .flags =  0 ,
> +            .instance = (mrec->next_attr_instance) ++ ,
> +            .data=
> +            {
> +                .resident=
> +                {
> +                    .value_length = 0,
> +                    .value_offset = attr_end_len  ,
> +                    .flags = 0 ,
> +                }
> +            },
> +        };
> +        /** insert END into new_file_record **/
> +        memcpy(&(temp_new_record[new_temp_offset]),&attr_end, attr_end_len);
> +        new_temp_offset += attr_end_len ;
> +        ntfs_debug("new_temp_offset [%d]",new_temp_offset);
> +
> +    } /****************************** end of $END *************/
> +
> +
> +    /**FIXME : it do not support hard link **/
> +    mrec->link_count = cpu_to_le16(1);
> +    /** MUST set this **/
> +    mrec->bytes_in_use = new_temp_offset;
> +
> +    flush_dcache_mft_record_page(ni);
> +    mark_mft_record_dirty(ni);  /**ntfs_inode_mark_dirty(ni); int ntfs-3g**/
> +    unmap_mft_record(ni);
> +
> +    (VFS_I(ni))->i_op = &ntfs_file_inode_ops;
> +    (VFS_I(ni))->i_fop = &ntfs_file_ops;
> +
> +    if (NInoMstProtected(ni))
> +    {
> +        (VFS_I(ni))->i_mapping->a_ops = &ntfs_mst_aops;
> +    }
> +    else
> +    {
> +        (VFS_I(ni))->i_mapping->a_ops = &ntfs_normal_aops;
> +    }
> +
> +    (VFS_I(ni))->i_blocks = ni->allocated_size >> 9;
> +
> +
> +    ntfs_debug("Done.");
> +    return ni;
> +err_out:
> +    ntfs_debug("Failed.");
> +
> +    /* TODO : if ni->nr_extents had been set  should been clear here **/
> +
> +    if(fn)
> +    {
> +        kfree(fn);
> +        fn=NULL;
> +    }
> +    if(si)
> +    {
> +        kfree(si);
> +        si=NULL;
> +    }
> +    if(sd)
> +    {
> +        kfree(sd);
> +        sd=NULL;
> +    }
> +
> +    if(mrec)
> +    {
> +        if (ntfs_mft_record_free(ni->vol, ni,mrec))
> +        {
> +            ntfs_debug("Failed to free MFT record.  "
> +                    "Leaving inconsistent metadata. Run chkdsk.\n");
> +        }
> +        inode_dec_link_count(VFS_I(ni)); //(VFS_I(ni))->i_nlink--;
> +        unmap_mft_record(ni);
> +        atomic_dec(&(VFS_I(ni))->i_count);
> +        mrec=NULL;
> +    }
> +    return ERR_PTR(err);
> +}
> +
> +/**
> + * Some wrappers around __ntfs_create() ...
> + */
> +ntfs_inode *ntfs_create(ntfs_inode *dir_ni, ntfschar *name, u8 name_len,
> +        dev_t type)
> +{
> +    /*TODO : type could be { S_IFREG S_IFDIR  S_IFIFO  S_IFSOCK } */
> +    if (type != S_IFREG )
> +    {
> +        ntfs_error((VFS_I(dir_ni))->i_sb,"Invalid arguments.");
> +        return ERR_PTR(-EOPNOTSUPP);
> +    }
> +    return __ntfs_create(dir_ni, name, name_len, type);
> +}
> +
> +static int ntfs_create_inode(struct inode *dir,
> +                struct dentry *dent,
> +                umode_t mode,
> +                bool pn )
> +{
> +    ntfschar *uname;
> +    int uname_len;
> +    ntfs_inode* ni ;
> +
> +    /* Convert the name of the dentry to Unicode. */
> +    uname_len = ntfs_nlstoucs(NTFS_SB(dir->i_sb), dent->d_name.name, dent->d_name.len, &uname);
> +    if (uname_len < 0)
> +    {
> +        if (uname_len != -ENAMETOOLONG)
> +        {
> +            ntfs_error(dir->i_sb, "Failed to convert name to Unicode.");
> +        }
> +        return uname_len;
> +    }
> +
> +
> +    /* create file and inode */
> +    ni = ntfs_create(NTFS_I(dir), uname, uname_len , mode & S_IFMT  );
> +    kmem_cache_free(ntfs_name_cache, uname);
> +    if(likely(!IS_ERR(ni)))
> +    {
> +        d_instantiate(dent,VFS_I(ni));
> +        /* TODO : modify    dir->i_mtime  to CURRENT_TIME */
> +        ntfs_debug("Done.");
> +        return 0;
> +    }
> +    else
> +    {
> +        ntfs_error(dir->i_sb, "ntfs_create error! dentry->d_name.name[%s]", dent->d_name.name);
> +        return  PTR_ERR(ni);
> +    }
> +
> +}
> +
> +/************************* unlink support **********/
> +
> +/*
> +static int ntfs_index_rm_node(ntfs_index_context *icx)
> +{
> +    int entry_pos, pindex;
> +    VCN vcn;
> +    INDEX_BLOCK *ib = NULL;
> +    INDEX_ENTRY *ie_succ, *ie, *entry = icx->entry;
> +    INDEX_HEADER *ih;
> +    u32 new_size;
> +    int delta, ret = STATUS_ERROR;
> +
> +    ntfs_debug("Entering");
> +    
> +    if (!icx->ia_na) {
> +        icx->ia_na = ntfs_ia_open(icx, icx->ni);
> +        if (!icx->ia_na)
> +            return STATUS_ERROR;
> +    }
> +
> +    ib = ntfs_malloc(icx->block_size);
> +    if (!ib)
> +        return STATUS_ERROR;
> +    
> +    ie_succ = ntfs_ie_get_next(icx->entry);
> +    entry_pos = icx->parent_pos[icx->pindex]++;
> +    pindex = icx->pindex;
> +descend:
> +    vcn = ntfs_ie_get_vcn(ie_succ);
> +    if (ntfs_ib_read(icx, vcn, ib))
> +        goto out;
> +    
> +    ie_succ = ntfs_ie_get_first(&ib->index);
> +
> +    if (ntfs_icx_parent_inc(icx))
> +        goto out;
> +    
> +    icx->parent_vcn[icx->pindex] = vcn;
> +    icx->parent_pos[icx->pindex] = 0;
> +
> +    if ((ib->index.ih_flags & NODE_MASK) == INDEX_NODE)
> +        goto descend;
> +
> +    if (ntfs_ih_zero_entry(&ib->index)) {
> +        errno = EIO;
> +        ntfs_log_perror("Empty index block");
> +        goto out;
> +    }
> +
> +    ie = ntfs_ie_dup(ie_succ);
> +    if (!ie)
> +        goto out;
> +    
> +    if (ntfs_ie_add_vcn(&ie))
> +        goto out2;
> +
> +    ntfs_ie_set_vcn(ie, ntfs_ie_get_vcn(icx->entry));
> +
> +    if (icx->is_in_root)
> +        ih = &icx->ir->index;
> +    else
> +        ih = &icx->ib->index;
> +
> +    delta = le16_to_cpu(ie->length) - le16_to_cpu(icx->entry->length);
> +    new_size = le32_to_cpu(ih->index_length) + delta;
> +    if (delta > 0) {
> +        if (icx->is_in_root) {
> +            ret = ntfs_ir_make_space(icx, new_size);
> +            if (ret != STATUS_OK)
> +                goto out2;
> +            
> +            ih = &icx->ir->index;
> +            entry = ntfs_ie_get_by_pos(ih, entry_pos);
> +            
> +        } else if (new_size > le32_to_cpu(ih->allocated_size)) {
> +            icx->pindex = pindex;
> +            ret = ntfs_ib_split(icx, icx->ib);
> +            if (ret == STATUS_OK)
> +                ret = STATUS_KEEP_SEARCHING;
> +            goto out2;
> +        }
> +    }
> +
> +    ntfs_ie_delete(ih, entry);
> +    ntfs_ie_insert(ih, ie, entry);
> +    
> +    if (icx->is_in_root) {
> +        if (ntfs_ir_truncate(icx, new_size))
> +            goto out2;
> +    } else
> +        if (ntfs_icx_ib_write(icx))
> +            goto out2;
> +    
> +    ntfs_ie_delete(&ib->index, ie_succ);
> +    
> +    if (ntfs_ih_zero_entry(&ib->index)) {
> +        if (ntfs_index_rm_leaf(icx))
> +            goto out2;
> +    } else
> +        if (ntfs_ib_write(icx, ib))
> +            goto out2;
> +
> +    ret = STATUS_OK;
> +out2:
> +    free(ie);
> +out:
> +    free(ib);
> +    return ret;
> +}
> +*/
> +
> +static int ntfs_ie_end(INDEX_ENTRY *ie)
> +{
> +    return ie->flags & INDEX_ENTRY_END || !ie->length;
> +}
> +
> +static INDEX_ENTRY *ntfs_ie_get_first(INDEX_HEADER *ih)
> +{
> +    return (INDEX_ENTRY *)((u8 *)ih + le32_to_cpu(ih->entries_offset));
> +}
> +static INDEX_ENTRY *ntfs_ie_get_next(INDEX_ENTRY *ie)
> +{
> +    return (INDEX_ENTRY *)((char *)ie + le16_to_cpu(ie->length));
> +}
> +
> +
> +static void ntfs_ie_delete(INDEX_HEADER *ih, INDEX_ENTRY *ie)
> +{
> +    u32 new_size;
> +    ntfs_debug("Entering");
> +    new_size = le32_to_cpu(ih->index_length) - le16_to_cpu(ie->length);
> +    ih->index_length = cpu_to_le32(new_size);
> +    memmove(ie, (u8 *)ie + le16_to_cpu(ie->length), new_size - ((u8 *)ie - (u8 *)ih));
> +    ntfs_debug("Done");
> +}
> +
> +
> +
> +static u8 *ntfs_ie_get_end(INDEX_HEADER *ih)
> +{
> +    /* FIXME: check if it isn't overflowing the index block size */
> +    return (u8 *)ih + le32_to_cpu(ih->index_length);
> +}
> +
> +
> +static int ntfs_ih_numof_entries(INDEX_HEADER *ih)
> +{
> +    int n;
> +    INDEX_ENTRY *ie;
> +    u8 *end;
> +    
> +    ntfs_debug("Entering");
> +    
> +    end = ntfs_ie_get_end(ih);
> +    ie = ntfs_ie_get_first(ih);
> +    for (n = 0; !ntfs_ie_end(ie) && (u8 *)ie < end; n++)
> +        ie = ntfs_ie_get_next(ie);
> +    return n;
> +}
> +
> +static int ntfs_ih_one_entry(INDEX_HEADER *ih)
> +{
> +    return (ntfs_ih_numof_entries(ih) == 1);
> +}
> +/**
> + * ntfs_index_rm - remove entry from the index
> + * @icx:    index context describing entry to delete
> + *
> + * Delete entry described by @icx from the index. Index context is always
> + * reinitialized after use of this function, so it can be used for index
> + * lookup once again.
> + *
> + * Return 0 on success or -1 on error with errno set to the error code.
> + */
> +static int ntfs_index_rm(ntfs_index_context *icx)
> +{
> +    INDEX_HEADER *ih;
> +    int ret = STATUS_OK;
> +
> +    ntfs_debug("Entering");
> +    
> +    if (!icx || (!icx->ia && !icx->ir) || ntfs_ie_end(icx->entry))
> +    {
> +        ntfs_debug("Invalid arguments.");
> +        ret = -EINVAL;
> +        goto err_out;
> +    }
> +    if (icx->is_in_root)
> +    {
> +        ih = &icx->ir->index;
> +    }
> +    else
> +    {
> +        ih = &icx->ia->index;
> +    }
> +    
> +    if (icx->entry->flags & INDEX_ENTRY_NODE)
> +    {
> +    /* not support
> +        ret = ntfs_index_rm_node(icx);
> +    */
> +        ret =  -EOPNOTSUPP ;
> +        goto err_out;
> +    }
> +    else if (icx->is_in_root || !ntfs_ih_one_entry(ih))
> +    {
> +        ntfs_ie_delete(ih, icx->entry);
> +        
> +        if (icx->is_in_root)
> +        {
> +            ret = ntfs_ir_truncate(icx, le32_to_cpu(ih->index_length));
> +            if (ret != STATUS_OK)
> +            {
> +                goto err_out;
> +            }
> +            ntfs_debug("Before flush_dcache_mft_record_page ");
> +            flush_dcache_mft_record_page(icx->actx->ntfs_ino);
> +
> +            ntfs_debug("Before mark_mft_record_dirty ");
> +            mark_mft_record_dirty(icx->actx->ntfs_ino);
> +        }
> +        else
> +        {
> +            /* shut by Gzged
> +            if (ntfs_icx_ib_write(icx))
> +            {
> +                goto err_out;
> +            }
> +            */
> +            ntfs_debug("Before ntfs_index_entry_flush_dcache_page ");
> +            ntfs_index_entry_flush_dcache_page(icx);
> +            ntfs_debug("Before ntfs_index_entry_mark_dirty ");
> +            ntfs_index_entry_mark_dirty(icx);
> +        }
> +    }
> +    else
> +    {
> +        ret =  -EOPNOTSUPP ;
> +        goto err_out;
> +        /** not support yet
> +        if (ntfs_index_rm_leaf(icx))
> +        {
> +            goto err_out;
> +        }
> +        **/
> +    }
> +
> +
> +err_out:
> +    ntfs_debug("Done ");
> +    return ret;
> +}
> +/** 20091014 **/
> +int ntfs_index_remove(ntfs_inode *ni, const void *key, const int keylen)
> +{
> +    int ret = STATUS_ERROR;
> +    ntfs_index_context *icx;
> +
> +    icx = ntfs_index_ctx_get(ni);
> +    if (!icx)
> +    {
> +        return -1;
> +    }
> +
> +    while (1)
> +    {
> +        /** use my func
> +        if (ntfs_index_lookup(key, keylen, icx))
> +        */
> +        if ( (ret = ntfs_lookup_inode_by_key (key, keylen, icx) ) )
> +        {
> +            ntfs_debug("ntfs_lookup_inode_by_key faild ...");
> +            goto err_out;
> +        }
> +/*********debug print
> +    {
> +            struct qstr nls_name;
> +            nls_name.name = NULL;
> +            nls_name.len = (unsigned)ntfs_ucstonls(ni->vol,
> +                            ((FILE_NAME_ATTR *)icx->data)->file_name , icx->data_len,
> +                                            (unsigned char**)&nls_name.name, 0);
> +            ntfs_debug("icx data name=[%s]",nls_name.name);
> +            kfree(nls_name.name);
> +    }
> +
> +        if (((FILE_NAME_ATTR *)icx->data)->file_attributes & FILE_ATTR_REPARSE_POINT)
> +        {
> +            ntfs_debug("not support faild ");
> +            ret = -EOPNOTSUPP;
> +            goto err_out;
> +        } ********/
> +/*********debug print ********/
> +
> +        ret = ntfs_index_rm(icx);
> +        if (ret == STATUS_OK)
> +        {
> +            ntfs_debug("ntfs_index_rm Done");
> +            break;
> +        }
> +        else
> +        {
> +            ntfs_debug("ntfs_index_rm faild");
> +            goto err_out;
> +        }
> +        /*
> +        flush_dcache_mft_record_page(icx->actx->ntfs_ino);
> +        mark_mft_record_dirty(icx->actx->ntfs_ino);
> +        */
> +        /*FIXME:Gzged change
> +        ntfs_inode_mark_dirty(icx->actx->ntfs_ino);
> +        ntfs_index_ctx_reinit(icx);
> +        ***************/
> +        ntfs_index_ctx_put(icx);
> +        ntfs_index_ctx_get(ni);
> +    }
> +
> +    /*
> +    ntfs_debug("Before flush_dcache_mft_record_page ");
> +    flush_dcache_mft_record_page(icx->actx->ntfs_ino);
> +    ntfs_debug("Before mark_mft_record_dirty ");
> +    mark_mft_record_dirty(icx->actx->ntfs_ino);
> +    */
> +    /*
> +    ntfs_debug("Before ntfs_index_entry_flush_dcache_page ");
> +    ntfs_index_entry_flush_dcache_page(icx);
> +    ntfs_debug("Before ntfs_index_entry_mark_dirty ");
> +    ntfs_index_entry_mark_dirty(icx);
> +    */
> +
> +err_out:
> +    ntfs_debug("Delete Done");
> +    if(icx)
> +    {
> +        ntfs_index_ctx_put(icx);
> +    }
> +    return ret;
> +}
> +
> +static __inline__ int ntfs_attrs_walk(ntfs_attr_search_ctx *ctx)
> +{
> +    /** case all attr **/
> +    return ntfs_attr_lookup(0, NULL, 0, CASE_SENSITIVE, 0, NULL, 0, ctx);
> +}
> +
> +
> +//static const char *es = "  Leaving inconsistent metadata.  Unmount and run chkdsk.";
> +typedef bool BOOL;
> +#include "lcnalloc.h"
> +int ntfs_delete(ntfs_inode *ni, ntfs_inode *dir_ni )
> +{
> +    ntfs_attr_search_ctx *actx = NULL;
> +    MFT_RECORD* mrec;
> +    FILE_NAME_ATTR *fn = NULL;
> +    ntfs_volume* vol=ni->vol;
> +    /*
> +    BOOL looking_for_dos_name = FALSE, looking_for_win32_name = FALSE;
> +    BOOL case_sensitive_match = TRUE;
> +    */
> +    int err = 0;
> +
> +    ntfs_debug("Entering");
> +
> +    mrec = map_mft_record(ni);
> +    if (IS_ERR(mrec)) {
> +        err = PTR_ERR(mrec);
> +        mrec = NULL;
> +        goto err_out;
> +    }
> +
> +    if ( (mrec->flags & MFT_RECORD_IS_DIRECTORY) )
> +    {
> +        ntfs_debug("Invalid arguments.");
> +        err=  -EINVAL;
> +        goto err_out;
> +    }
> +    
> +    if (!ni || !dir_ni )
> +    {
> +        ntfs_debug("Invalid arguments.");
> +        err=  -EINVAL;
> +        goto err_out;
> +    }
> +
> +    if (ni->nr_extents == -1)
> +        ni = ni->ext.base_ntfs_ino;
> +    if (dir_ni->nr_extents == -1)
> +        dir_ni = dir_ni->ext.base_ntfs_ino;
> +
> +
> +/******************************************* get fn ******************/
> +    /*
> +     * Search for FILE_NAME attribute with such name. If it's in POSIX or
> +     * WIN32_AND_DOS namespace, then simply remove it from index and inode.
> +     * If filename in DOS or in WIN32 namespace, then remove DOS name first,
> +     * only then remove WIN32 name.
> +     */
> +    actx = ntfs_attr_get_search_ctx(ni, mrec);
> +    if (!actx)
> +    {
> +        goto err_out;
> +    }
> +    while (!ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, CASE_SENSITIVE,
> +            0, NULL, 0, actx)) {
> +/*debuger need...
> +        char *s;
> +        **/
> +        BOOL case_sensitive = IGNORE_CASE;
> +
> +        fn = (FILE_NAME_ATTR*)((u8*)actx->attr +
> +                le16_to_cpu(actx->attr->data.resident.value_offset));
> +/*debuger need...
> +        s = ntfs_attr_name_get(fn->file_name, fn->file_name_length);
> +        ntfs_debug("name: '%s'  type: %d  dos: %d  win32: %d  "
> +                   "case: %d\n", s, fn->file_name_type,
> +                   looking_for_dos_name, looking_for_win32_name,
> +                   case_sensitive_match);
> +        ntfs_attr_name_free(&s);
> +        */
> +/** all ways use posix name
> +        if (looking_for_dos_name) {
> +            if (fn->file_name_type == FILE_NAME_DOS)
> +                break;
> +            else
> +                continue;
> +        }
> +        if (looking_for_win32_name) {
> +            if  (fn->file_name_type == FILE_NAME_WIN32)
> +                break;
> +            else
> +                continue;
> +        }
> +        **/
> +        
> +        /* Ignore hard links from other directories */
> +        if (dir_ni->mft_no != MREF_LE(fn->parent_directory)) {
> +            ntfs_debug("MFT record numbers don't match "
> +                       "(%llu != %llu)",
> +                       (long long unsigned)dir_ni->mft_no,
> +                       (long long unsigned)MREF_LE(fn->parent_directory));
> +            continue;
> +        }
> +            
> +/****all ways use posix case
> +        if (fn->file_name_type == FILE_NAME_POSIX || case_sensitive_match)
> +        */
> +            case_sensitive = CASE_SENSITIVE;
> +        
> +        /** all ways think name is equal
> +        if (ntfs_names_are_equal(fn->file_name, fn->file_name_length,
> +                     name, name_len, case_sensitive,
> +                     ni->vol->upcase, ni->vol->upcase_len))*/ {
> +            
> +            /**  all ways think it`s posix name...
> +            if (fn->file_name_type == FILE_NAME_WIN32) {
> +                looking_for_dos_name = TRUE;
> +                ntfs_attr_reinit_search_ctx(actx);
> +                continue;
> +            }
> +            if (fn->file_name_type == FILE_NAME_DOS)
> +                looking_for_dos_name = TRUE;
> +                */
> +            break;
> +        }
> +    }
> +/******************************************* get fn ******************/
> +    
> +/*********** cut the entry down *****************/
> +    if ( (err = ntfs_index_remove(dir_ni, fn, le32_to_cpu(actx->attr->data.resident.value_length)) ) )
> +    {
> +        ntfs_debug("ntfs_index_remove error.");
> +        goto err_out;
> +    }
> +    
> +    mrec->link_count = cpu_to_le16(le16_to_cpu( mrec->link_count) - 1);
> +/*********** cut the entry down *****************/
> +
> +
> +/********************flush ***************/
> +    flush_dcache_mft_record_page(ni);
> +    mark_mft_record_dirty(ni);
> +/********************flush end ***************/
> +
> +/********************cut down all run list ***************/
> +    ntfs_attr_put_search_ctx(actx);
> +    actx = ntfs_attr_get_search_ctx(ni, mrec);
> +    
> +    err=  STATUS_OK ;
> +    ntfs_debug("Before while ");
> +    while (!ntfs_attrs_walk(actx))
> +    {
> +        ntfs_debug("new loop ");
> +        if (actx->attr->non_resident)
> +        {
> +            ntfs_debug("Inner case ");
> +            /*
> +            runlist *rl;
> +            rl = ntfs_mapping_pairs_decompress(ni->vol, actx->attr,
> +                    NULL);
> +            if (!rl) {
> +                err = -EOPNOTSUPP ;
> +                ntfs_debug("Failed to decompress runlist.  "
> +                        "Leaving inconsistent metadata.\n");
> +                continue;
> +            }
> +            if (ntfs_cluster_free_from_rl(ni->vol, rl)) {
> +                err = -EOPNOTSUPP ;
> +                ntfs_debug("Failed to free clusters.  "
> +                        "Leaving inconsistent metadata.\n");
> +                continue;
> +            }
> +            free(rl);
> +            */
> +
> +            /* alloc_change < 0 */
> +            /* Free the clusters.
> +            err = ntfs_cluster_free(ni, 0, -1, actx);*/
> +            
> +            ntfs_debug("before __ntfs_cluster_free ");
> +            err = __ntfs_cluster_free(ni, 0, -1, actx, false);
> +            if (unlikely(err < 0))
> +            {
> +                ntfs_error(vol->sb, "Failed to release cluster(s) (error code "
> +                        "%lli).  Unmount and run chkdsk to recover "
> +                        "the lost cluster(s).", (long long)err);
> +                NVolSetErrors(vol);
> +                err = 0;
> +            }
> +            /* Truncate the runlist.  NO NEED
> +            err = ntfs_rl_truncate_nolock(vol, &ni->runlist, 0);
> +            if (unlikely(err || IS_ERR(actx->mrec)))
> +            {
> +                ntfs_error(vol->sb, "Failed to %s (error code %li).%s",
> +                        IS_ERR(actx->mrec) ?
> +                        "restore attribute search context" :
> +                        "truncate attribute runlist",
> +                        IS_ERR(actx->mrec) ? PTR_ERR(actx->mrec) : err, es);
> +                err = -EIO;
> +                goto err_out;
> +            }*/
> +            ntfs_debug("before flush_dcache_mft_record_page actx->ntfs_ino[%p]",actx->ntfs_ino);
> +            flush_dcache_mft_record_page(actx->ntfs_ino);
> +            ntfs_debug("before mark_mft_record_dirty actx->ntfs_ino[%p]",actx->ntfs_ino);
> +            mark_mft_record_dirty(actx->ntfs_ino);
> +        }
> +    }
> +    if (err )
> +    {
> +        ntfs_debug("Attribute enumeration failed.  "
> +                "Probably leaving inconsistent metadata.\n");
> +    }
> +    /* All extents should be attached after attribute walk. */
> +    while (ni->nr_extents)
> +    {
> +        ntfs_error(vol->sb,"need use ntfs_extent_mft_record_free. not support now ");
> +        /**FIXME
> +        if ( ( err = ntfs_mft_record_free(ni->vol, *(ni->extent_nis) )))
> +        {
> +            ntfs_debug("Failed to free extent MFT record.  "
> +                    "Leaving inconsistent metadata.\n");
> +        }
> +        **/
> +    }
> +
> +    if (ntfs_mft_record_free(ni->vol, ni,mrec))
> +    {
> +        err = -EIO;
> +        ntfs_debug("Failed to free base MFT record.  "
> +                "Leaving inconsistent metadata.\n");
> +    }
> +    /* FIXME */
> +
> +    flush_dcache_mft_record_page(ni);
> +    mark_mft_record_dirty(ni);
> +    /*FIXME:Gzged add */
> +    ntfs_debug("before unmap_mft_record.");
> +    unmap_mft_record(ni);
> +    ni = NULL;
> +    mrec=NULL;
> +
> +
> +    /** Gzged shut now
> +    ntfs_inode_update_times(dir_ni, NTFS_UPDATE_MCTIME);
> +    */
> +err_out:
> +    if (actx)
> +        ntfs_attr_put_search_ctx(actx);
> +    if (mrec)
> +        unmap_mft_record(ni);
> +    if (err)
> +    {
> +        ntfs_debug("Could not delete file");
> +        return err;
> +    }
> +    else
> +    {
> +        ntfs_debug("Done.");
> +        return 0;
> +    }
> +}
> +
> +static int ntfs_unlink_inode(struct inode *pi,struct dentry *pd)
> +{
> +    ntfs_inode* ni = NTFS_I(pd->d_inode);
> +    int err = -ENOENT;
> +
> +    ntfs_debug("Entering");
> +    BUG_ON(!ni);
> +
> +    err =   ntfs_delete(ni,NTFS_I(pi));
> +    if(err)
> +    {
> +        ntfs_debug("Faile");
> +        return err;
> +    }
> +    else
> +    {
> +        (VFS_I(ni))->i_ctime = pi->i_ctime;
> +        inode_dec_link_count(VFS_I(ni)); //(VFS_I(ni))->i_nlink--;
> +        ntfs_debug("Done");
> +        return  err;
> +    }
> +}
> /**
>  * Inode operations for directories.
>  */
> const struct inode_operations ntfs_dir_inode_ops = {
>    .lookup    = ntfs_lookup,    /* VFS: Lookup directory. */
> +    .create = ntfs_create_inode,
> +    .unlink = ntfs_unlink_inode,
> };
>
> /**
> --
> 2.9.3
>

------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Linux-NTFS-Dev mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/linux-ntfs-dev
Loading...