Changes by: antona
Update of /cvsroot/linux-ntfs/linux-ntfs/libntfs
In directory usw-pr-cvs1:/tmp/cvs-serv4600/libntfs
Modified Files:
mft.c volume.c
Log Message:
More or less finished file handling. (Probably some useful functions are
still missing but they will be implemented as need arises.)
One thing that is stupid at the moment is we don't limit the amount of
cached mft_records so if you were to load loads the machine would eventually
run out of memory... Can't happen with files as they are limited to 1000,
unless you are short of memory. (Hard limit at the moment, set in ntfs_mount().
Maybe ntfsd should be monitoring memory usage and be throwing out unused cache
entries and closed_files? That would mean to have locking everywhere, though.)
Still missing: - Convert old code to use new stuff. - Add non-resident
attributes somewhere. Either into the mft_entry structure or into the
ntfs_file structure, but which? At the moment I tend to mft_entry so they can
be synced together with the entries by ntfsd.
Index: mft.c
===================================================================
RCS file: /cvsroot/linux-ntfs/linux-ntfs/libntfs/mft.c,v
retrieving revision 1.5
retrieving revision 1.6
diff -U2 -r1.5 -r1.6
--- mft.c 2001/04/08 01:58:29 1.5
+++ mft.c 2001/04/09 00:05:37 1.6
@@ -144,5 +144,5 @@
__inline__ int __free_mft_entry(mft_entry *me)
{
- if (spin_is_locked(&me->m_lock) || atomic_read(me->count) ||
+ if (spin_is_locked(&me->m_lock) || atomic_read(&me->count) ||
MftEntryDirty(me) || MftEntryMapped(me)) {
return -EBUSY;
@@ -263,6 +263,7 @@
}
-int insert_mft_entry(ntfs_volume *vol, mft_entry *me, const MFT_REFERENCE mref,
- const MFT_RECORD *mrec, const BOOL dirty)
+int insert_mft_entry(ntfs_volume *vol, mft_entry **me, const MFT_REFERENCE mref,
+ const MFT_RECORD *mrec, const BOOL dirty,
+ const ntfs_file *f)
{
struct list_head *tmp;
@@ -270,4 +271,6 @@
int i;
+ if (!*me && !__allocate_mft_entry())
+ return -errno;
*(__u64*)&me->m_ref = mref;
me->m_vol = vol;
@@ -279,9 +282,4 @@
} else
__map_mft_entry(me);
- i = 0;
- while (!spin_trylock(&vol->mft_entries_lock) && i++ < 100)
- sched_yield();
- if (!spin_is_locked(&vol->mft_entries_lock))
- return -EDEADLK;
list_for_each(tmp, &vol->mft_entries) {
m = list_entry(tmp, mft_entry, m_list);
@@ -296,8 +294,9 @@
break;
}
- spin_unlock(&vol->mft_entries_lock);
fprintf(stderr, "Linux-NTFS: BUG! insert_mft_entry(): mft "
"entry already present in volume.\n");
- if (m->m_ref.sequence_number != me->m_ref.sequence_number)
+ if (m->m_ref.sequence_number != me->m_ref.sequence_number &&
+ m->m_ref.sequence_number != 0 &&
+ me->m_ref.sequence_number != 0)
fprintf(stderr, "On top of this, the sequence numbers "
"are mismatched.\n");
@@ -305,5 +304,7 @@
}
vol->nr_mft_entries++;
- spin_unlock(&vol->mft_entries_lock);
+ me->m_file = f;
+ if (f)
+ atomic_inc(&me->m_file);
if (!MftEntryDirty(me))
return 0;
@@ -344,6 +345,10 @@
spin_unlock(&me->m_lock);
return err < 0 ? err : -EBUSY;
+ }
+ if (me->m_file && (err = __remove_mft_entry_from_ntfs_file(me))) {
+ spin_unlock(&me->m_lock);
+ return err < 0 ? err : -EBUSY;
}
- if (atomic_read(me->count)) {
+ if (atomic_read(&me->count)) {
spin_unlock(&me->m_lock);
return -EBUSY;
@@ -356,33 +361,313 @@
return -EDEADLK;
}
- i = 0;
- while (!spin_trylock(&vol->mft_entries_lock) && i++ < 100)
- sched_yield();
- if (!spin_is_locked(&vol->mft_entries_lock)) {
- spin_unlock(&me->m_rec_lock);
- spin_unlock(&me->m_lock);
- return -EDEADLK;
- }
- list_del(&me->m_list);
- vol->nr_mft_entries--;
- spin_unlock(&vol->mft_entries_lock);
free(me->m_rec);
ClearMftEntryMapped(me);
spin_unlock(&me->m_rec_lock);
- spin_unlock(&me->m_rec);
+ list_del(&me->m_list);
+ vol->nr_mft_entries--;
+ spin_unlock(&me->m_lock);
free(me);
return 0;
}
+
+__inline__ ntfs_file *__allocate_ntfs_file(void)
+{
+ ntfs_file *f = (ntfs_file*)malloc(sizeof(ntfs_file));
+ if (!f)
+ return NULL;
+ memset(f, 0, sizeof(ntfs_file));
+ &f->f_count = ATOMIC_INIT(0);
+ INIT_LIST_HEAD(f->f_list);
+ return f;
+}
+
+__inline__ int __free_ntfs_file(ntfs_file *f)
+{
+ if (atomic_read(&f->f_count) || f->nr_m_refs)
+ return -EBUSY;
+ free(f);
+ return 0;
+}
+
+int __remove_all_extension_records_from_ntfs_file(ntfs_file *f)
+{
+ int i;
+ char *t;
+ mft_entry *m;
+
+ if (atomic_read(&f->f_count))
+ return - EBUSY;
+ for (i = f->nr_m_refs - 1; i > 0; i--) {
+ m = f->m_entries[i];
+ if (remove_mft_entry(m) && m->m_file)
+ return -EBUSY;
+ f->nr_m_refs--;
+ t = realloc(f->m_refs, f->nr_m_refs * sizeof(MFT_REFERENCE));
+ if (t)
+ f->m_refs = (MFT_REFERENCE*)t;
+ t = realloc(f->m_entries, f->nr_m_refs * sizeof(mft_entry*));
+ if (t)
+ f->m_entries = (mft_entry**)t;
+ }
+ return 0;
+}
-ntfs_file *ntfs_open_by_mref(ntfs_volume *vol, const MFT_REFERENCE *mref)
+int __add_mtf_entry_to_ntfs_file(ntfs_file *f, mft_entry *me)
{
- errno = -ENOTSUP;
+ int i;
+ void *t;
+
+ if (!MftEntryMapped(me) && (i = __map_mft_entry(me)))
+ return i;
+ if (me->m_file == f)
+ return 0;
+ atomic_inc(me->m_count);
+ me->m_file = f;
+ for (i = 0; i < f->nr_m_refs; i++)
+ if (f->m_refs[i] & MFT_REFERENCE_MASK_CPU ==
+ (__u64)me->m_ref & MFT_REFERENCE_MASK_CPU)
+ return 0;
+ f->nr_m_refs++;
+ t = realloc(f->m_refs, f->nr_m_refs * sizeof(MFT_REFERENCE));
+ if (!t)
+ goto undo_add_ret;
+ f->m_refs = t;
+ t = realloc(f->m_entries, f->nr_m_refs * sizeof(mft_entry*));
+ if (!t) {
+ t = realloc(f->m_refs, (f->nr_m_refs - 1) *
+ sizeof(MFT_REFERENCE));
+ if (t)
+ f->m_refs = t;
+ goto undo_add_ret;
+ }
+ f->m_entries = t;
+ f->m_refs[f->nr_m_refs - 1] = me->m_ref;
+ f->m_entries[f->nr_m_refs - 1] = me;
+ return 0;
+undo_add_ret:
+ f->nr_m_refs--;
+ atomic_dec(me->m_count);
+ me->m_file = NULL;
+ return -errno;
+}
+
+int __remove_mft_entry_from_ntfs_file(mft_entry *me)
+{
+ int i;
+ MFT_REFERENCE mref = 0ULL;
+ ntfs_file *f = me->m_file;
+ if (!f)
+ return 0;
+ if (f->f_count)
+ return -EBUSY;
+ if (f->nr_m_refs <= 0)
+ goto not_there;
+ for (i = 0; i < f->nr_m_refs; i++) {
+ if ((__u64)me->m_ref & MFT_REFERENCE_MASK_CPU ==
+ (__u64)f->m_refs[i] & MFT_REFERENCE_MASK_CPU) {
+ mref = f->m_refs[i];
+ break;
+ }
+ }
+ if (!(__u64)mref)
+ goto not_there;
+ if (me->m_ref.sequence_number != mref.sequence_number &&
+ me->m_ref.sequence_number != 0 && mref.sequence_number != 0)
+ fprintf(stderr, "Linux-NTFS: BUG() "
+ "__remove_mft_entry_from_file(): Sequence "
+ "number mismatch. Removing anyway.\n");
+ /* Not a base record. Just take it out. */
+ if (i) {
+ void *t;
+ f->nr_m_refs--;
+ if (i < f->nr_m_refs) {
+ memmove(f->m_refs + i, f->m_refs + i + 1,
+ (f->nr_m_refs - i) * sizeof(MFT_REFERENCE));
+ memmove(f->m_entries + i, f->m_entries + i + 1,
+ (f->nr_m_refs - i) * sizeof(mft_entry*));
+ }
+ t = realloc(f->m_refs, f->nr_m_refs * sizeof(MFT_REFERENCE));
+ if (t)
+ f->m_refs = (MFT_REFERENCE*)t;
+ t = realloc(f->m_entries, f->nr_m_refs * sizeof(mft_entry*));
+ if (t)
+ f->m_entries = (mft_entry*)t;
+ goto finished;
+ }
+ /* This is the base record, we need to remove the file as well. */
+ if (f->nr_m_refs > 1) {
+ /* There are extension records, need to remove them all. */
+ if (i = __remove_all_extension_records_from_ntfs_file(f))
+ return i < 0 ? i : -EBUSY;
+ }
+ /* No extension records (left), kill the file. */
+ list_del(&f->f_list);
+ f->f_vol->nr_closed_files--;
+ free(f->m_refs)
+ free(f->m_entries);
+ free(f);
+finished:
+ atomic_dec(&me->m_count);
+ me->m_file = NULL;
+ return 0;
+not_there:
+ fprintf(stderr, "Linux-NTFS: BUG() "
+ "__remove_mft_entry_from_ntfs_file(): Mft entry "
+ "points to file, but not vice versa. Fixed.\n");
+ goto finished;
+}
+
+int remove_closed_ntfs_file(ntfs_file *f)
+{
+ mft_entry *m = f->m_entries[0];
+
+ if (__remove_all_extension_records_from_ntfs_file(f))
+ return -EBUSY;
+ /* Even on error, it is still possible the file has been killed. */
+ if (remove_mft_entry(m) && m->m_file)
+ return -EBUSY;
+ return 0;
+}
+
+int __insert_open_ntfs_file(ntfs_volume *v, ntfs_file *f, mft_entry *m)
+{
+ /* Add m to f. */
+ f->m_refs = (MFT_REFERENCE*)malloc(sizeof(MFT_REFERENCE));
+ if (!f->m_refs)
+ return -errno;
+ f->m_entries = (mft_entry**)malloc(sizeof(mft_entry*));
+ if (!f->m_entries) {
+ free(f->m_refs);
+ f->m_refs = NULL;
+ return -errno;
+ }
+ f->m_refs[0] = m->m_ref;
+ f->m_entries[0] = m;
+ /* Add f to m. */
+ m->m_file = f;
+ atomic_inc(&m->m_count);
+ /* Add v to f and vice versa. */
+ atomic_inc(&f->f_count);
+ f->f_vol = v;
+ v->nr_open_files++;
+ list_add(&f->f_list, &v->open_files);
+ return 0;
+}
+
+ntfs_file *ntfs_open_by_mref(ntfs_volume *vol, const MFT_REFERENCE mref)
+{
+ struct list_head *tmp;
+ ntfs_file *f = NULL;
+ mft_entry *m, *have_m = NULL;
+ int i;
+
+ list_for_each(tmp, &vol->mft_entries) {
+ m = list_entry(tmp, mft_entry, m_list);
+ if ((__u64)m->m_ref & MFT_REFERENCE_MASK_CPU <
+ (__u64)mref & MFT_REFERENCE_MASK_CPU)
+ continue;
+ if ((__u64)m->m_ref & MFT_REFERENCE_MASK_CPU >
+ (__u64)mref & MFT_REFERENCE_MASK_CPU)
+ break;
+ if (m->m_ref.sequence_number != mref.sequence_number &&
+ m->m_ref.sequence_number != 0 &&
+ mref.sequence_number != 0) {
+ fprintf(stderr, "Linux-NTFS: ntfs_open_by_mref(): "
+ "mft reference sequence numbers do "
+ "not match.\nRefusing to open.");
+ errno = EEXIST;
+ return NULL;
+ }
+ if (m->m_file)
+ f = m->m_file;
+ else
+ have_m = m;
+ break;
+ }
+ if (f) { /* File is in cache. */
+ for (i = 0; i < f->nr_m_refs; i++)
+ if ((__u64)f->m_refs[i] & MFT_REFERENCE_MASK_CPU ==
+ (__u64)mref & MFT_REFERENCE_MASK_CPU)
+ break;
+ /* Only base mft records can be opened as files. */
+ if (i > 0)
+ goto ret_no_entry;
+ if (!f->nr_m_refs || i == f->nr_m_refs)
+ goto err_ret_no_entry;
+ atomic_inc(&f->f_count);
+ if (atomic_read(&f->f_count) > 1)
+ return f;
+ /* File wasn't open, so move it to the open list now. */
+ list_del(&f->f_list);
+ vol->nr_closed_files--;
+ list_add(&f->f_list, &vol->open_files);
+ vol->nr_open_files++;
+ return f;
+ }
+ /* File not present in cache. Read the mft record if not in cache. */
+ if (!have_m &&
+ (i = insert_mft_entry(vol, &have_m, mref, NULL, 0, NULL))) {
+ errno = -i;
+ return NULL;
+ }
+ /* Only base mft records can be opened as files. */
+ if (have_m->m_rec->base_mft_record)
+ goto ret_no_entry;
+ /*
+ * Do we need to free some closed files? FIXME: This is probably the
+ * wrong place, but it should work, so leaving it here for now. (AIA)
+ */
+ if ((vol->nr_open_files + vol->nr_closed_files) >= vol->max_files) {
+ i = 0;
+ if (vol->nr_closed_files < 10)
+ i = vol->nr_closed_files;
+ /* Too many files open! */
+ if (i <= 0) {
+ errno = EMFILE;
+ return NULL;
+ }
+ list_for_each(tmp, &vol->closed_files) {
+ if (!remove_closed_ntfs_file(list_entry(tmp, ntfs_file,
+ f_list)))
+ i--;
+ if (i <= 0)
+ break;
+ }
+ /* Didn't manage to free any files. )-: */
+ if ((vol->nr_open_files + vol->nr_closed_files) >=
+ vol->max_files) {
+ errno = EMFILE;
+ return NULL;
+ }
+ }
+ f = __allocate_ntfs_file();
+ if (!f)
+ return NULL;
+ if (!(i = __insert_open_ntfs_file(vol, f, have_m)))
+ return f; /* Done! */
+ free(f);
+ errno = -i;
return NULL;
+err_ret_no_entry:
+ fprintf(stderr, "Linux-NTFS: BUG() ntfs_open_by_mref(): file doesn't "
+ "contain the mft reference.\n");
+ret_no_entry:
+ errno = ENOENT;
+ return NULL;
}
-int ntfs_close(ntfs_file *fref)
+int ntfs_close(ntfs_file *f)
{
- errno = -ENOTSUP;
- return -1;
+ ntfs_volume *v;
+ if (atomic_read(&f->f_count) > 0 &&
+ !atomic_dec_and_test(&f->f_count))
+ return 0;
+ atomic_set(&f->f_count, 0);
+ list_del(&f->f_list);
+ v = f->f_vol;
+ v->nr_open_files--;
+ list_add(&f->f_list, &v->closed_files);
+ v->nr_closed_files++;
+ return 0;
}
Index: volume.c
===================================================================
RCS file: /cvsroot/linux-ntfs/linux-ntfs/libntfs/volume.c,v
retrieving revision 1.11
retrieving revision 1.12
diff -U2 -r1.11 -r1.12
--- volume.c 2001/04/08 03:02:55 1.11
+++ volume.c 2001/04/09 00:05:37 1.12
@@ -52,5 +52,6 @@
}
list_for_each(tmp, &vol->dirty_mft_entries)
- flush_mft_entry(list_entry(tmp, mft_entry, m_dirty_list));
+ if (flush_mft_entry(list_entry(tmp, mft_entry, m_dirty_list)))
+ nr_err++;
spin_unlock(&vol->dirty_mft_entries_lock);
if (!nr_err)
|