162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * quota.c - NTFS kernel quota ($Quota) handling.  Part of the Linux-NTFS
462306a36Sopenharmony_ci *	     project.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (c) 2004 Anton Altaparmakov
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#ifdef NTFS_RW
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include "index.h"
1262306a36Sopenharmony_ci#include "quota.h"
1362306a36Sopenharmony_ci#include "debug.h"
1462306a36Sopenharmony_ci#include "ntfs.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci/**
1762306a36Sopenharmony_ci * ntfs_mark_quotas_out_of_date - mark the quotas out of date on an ntfs volume
1862306a36Sopenharmony_ci * @vol:	ntfs volume on which to mark the quotas out of date
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci * Mark the quotas out of date on the ntfs volume @vol and return 'true' on
2162306a36Sopenharmony_ci * success and 'false' on error.
2262306a36Sopenharmony_ci */
2362306a36Sopenharmony_cibool ntfs_mark_quotas_out_of_date(ntfs_volume *vol)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	ntfs_index_context *ictx;
2662306a36Sopenharmony_ci	QUOTA_CONTROL_ENTRY *qce;
2762306a36Sopenharmony_ci	const le32 qid = QUOTA_DEFAULTS_ID;
2862306a36Sopenharmony_ci	int err;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	ntfs_debug("Entering.");
3162306a36Sopenharmony_ci	if (NVolQuotaOutOfDate(vol))
3262306a36Sopenharmony_ci		goto done;
3362306a36Sopenharmony_ci	if (!vol->quota_ino || !vol->quota_q_ino) {
3462306a36Sopenharmony_ci		ntfs_error(vol->sb, "Quota inodes are not open.");
3562306a36Sopenharmony_ci		return false;
3662306a36Sopenharmony_ci	}
3762306a36Sopenharmony_ci	inode_lock(vol->quota_q_ino);
3862306a36Sopenharmony_ci	ictx = ntfs_index_ctx_get(NTFS_I(vol->quota_q_ino));
3962306a36Sopenharmony_ci	if (!ictx) {
4062306a36Sopenharmony_ci		ntfs_error(vol->sb, "Failed to get index context.");
4162306a36Sopenharmony_ci		goto err_out;
4262306a36Sopenharmony_ci	}
4362306a36Sopenharmony_ci	err = ntfs_index_lookup(&qid, sizeof(qid), ictx);
4462306a36Sopenharmony_ci	if (err) {
4562306a36Sopenharmony_ci		if (err == -ENOENT)
4662306a36Sopenharmony_ci			ntfs_error(vol->sb, "Quota defaults entry is not "
4762306a36Sopenharmony_ci					"present.");
4862306a36Sopenharmony_ci		else
4962306a36Sopenharmony_ci			ntfs_error(vol->sb, "Lookup of quota defaults entry "
5062306a36Sopenharmony_ci					"failed.");
5162306a36Sopenharmony_ci		goto err_out;
5262306a36Sopenharmony_ci	}
5362306a36Sopenharmony_ci	if (ictx->data_len < offsetof(QUOTA_CONTROL_ENTRY, sid)) {
5462306a36Sopenharmony_ci		ntfs_error(vol->sb, "Quota defaults entry size is invalid.  "
5562306a36Sopenharmony_ci				"Run chkdsk.");
5662306a36Sopenharmony_ci		goto err_out;
5762306a36Sopenharmony_ci	}
5862306a36Sopenharmony_ci	qce = (QUOTA_CONTROL_ENTRY*)ictx->data;
5962306a36Sopenharmony_ci	if (le32_to_cpu(qce->version) != QUOTA_VERSION) {
6062306a36Sopenharmony_ci		ntfs_error(vol->sb, "Quota defaults entry version 0x%x is not "
6162306a36Sopenharmony_ci				"supported.", le32_to_cpu(qce->version));
6262306a36Sopenharmony_ci		goto err_out;
6362306a36Sopenharmony_ci	}
6462306a36Sopenharmony_ci	ntfs_debug("Quota defaults flags = 0x%x.", le32_to_cpu(qce->flags));
6562306a36Sopenharmony_ci	/* If quotas are already marked out of date, no need to do anything. */
6662306a36Sopenharmony_ci	if (qce->flags & QUOTA_FLAG_OUT_OF_DATE)
6762306a36Sopenharmony_ci		goto set_done;
6862306a36Sopenharmony_ci	/*
6962306a36Sopenharmony_ci	 * If quota tracking is neither requested, nor enabled and there are no
7062306a36Sopenharmony_ci	 * pending deletes, no need to mark the quotas out of date.
7162306a36Sopenharmony_ci	 */
7262306a36Sopenharmony_ci	if (!(qce->flags & (QUOTA_FLAG_TRACKING_ENABLED |
7362306a36Sopenharmony_ci			QUOTA_FLAG_TRACKING_REQUESTED |
7462306a36Sopenharmony_ci			QUOTA_FLAG_PENDING_DELETES)))
7562306a36Sopenharmony_ci		goto set_done;
7662306a36Sopenharmony_ci	/*
7762306a36Sopenharmony_ci	 * Set the QUOTA_FLAG_OUT_OF_DATE bit thus marking quotas out of date.
7862306a36Sopenharmony_ci	 * This is verified on WinXP to be sufficient to cause windows to
7962306a36Sopenharmony_ci	 * rescan the volume on boot and update all quota entries.
8062306a36Sopenharmony_ci	 */
8162306a36Sopenharmony_ci	qce->flags |= QUOTA_FLAG_OUT_OF_DATE;
8262306a36Sopenharmony_ci	/* Ensure the modified flags are written to disk. */
8362306a36Sopenharmony_ci	ntfs_index_entry_flush_dcache_page(ictx);
8462306a36Sopenharmony_ci	ntfs_index_entry_mark_dirty(ictx);
8562306a36Sopenharmony_ciset_done:
8662306a36Sopenharmony_ci	ntfs_index_ctx_put(ictx);
8762306a36Sopenharmony_ci	inode_unlock(vol->quota_q_ino);
8862306a36Sopenharmony_ci	/*
8962306a36Sopenharmony_ci	 * We set the flag so we do not try to mark the quotas out of date
9062306a36Sopenharmony_ci	 * again on remount.
9162306a36Sopenharmony_ci	 */
9262306a36Sopenharmony_ci	NVolSetQuotaOutOfDate(vol);
9362306a36Sopenharmony_cidone:
9462306a36Sopenharmony_ci	ntfs_debug("Done.");
9562306a36Sopenharmony_ci	return true;
9662306a36Sopenharmony_cierr_out:
9762306a36Sopenharmony_ci	if (ictx)
9862306a36Sopenharmony_ci		ntfs_index_ctx_put(ictx);
9962306a36Sopenharmony_ci	inode_unlock(vol->quota_q_ino);
10062306a36Sopenharmony_ci	return false;
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci#endif /* NTFS_RW */
104