15e5c12b0Sopenharmony_ci/** quotaio.c
25e5c12b0Sopenharmony_ci *
35e5c12b0Sopenharmony_ci * Generic IO operations on quotafiles
45e5c12b0Sopenharmony_ci * Jan Kara <jack@suse.cz> - sponsored by SuSE CR
55e5c12b0Sopenharmony_ci * Aditya Kali <adityakali@google.com> - Ported to e2fsprogs
65e5c12b0Sopenharmony_ci * Hyojun Kim <hyojun@google.com> - Ported to f2fs-tools
75e5c12b0Sopenharmony_ci */
85e5c12b0Sopenharmony_ci
95e5c12b0Sopenharmony_ci#include <stdio.h>
105e5c12b0Sopenharmony_ci#include <errno.h>
115e5c12b0Sopenharmony_ci#include <string.h>
125e5c12b0Sopenharmony_ci#include <unistd.h>
135e5c12b0Sopenharmony_ci#include <stdlib.h>
145e5c12b0Sopenharmony_ci#include <time.h>
155e5c12b0Sopenharmony_ci#include <sys/types.h>
165e5c12b0Sopenharmony_ci#include <sys/stat.h>
175e5c12b0Sopenharmony_ci#include <sys/file.h>
185e5c12b0Sopenharmony_ci#include <assert.h>
195e5c12b0Sopenharmony_ci
205e5c12b0Sopenharmony_ci#include "common.h"
215e5c12b0Sopenharmony_ci#include "quotaio.h"
225e5c12b0Sopenharmony_ci
235e5c12b0Sopenharmony_cistatic const char * const extensions[MAXQUOTAS] = {
245e5c12b0Sopenharmony_ci	[USRQUOTA] = "user",
255e5c12b0Sopenharmony_ci	[GRPQUOTA] = "group",
265e5c12b0Sopenharmony_ci	[PRJQUOTA] = "project",
275e5c12b0Sopenharmony_ci};
285e5c12b0Sopenharmony_ci
295e5c12b0Sopenharmony_ci/* Header in all newer quotafiles */
305e5c12b0Sopenharmony_cistruct disk_dqheader {
315e5c12b0Sopenharmony_ci	__le32 dqh_magic;
325e5c12b0Sopenharmony_ci	__le32 dqh_version;
335e5c12b0Sopenharmony_ci};
345e5c12b0Sopenharmony_ci
355e5c12b0Sopenharmony_cistatic_assert(sizeof(struct disk_dqheader) == 8, "");
365e5c12b0Sopenharmony_ci
375e5c12b0Sopenharmony_ciint cur_qtype = -1;
385e5c12b0Sopenharmony_ciu32 qf_last_blkofs[MAXQUOTAS] = {0, 0, 0};
395e5c12b0Sopenharmony_cienum qf_szchk_type_t qf_szchk_type[MAXQUOTAS] =
405e5c12b0Sopenharmony_ci{
415e5c12b0Sopenharmony_ci	QF_SZCHK_NONE, QF_SZCHK_NONE, QF_SZCHK_NONE
425e5c12b0Sopenharmony_ci};
435e5c12b0Sopenharmony_ciu64 qf_maxsize[MAXQUOTAS];
445e5c12b0Sopenharmony_ci
455e5c12b0Sopenharmony_ci/**
465e5c12b0Sopenharmony_ci * Convert type of quota to written representation
475e5c12b0Sopenharmony_ci */
485e5c12b0Sopenharmony_ciconst char *quota_type2name(enum quota_type qtype)
495e5c12b0Sopenharmony_ci{
505e5c12b0Sopenharmony_ci	if (qtype >= MAXQUOTAS)
515e5c12b0Sopenharmony_ci		return "unknown";
525e5c12b0Sopenharmony_ci	return extensions[qtype];
535e5c12b0Sopenharmony_ci}
545e5c12b0Sopenharmony_ci
555e5c12b0Sopenharmony_ci/*
565e5c12b0Sopenharmony_ci * Set grace time if needed
575e5c12b0Sopenharmony_ci */
585e5c12b0Sopenharmony_civoid update_grace_times(struct dquot *q)
595e5c12b0Sopenharmony_ci{
605e5c12b0Sopenharmony_ci	time_t now;
615e5c12b0Sopenharmony_ci
625e5c12b0Sopenharmony_ci	time(&now);
635e5c12b0Sopenharmony_ci	if (q->dq_dqb.dqb_bsoftlimit && toqb(q->dq_dqb.dqb_curspace) >
645e5c12b0Sopenharmony_ci			q->dq_dqb.dqb_bsoftlimit) {
655e5c12b0Sopenharmony_ci		if (!q->dq_dqb.dqb_btime)
665e5c12b0Sopenharmony_ci			q->dq_dqb.dqb_btime =
675e5c12b0Sopenharmony_ci				now + q->dq_h->qh_info.dqi_bgrace;
685e5c12b0Sopenharmony_ci	} else {
695e5c12b0Sopenharmony_ci		q->dq_dqb.dqb_btime = 0;
705e5c12b0Sopenharmony_ci	}
715e5c12b0Sopenharmony_ci
725e5c12b0Sopenharmony_ci	if (q->dq_dqb.dqb_isoftlimit && q->dq_dqb.dqb_curinodes >
735e5c12b0Sopenharmony_ci			q->dq_dqb.dqb_isoftlimit) {
745e5c12b0Sopenharmony_ci		if (!q->dq_dqb.dqb_itime)
755e5c12b0Sopenharmony_ci				q->dq_dqb.dqb_itime =
765e5c12b0Sopenharmony_ci					now + q->dq_h->qh_info.dqi_igrace;
775e5c12b0Sopenharmony_ci	} else {
785e5c12b0Sopenharmony_ci		q->dq_dqb.dqb_itime = 0;
795e5c12b0Sopenharmony_ci	}
805e5c12b0Sopenharmony_ci}
815e5c12b0Sopenharmony_ci
825e5c12b0Sopenharmony_ci/* Functions to read/write quota file. */
835e5c12b0Sopenharmony_cistatic unsigned int quota_write_nomount(struct quota_file *qf,
845e5c12b0Sopenharmony_ci					long offset,
855e5c12b0Sopenharmony_ci					void *buf, unsigned int size)
865e5c12b0Sopenharmony_ci{
875e5c12b0Sopenharmony_ci	unsigned int written;
885e5c12b0Sopenharmony_ci
895e5c12b0Sopenharmony_ci	written = f2fs_write(qf->sbi, qf->ino, buf, size, offset);
905e5c12b0Sopenharmony_ci	if (qf->filesize < offset + written)
915e5c12b0Sopenharmony_ci		qf->filesize = offset + written;
925e5c12b0Sopenharmony_ci	if (written != size)
935e5c12b0Sopenharmony_ci		return -EIO;
945e5c12b0Sopenharmony_ci	return written;
955e5c12b0Sopenharmony_ci}
965e5c12b0Sopenharmony_ci
975e5c12b0Sopenharmony_cistatic unsigned int quota_read_nomount(struct quota_file *qf, long offset,
985e5c12b0Sopenharmony_ci		void *buf, unsigned int size)
995e5c12b0Sopenharmony_ci{
1005e5c12b0Sopenharmony_ci	return f2fs_read(qf->sbi, qf->ino, buf, size, offset);
1015e5c12b0Sopenharmony_ci}
1025e5c12b0Sopenharmony_ci
1035e5c12b0Sopenharmony_ci/*
1045e5c12b0Sopenharmony_ci * Detect quota format and initialize quota IO
1055e5c12b0Sopenharmony_ci */
1065e5c12b0Sopenharmony_cierrcode_t quota_file_open(struct f2fs_sb_info *sbi, struct quota_handle *h,
1075e5c12b0Sopenharmony_ci			  enum quota_type qtype, int flags)
1085e5c12b0Sopenharmony_ci{
1095e5c12b0Sopenharmony_ci	struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
1105e5c12b0Sopenharmony_ci	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
1115e5c12b0Sopenharmony_ci	quota_ctx_t qctx = fsck->qctx;
1125e5c12b0Sopenharmony_ci	f2fs_ino_t qf_ino;
1135e5c12b0Sopenharmony_ci	errcode_t err = 0;
1145e5c12b0Sopenharmony_ci	int allocated_handle = 0;
1155e5c12b0Sopenharmony_ci
1165e5c12b0Sopenharmony_ci	if (qtype >= MAXQUOTAS)
1175e5c12b0Sopenharmony_ci		return EINVAL;
1185e5c12b0Sopenharmony_ci
1195e5c12b0Sopenharmony_ci	qf_ino = sb->qf_ino[qtype];
1205e5c12b0Sopenharmony_ci
1215e5c12b0Sopenharmony_ci	if (!h) {
1225e5c12b0Sopenharmony_ci		if (qctx->quota_file[qtype]) {
1235e5c12b0Sopenharmony_ci			h = qctx->quota_file[qtype];
1245e5c12b0Sopenharmony_ci			(void) quota_file_close(sbi, h, 0);
1255e5c12b0Sopenharmony_ci		}
1265e5c12b0Sopenharmony_ci		err = quota_get_mem(sizeof(struct quota_handle), &h);
1275e5c12b0Sopenharmony_ci		if (err) {
1285e5c12b0Sopenharmony_ci			log_err("Unable to allocate quota handle");
1295e5c12b0Sopenharmony_ci			return err;
1305e5c12b0Sopenharmony_ci		}
1315e5c12b0Sopenharmony_ci		allocated_handle = 1;
1325e5c12b0Sopenharmony_ci	}
1335e5c12b0Sopenharmony_ci
1345e5c12b0Sopenharmony_ci	h->qh_qf.sbi = sbi;
1355e5c12b0Sopenharmony_ci	h->qh_qf.ino = qf_ino;
1365e5c12b0Sopenharmony_ci	h->write = quota_write_nomount;
1375e5c12b0Sopenharmony_ci	h->read = quota_read_nomount;
1385e5c12b0Sopenharmony_ci	h->qh_file_flags = flags;
1395e5c12b0Sopenharmony_ci	h->qh_io_flags = 0;
1405e5c12b0Sopenharmony_ci	h->qh_type = qtype;
1415e5c12b0Sopenharmony_ci	h->qh_fmt = QFMT_VFS_V1;
1425e5c12b0Sopenharmony_ci	memset(&h->qh_info, 0, sizeof(h->qh_info));
1435e5c12b0Sopenharmony_ci	h->qh_ops = &quotafile_ops_2;
1445e5c12b0Sopenharmony_ci
1455e5c12b0Sopenharmony_ci	if (h->qh_ops->check_file &&
1465e5c12b0Sopenharmony_ci	    (h->qh_ops->check_file(h, qtype) == 0)) {
1475e5c12b0Sopenharmony_ci		log_err("qh_ops->check_file failed");
1485e5c12b0Sopenharmony_ci		err = EIO;
1495e5c12b0Sopenharmony_ci		goto errout;
1505e5c12b0Sopenharmony_ci	}
1515e5c12b0Sopenharmony_ci
1525e5c12b0Sopenharmony_ci	if (h->qh_ops->init_io && (h->qh_ops->init_io(h, qtype) < 0)) {
1535e5c12b0Sopenharmony_ci		log_err("qh_ops->init_io failed");
1545e5c12b0Sopenharmony_ci		err = EIO;
1555e5c12b0Sopenharmony_ci		goto errout;
1565e5c12b0Sopenharmony_ci	}
1575e5c12b0Sopenharmony_ci	if (allocated_handle)
1585e5c12b0Sopenharmony_ci		qctx->quota_file[qtype] = h;
1595e5c12b0Sopenharmony_cierrout:
1605e5c12b0Sopenharmony_ci	return err;
1615e5c12b0Sopenharmony_ci}
1625e5c12b0Sopenharmony_ci
1635e5c12b0Sopenharmony_ci/*
1645e5c12b0Sopenharmony_ci * Create new quotafile of specified format on given filesystem
1655e5c12b0Sopenharmony_ci */
1665e5c12b0Sopenharmony_cierrcode_t quota_file_create(struct f2fs_sb_info *sbi, struct quota_handle *h,
1675e5c12b0Sopenharmony_ci		enum quota_type qtype)
1685e5c12b0Sopenharmony_ci{
1695e5c12b0Sopenharmony_ci	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
1705e5c12b0Sopenharmony_ci	f2fs_ino_t qf_inum = sb->qf_ino[qtype];
1715e5c12b0Sopenharmony_ci	errcode_t err = 0;
1725e5c12b0Sopenharmony_ci
1735e5c12b0Sopenharmony_ci	memset(&h->qh_qf, 0, sizeof(h->qh_qf));
1745e5c12b0Sopenharmony_ci	h->qh_qf.sbi = sbi;
1755e5c12b0Sopenharmony_ci	h->qh_qf.ino = qf_inum;
1765e5c12b0Sopenharmony_ci	h->write = quota_write_nomount;
1775e5c12b0Sopenharmony_ci	h->read = quota_read_nomount;
1785e5c12b0Sopenharmony_ci
1795e5c12b0Sopenharmony_ci	log_debug("Creating quota ino=%u, type=%d", qf_inum, qtype);
1805e5c12b0Sopenharmony_ci	h->qh_io_flags = 0;
1815e5c12b0Sopenharmony_ci	h->qh_type = qtype;
1825e5c12b0Sopenharmony_ci	h->qh_fmt = QFMT_VFS_V1;
1835e5c12b0Sopenharmony_ci	memset(&h->qh_info, 0, sizeof(h->qh_info));
1845e5c12b0Sopenharmony_ci	h->qh_ops = &quotafile_ops_2;
1855e5c12b0Sopenharmony_ci
1865e5c12b0Sopenharmony_ci	if (h->qh_ops->new_io && (h->qh_ops->new_io(h) < 0)) {
1875e5c12b0Sopenharmony_ci		log_err("qh_ops->new_io failed");
1885e5c12b0Sopenharmony_ci		err = EIO;
1895e5c12b0Sopenharmony_ci	}
1905e5c12b0Sopenharmony_ci
1915e5c12b0Sopenharmony_ci	return err;
1925e5c12b0Sopenharmony_ci}
1935e5c12b0Sopenharmony_ci
1945e5c12b0Sopenharmony_ci/*
1955e5c12b0Sopenharmony_ci * Close quotafile and release handle
1965e5c12b0Sopenharmony_ci */
1975e5c12b0Sopenharmony_cierrcode_t quota_file_close(struct f2fs_sb_info *sbi, struct quota_handle *h,
1985e5c12b0Sopenharmony_ci		int update_filesize)
1995e5c12b0Sopenharmony_ci{
2005e5c12b0Sopenharmony_ci	struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
2015e5c12b0Sopenharmony_ci	quota_ctx_t qctx = fsck->qctx;
2025e5c12b0Sopenharmony_ci
2035e5c12b0Sopenharmony_ci        if (h->qh_io_flags & IOFL_INFODIRTY) {
2045e5c12b0Sopenharmony_ci		if (h->qh_ops->write_info && h->qh_ops->write_info(h) < 0)
2055e5c12b0Sopenharmony_ci			return EIO;
2065e5c12b0Sopenharmony_ci		h->qh_io_flags &= ~IOFL_INFODIRTY;
2075e5c12b0Sopenharmony_ci	}
2085e5c12b0Sopenharmony_ci	if (h->qh_ops->end_io && h->qh_ops->end_io(h) < 0)
2095e5c12b0Sopenharmony_ci		return EIO;
2105e5c12b0Sopenharmony_ci	if (update_filesize) {
2115e5c12b0Sopenharmony_ci		f2fs_filesize_update(sbi, h->qh_qf.ino, h->qh_qf.filesize);
2125e5c12b0Sopenharmony_ci	}
2135e5c12b0Sopenharmony_ci	if (qctx->quota_file[h->qh_type] == h)
2145e5c12b0Sopenharmony_ci		quota_free_mem(&qctx->quota_file[h->qh_type]);
2155e5c12b0Sopenharmony_ci	return 0;
2165e5c12b0Sopenharmony_ci}
2175e5c12b0Sopenharmony_ci
2185e5c12b0Sopenharmony_ci/*
2195e5c12b0Sopenharmony_ci * Create empty quota structure
2205e5c12b0Sopenharmony_ci */
2215e5c12b0Sopenharmony_cistruct dquot *get_empty_dquot(void)
2225e5c12b0Sopenharmony_ci{
2235e5c12b0Sopenharmony_ci	struct dquot *dquot;
2245e5c12b0Sopenharmony_ci
2255e5c12b0Sopenharmony_ci	if (quota_get_memzero(sizeof(struct dquot), &dquot)) {
2265e5c12b0Sopenharmony_ci		log_err("Failed to allocate dquot");
2275e5c12b0Sopenharmony_ci		return NULL;
2285e5c12b0Sopenharmony_ci	}
2295e5c12b0Sopenharmony_ci
2305e5c12b0Sopenharmony_ci	dquot->dq_id = -1;
2315e5c12b0Sopenharmony_ci	return dquot;
2325e5c12b0Sopenharmony_ci}
233