VServer 1.9.2 (patch-2.6.8.1-vs1.9.2.diff)
[linux-2.6.git] / fs / ntfs / super.c
index 4c3a32b..e494285 100644 (file)
@@ -32,6 +32,9 @@
 #include "ntfs.h"
 #include "sysctl.h"
 #include "logfile.h"
+#include "quota.h"
+#include "dir.h"
+#include "index.h"
 
 /* Number of mounted file systems which have compression enabled. */
 static unsigned long ntfs_nr_compression_users;
@@ -413,7 +416,8 @@ static int ntfs_remount(struct super_block *sb, int *flags, char *opt)
         * flags are set.  Also, empty the logfile journal as it would become
         * stale as soon as something is written to the volume and mark the
         * volume dirty so that chkdsk is run if the volume is not umounted
-        * cleanly.
+        * cleanly.  Finally, mark the quotas out of date so Windows rescans
+        * the volume on boot and updates them.
         *
         * When remounting read-only, mark the volume clean if no volume errors
         * have occured.
@@ -460,6 +464,12 @@ static int ntfs_remount(struct super_block *sb, int *flags, char *opt)
                        NVolSetErrors(vol);
                        return -EROFS;
                }
+               if (!ntfs_mark_quotas_out_of_date(vol)) {
+                       ntfs_error(sb, "Failed to mark quotas out of date%s",
+                                       es);
+                       NVolSetErrors(vol);
+                       return -EROFS;
+               }
        } else if (!(sb->s_flags & MS_RDONLY) && (*flags & MS_RDONLY)) {
                /* Remounting read-only. */
                if (!NVolErrors(vol)) {
@@ -875,6 +885,7 @@ static BOOL load_and_init_mft_mirror(ntfs_volume *vol)
        struct inode *tmp_ino;
        ntfs_inode *tmp_ni;
 
+       ntfs_debug("Entering.");
        /* Get mft mirror inode. */
        tmp_ino = ntfs_iget(vol->sb, FILE_MFTMirr);
        if (IS_ERR(tmp_ino) || is_bad_inode(tmp_ino)) {
@@ -906,6 +917,7 @@ static BOOL load_and_init_mft_mirror(ntfs_volume *vol)
        tmp_ni->itype.index.block_size = vol->mft_record_size;
        tmp_ni->itype.index.block_size_bits = vol->mft_record_size_bits;
        vol->mftmirr_ino = tmp_ino;
+       ntfs_debug("Done.");
        return TRUE;
 }
 
@@ -1054,6 +1066,76 @@ static BOOL load_and_check_logfile(ntfs_volume *vol)
        return TRUE;
 }
 
+/**
+ * load_and_init_quota - load and setup the quota file for a volume if present
+ * @vol:       ntfs super block describing device whose quota file to load
+ *
+ * Return TRUE on success or FALSE on error.  If $Quota is not present, we
+ * leave vol->quota_ino as NULL and return success.
+ */
+static BOOL load_and_init_quota(ntfs_volume *vol)
+{
+       MFT_REF mref;
+       struct inode *tmp_ino;
+       ntfs_name *name = NULL;
+       static const ntfschar Quota[7] = { const_cpu_to_le16('$'),
+                       const_cpu_to_le16('Q'), const_cpu_to_le16('u'),
+                       const_cpu_to_le16('o'), const_cpu_to_le16('t'),
+                       const_cpu_to_le16('a'), const_cpu_to_le16(0) };
+       static ntfschar Q[3] = { const_cpu_to_le16('$'),
+                       const_cpu_to_le16('Q'), const_cpu_to_le16(0) };
+
+       ntfs_debug("Entering.");
+       /*
+        * Find the inode number for the quota file by looking up the filename
+        * $Quota in the extended system files directory $Extend.
+        */
+       down(&vol->extend_ino->i_sem);
+       mref = ntfs_lookup_inode_by_name(NTFS_I(vol->extend_ino), Quota, 6,
+                       &name);
+       up(&vol->extend_ino->i_sem);
+       if (IS_ERR_MREF(mref)) {
+               /*
+                * If the file does not exist, quotas are disabled and have
+                * never been enabled on this volume, just return success.
+                */
+               if (MREF_ERR(mref) == -ENOENT) {
+                       ntfs_debug("$Quota not present.  Volume does not have "
+                                       "quotas enabled.");
+                       /*
+                        * No need to try to set quotas out of date if they are
+                        * not enabled.
+                        */
+                       NVolSetQuotaOutOfDate(vol);
+                       return TRUE;
+               }
+               /* A real error occured. */
+               ntfs_error(vol->sb, "Failed to find inode number for $Quota.");
+               return FALSE;
+       }
+       /* We do not care for the type of match that was found. */
+       if (name)
+               kfree(name);
+       /* Get the inode. */
+       tmp_ino = ntfs_iget(vol->sb, MREF(mref));
+       if (IS_ERR(tmp_ino) || is_bad_inode(tmp_ino)) {
+               if (!IS_ERR(tmp_ino))
+                       iput(tmp_ino);
+               ntfs_error(vol->sb, "Failed to load $Quota.");
+               return FALSE;
+       }
+       vol->quota_ino = tmp_ino;
+       /* Get the $Q index allocation attribute. */
+       tmp_ino = ntfs_index_iget(vol->quota_ino, Q, 2);
+       if (IS_ERR(tmp_ino)) {
+               ntfs_error(vol->sb, "Failed to load $Quota/$Q index.");
+               return FALSE;
+       }
+       vol->quota_q_ino = tmp_ino;
+       ntfs_debug("Done.");
+       return TRUE;
+}
+
 #endif /* NTFS_RW */
 
 /**
@@ -1108,7 +1190,7 @@ read_partial_upcase_page:
                        goto read_partial_upcase_page;
        }
        vol->upcase_len = ino->i_size >> UCHAR_T_SIZE_BITS;
-       ntfs_debug("Read %llu bytes from $UpCase (expected %u bytes).",
+       ntfs_debug("Read %llu bytes from $UpCase (expected %zu bytes).",
                        ino->i_size, 64 * 1024 * sizeof(ntfschar));
        iput(ino);
        down(&ntfs_lock);
@@ -1436,20 +1518,66 @@ get_ctx_vol_failed:
        }
        // FIXME: Initialize security.
        /* Get the extended system files' directory inode. */
-       tmp_ino = ntfs_iget(sb, FILE_Extend);
-       if (IS_ERR(tmp_ino) || is_bad_inode(tmp_ino)) {
-               if (!IS_ERR(tmp_ino))
-                       iput(tmp_ino);
+       vol->extend_ino = ntfs_iget(sb, FILE_Extend);
+       if (IS_ERR(vol->extend_ino) || is_bad_inode(vol->extend_ino)) {
+               if (!IS_ERR(vol->extend_ino))
+                       iput(vol->extend_ino);
                ntfs_error(sb, "Failed to load $Extend.");
                goto iput_sec_err_out;
        }
-       // FIXME: Do something. E.g. want to delete the $UsnJrnl if exists.
-       // Note we might be doing this at the wrong level; we might want to
-       // d_alloc_root() and then do a "normal" open(2) of $Extend\$UsnJrnl
-       // rather than using ntfs_iget here, as we don't know the inode number
-       // for the files in $Extend directory.
-       iput(tmp_ino);
+#ifdef NTFS_RW
+       /* Find the quota file, load it if present, and set it up. */
+       if (!load_and_init_quota(vol)) {
+               static const char *es1 = "Failed to load $Quota";
+               static const char *es2 = ".  Run chkdsk.";
+
+               /* If a read-write mount, convert it to a read-only mount. */
+               if (!(sb->s_flags & MS_RDONLY)) {
+                       if (!(vol->on_errors & (ON_ERRORS_REMOUNT_RO |
+                                       ON_ERRORS_CONTINUE))) {
+                               ntfs_error(sb, "%s and neither on_errors="
+                                               "continue nor on_errors="
+                                               "remount-ro was specified%s",
+                                               es1, es2);
+                               goto iput_quota_err_out;
+                       }
+                       sb->s_flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME;
+                       ntfs_error(sb, "%s.  Mounting read-only%s", es1, es2);
+               } else
+                       ntfs_warning(sb, "%s.  Will not be able to remount "
+                                       "read-write%s", es1, es2);
+               /* This will prevent a read-write remount. */
+               NVolSetErrors(vol);
+       }
+       /* If (still) a read-write mount, mark the quotas out of date. */
+       if (!(sb->s_flags & MS_RDONLY) &&
+                       !ntfs_mark_quotas_out_of_date(vol)) {
+               static const char *es1 = "Failed to mark quotas out of date";
+               static const char *es2 = ".  Run chkdsk.";
+
+               /* Convert to a read-only mount. */
+               if (!(vol->on_errors & (ON_ERRORS_REMOUNT_RO |
+                               ON_ERRORS_CONTINUE))) {
+                       ntfs_error(sb, "%s and neither on_errors=continue nor "
+                                       "on_errors=remount-ro was specified%s",
+                                       es1, es2);
+                       goto iput_quota_err_out;
+               }
+               ntfs_error(sb, "%s.  Mounting read-only%s", es1, es2);
+               sb->s_flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME;
+               NVolSetErrors(vol);
+       }
+       // TODO: Delete or checkpoint the $UsnJrnl if it exists.
+#endif /* NTFS_RW */
        return TRUE;
+#ifdef NTFS_RW
+iput_quota_err_out:
+       if (vol->quota_q_ino)
+               iput(vol->quota_q_ino);
+       if (vol->quota_ino)
+               iput(vol->quota_ino);
+       iput(vol->extend_ino);
+#endif /* NTFS_RW */
 iput_sec_err_out:
        iput(vol->secure_ino);
 iput_root_err_out:
@@ -1496,6 +1624,12 @@ static void ntfs_put_super(struct super_block *sb)
 
        /* NTFS 3.0+ specific. */
        if (vol->major_ver >= 3) {
+               if (vol->quota_q_ino)
+                       ntfs_commit_inode(vol->quota_q_ino);
+               if (vol->quota_ino)
+                       ntfs_commit_inode(vol->quota_ino);
+               if (vol->extend_ino)
+                       ntfs_commit_inode(vol->extend_ino);
                if (vol->secure_ino)
                        ntfs_commit_inode(vol->secure_ino);
        }
@@ -1544,6 +1678,20 @@ static void ntfs_put_super(struct super_block *sb)
 
        /* NTFS 3.0+ specific clean up. */
        if (vol->major_ver >= 3) {
+#ifdef NTFS_RW
+               if (vol->quota_q_ino) {
+                       iput(vol->quota_q_ino);
+                       vol->quota_q_ino = NULL;
+               }
+               if (vol->quota_ino) {
+                       iput(vol->quota_ino);
+                       vol->quota_ino = NULL;
+               }
+#endif /* NTFS_RW */
+               if (vol->extend_ino) {
+                       iput(vol->extend_ino);
+                       vol->extend_ino = NULL;
+               }
                if (vol->secure_ino) {
                        iput(vol->secure_ino);
                        vol->secure_ino = NULL;
@@ -2018,7 +2166,6 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent)
        init_rwsem(&vol->mftbmp_lock);
 #ifdef NTFS_RW
        vol->mftmirr_ino = NULL;
-       vol->mftmirr_size = 0;
        vol->logfile_ino = NULL;
 #endif /* NTFS_RW */
        vol->lcnbmp_ino = NULL;
@@ -2026,10 +2173,11 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent)
        vol->vol_ino = NULL;
        vol->root_ino = NULL;
        vol->secure_ino = NULL;
-       vol->uid = vol->gid = 0;
-       vol->flags = 0;
-       vol->on_errors = 0;
-       vol->mft_zone_multiplier = 0;
+       vol->extend_ino = NULL;
+#ifdef NTFS_RW
+       vol->quota_ino = NULL;
+       vol->quota_q_ino = NULL;
+#endif /* NTFS_RW */
        vol->nls_map = NULL;
 
        /*
@@ -2178,23 +2326,48 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent)
        }
        ntfs_error(sb, "Failed to allocate root directory.");
        /* Clean up after the successful load_system_files() call from above. */
+       // TODO: Use ntfs_put_super() instead of repeating all this code...
+       // FIXME: Should mark the volume clean as the error is most likely
+       //        -ENOMEM.
        iput(vol->vol_ino);
        vol->vol_ino = NULL;
        /* NTFS 3.0+ specific clean up. */
        if (vol->major_ver >= 3) {
-               iput(vol->secure_ino);
-               vol->secure_ino = NULL;
+#ifdef NTFS_RW
+               if (vol->quota_q_ino) {
+                       iput(vol->quota_q_ino);
+                       vol->quota_q_ino = NULL;
+               }
+               if (vol->quota_ino) {
+                       iput(vol->quota_ino);
+                       vol->quota_ino = NULL;
+               }
+#endif /* NTFS_RW */
+               if (vol->extend_ino) {
+                       iput(vol->extend_ino);
+                       vol->extend_ino = NULL;
+               }
+               if (vol->secure_ino) {
+                       iput(vol->secure_ino);
+                       vol->secure_ino = NULL;
+               }
        }
        iput(vol->root_ino);
        vol->root_ino = NULL;
        iput(vol->lcnbmp_ino);
        vol->lcnbmp_ino = NULL;
-#ifdef NTFS_RW
-       iput(vol->mftmirr_ino);
-       vol->mftmirr_ino = NULL;
-#endif /* NTFS_RW */
        iput(vol->mftbmp_ino);
        vol->mftbmp_ino = NULL;
+#ifdef NTFS_RW
+       if (vol->logfile_ino) {
+               iput(vol->logfile_ino);
+               vol->logfile_ino = NULL;
+       }
+       if (vol->mftmirr_ino) {
+               iput(vol->mftmirr_ino);
+               vol->mftmirr_ino = NULL;
+       }
+#endif /* NTFS_RW */
        vol->upcase_len = 0;
        if (vol->upcase != default_upcase)
                ntfs_free(vol->upcase);
@@ -2220,10 +2393,9 @@ unl_upcase_iput_tmp_ino_err_out_now:
        up(&ntfs_lock);
 iput_tmp_ino_err_out_now:
        iput(tmp_ino);
-       if (vol->mft_ino && vol->mft_ino != tmp_ino) {
+       if (vol->mft_ino && vol->mft_ino != tmp_ino)
                iput(vol->mft_ino);
-               vol->mft_ino = NULL;
-       }
+       vol->mft_ino = NULL;
        /*
         * This is needed to get ntfs_clear_extent_inode() called for each
         * inode we have ever called ntfs_iget()/iput() on, otherwise we A)
@@ -2270,10 +2442,11 @@ static void ntfs_big_inode_init_once(void *foo, kmem_cache_t *cachep,
 }
 
 /*
- * Slab cache to optimize allocations and deallocations of attribute search
- * contexts.
+ * Slab caches to optimize allocations and deallocations of attribute search
+ * contexts and index contexts, respectively.
  */
 kmem_cache_t *ntfs_attr_ctx_cache;
+kmem_cache_t *ntfs_index_ctx_cache;
 
 /* A global default upcase table and a corresponding reference count. */
 wchar_t *default_upcase = NULL;
@@ -2300,6 +2473,7 @@ static struct file_system_type ntfs_fs_type = {
 };
 
 /* Stable names for the slab caches. */
+static const char ntfs_index_ctx_cache_name[] = "ntfs_index_ctx_cache";
 static const char ntfs_attr_ctx_cache_name[] = "ntfs_attr_ctx_cache";
 static const char ntfs_name_cache_name[] = "ntfs_name_cache";
 static const char ntfs_inode_cache_name[] = "ntfs_inode_cache";
@@ -2326,13 +2500,21 @@ static int __init init_ntfs_fs(void)
 
        ntfs_debug("Debug messages are enabled.");
 
+       ntfs_index_ctx_cache = kmem_cache_create(ntfs_index_ctx_cache_name,
+                       sizeof(ntfs_index_context), 0 /* offset */,
+                       SLAB_HWCACHE_ALIGN, NULL /* ctor */, NULL /* dtor */);
+       if (!ntfs_index_ctx_cache) {
+               printk(KERN_CRIT "NTFS: Failed to create %s!\n",
+                               ntfs_index_ctx_cache_name);
+               goto ictx_err_out;
+       }
        ntfs_attr_ctx_cache = kmem_cache_create(ntfs_attr_ctx_cache_name,
                        sizeof(attr_search_context), 0 /* offset */,
                        SLAB_HWCACHE_ALIGN, NULL /* ctor */, NULL /* dtor */);
        if (!ntfs_attr_ctx_cache) {
                printk(KERN_CRIT "NTFS: Failed to create %s!\n",
                                ntfs_attr_ctx_cache_name);
-               goto ctx_err_out;
+               goto actx_err_out;
        }
 
        ntfs_name_cache = kmem_cache_create(ntfs_name_cache_name,
@@ -2385,7 +2567,9 @@ inode_err_out:
        kmem_cache_destroy(ntfs_name_cache);
 name_err_out:
        kmem_cache_destroy(ntfs_attr_ctx_cache);
-ctx_err_out:
+actx_err_out:
+       kmem_cache_destroy(ntfs_index_ctx_cache);
+ictx_err_out:
        if (!err) {
                printk(KERN_CRIT "NTFS: Aborting NTFS file system driver "
                                "registration...\n");
@@ -2414,6 +2598,9 @@ static void __exit exit_ntfs_fs(void)
        if (kmem_cache_destroy(ntfs_attr_ctx_cache) && (err = 1))
                printk(KERN_CRIT "NTFS: Failed to destory %s.\n",
                                ntfs_attr_ctx_cache_name);
+       if (kmem_cache_destroy(ntfs_index_ctx_cache) && (err = 1))
+               printk(KERN_CRIT "NTFS: Failed to destory %s.\n",
+                               ntfs_index_ctx_cache_name);
        if (err)
                printk(KERN_CRIT "NTFS: This causes memory to leak! There is "
                                "probably a BUG in the driver! Please report "