📄 dquot.c
字号:
/* * Implementation of the diskquota system for the LINUX operating * system. QUOTA is implemented using the BSD systemcall interface as * the means of communication with the user level. Currently only the * ext2-filesystem has support for diskquotas. Other filesystems may * be added in future time. This file contains the generic routines * called by the different filesystems on allocation of an inode or * block. These routines take care of the administration needed to * have a consistent diskquota tracking system. The ideas of both * user and group quotas are based on the Melbourne quota system as * used on BSD derived systems. The internal implementation is * based on the LINUX inode-subsystem with added complexity of the * diskquota system. This implementation is not based on any BSD * kernel sourcecode. * * Version: $Id: dquot.c,v 1.2 1999/07/16 23:52:00 bjornw Exp $ * * Author: Marco van Wieringen <mvw@mcs.ow.nl> <mvw@tnix.net> * * Fixes: Dmitry Gorodchanin <pgmdsg@ibi.com>, 11 Feb 96 * removed race conditions in dqput(), dqget() and iput(). * Nick Kralevich <nickkral@cal.alumni.berkeley.edu>, 21 Jul 97 * Fixed a condition where user and group quotas could get mixed up. * * (C) Copyright 1994, 1995 Marco van Wieringen * */#include <linux/errno.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/types.h>#include <linux/string.h>#include <linux/fcntl.h>#include <linux/stat.h>#include <linux/tty.h>#include <linux/malloc.h>#include <linux/mount.h>#include <asm/segment.h>#define __DQUOT_VERSION__ "dquot_5.6.0"static char quotamessage[MAX_QUOTA_MESSAGE];static char *quotatypes[] = INITQFNAMES;static int nr_dquots = 0, nr_free_dquots = 0;static struct dquot *hash_table[NR_DQHASH];static struct dquot *first_dquot;static struct dqstats dqstats;static struct wait_queue *dquot_wait = (struct wait_queue *)NULL;extern void add_dquot_ref(kdev_t dev, short type);extern void reset_dquot_ptrs(kdev_t dev, short type);#ifndef min#define min(a,b) ((a) < (b)) ? (a) : (b)#endif/* * Functions for management of the hashlist. */static inline int const hashfn(kdev_t dev, unsigned int id, short type){ return((HASHDEV(dev) ^ id) * (MAXQUOTAS - type)) % NR_DQHASH;}static inline struct dquot **const hash(kdev_t dev, unsigned int id, short type){ return(hash_table + hashfn(dev, id, type));}static inline int has_quota_enabled(kdev_t dev, short type){ struct vfsmount *vfsmnt; return((vfsmnt = lookup_vfsmnt(dev)) != (struct vfsmount *)NULL && (vfsmnt->mnt_quotas[type] != (struct file *)NULL));}static void insert_dquot_free(struct dquot *dquot){ dquot->dq_next = first_dquot; dquot->dq_prev = first_dquot->dq_prev; dquot->dq_next->dq_prev = dquot; dquot->dq_prev->dq_next = dquot; first_dquot = dquot;}static void remove_dquot_free(struct dquot *dquot){ if (first_dquot == dquot) first_dquot = first_dquot->dq_next; if (dquot->dq_next) dquot->dq_next->dq_prev = dquot->dq_prev; if (dquot->dq_prev) dquot->dq_prev->dq_next = dquot->dq_next; dquot->dq_next = dquot->dq_prev = NODQUOT;}static void insert_dquot_hash(struct dquot *dquot){ struct dquot **hash_ent; hash_ent = hash(dquot->dq_dev, dquot->dq_id, dquot->dq_type); dquot->dq_hash_next = *hash_ent; dquot->dq_hash_prev = NODQUOT; if (dquot->dq_hash_next) dquot->dq_hash_next->dq_hash_prev = dquot; *hash_ent = dquot;}static void remove_dquot_hash(struct dquot *dquot){ struct dquot **hash_ent; hash_ent = hash(dquot->dq_dev, dquot->dq_id, dquot->dq_type); if (*hash_ent == dquot) *hash_ent = dquot->dq_hash_next; if (dquot->dq_hash_next) dquot->dq_hash_next->dq_hash_prev = dquot->dq_hash_prev; if (dquot->dq_hash_prev) dquot->dq_hash_prev->dq_hash_next = dquot->dq_hash_next; dquot->dq_hash_prev = dquot->dq_hash_next = NODQUOT;}static void put_last_free(struct dquot *dquot){ remove_dquot_free(dquot); dquot->dq_prev = first_dquot->dq_prev; dquot->dq_prev->dq_next = dquot; dquot->dq_next = first_dquot; dquot->dq_next->dq_prev = dquot;}static void grow_dquots(void){ struct dquot *dquot; int cnt; if (!(dquot = (struct dquot*) get_free_page(GFP_KERNEL))) return; dqstats.pages_allocated++; cnt = PAGE_SIZE / sizeof(struct dquot); nr_dquots += cnt; nr_free_dquots += cnt; if (!first_dquot) { dquot->dq_next = dquot->dq_prev = first_dquot = dquot++; cnt--; } for (; cnt; cnt--) insert_dquot_free(dquot++);}/* * Functions for locking and waiting on dquots. */static void __wait_on_dquot(struct dquot *dquot){ struct wait_queue wait = {current, NULL}; add_wait_queue(&dquot->dq_wait, &wait);repeat: current->state = TASK_UNINTERRUPTIBLE; if (dquot->dq_flags & DQ_LOCKED) { dquot->dq_flags |= DQ_WANT; schedule(); goto repeat; } remove_wait_queue(&dquot->dq_wait, &wait); current->state = TASK_RUNNING;}static inline void wait_on_dquot(struct dquot *dquot){ if (dquot->dq_flags & DQ_LOCKED) __wait_on_dquot(dquot);}static inline void lock_dquot(struct dquot *dquot){ wait_on_dquot(dquot); dquot->dq_flags |= DQ_LOCKED;}static inline void unlock_dquot(struct dquot *dquot){ dquot->dq_flags &= ~DQ_LOCKED; if (dquot->dq_flags & DQ_WANT) { dquot->dq_flags &= ~DQ_WANT; wake_up(&dquot->dq_wait); }}/* * Note that we don't want to disturb any wait-queues when we discard * an dquot. * * FIXME: As soon as we have a nice solution for the inode problem we * can also fix this one. I.e. the volatile part. */static void clear_dquot(struct dquot * dquot){ struct wait_queue *wait; wait_on_dquot(dquot); remove_dquot_hash(dquot); remove_dquot_free(dquot); wait = ((volatile struct dquot *) dquot)->dq_wait; if (dquot->dq_count) nr_free_dquots++; memset(dquot, 0, sizeof(*dquot)); ((volatile struct dquot *) dquot)->dq_wait = wait; insert_dquot_free(dquot);}static void write_dquot(struct dquot *dquot){ short type = dquot->dq_type; struct file *filp = dquot->dq_mnt->mnt_quotas[type]; unsigned short fs; if (!(dquot->dq_flags & DQ_MOD) || (filp == (struct file *)NULL)) return; lock_dquot(dquot); down(&dquot->dq_mnt->mnt_sem); if (filp->f_op->lseek) { if (filp->f_op->lseek(filp->f_inode, filp, dqoff(dquot->dq_id), 0) != dqoff(dquot->dq_id)) { up(&dquot->dq_mnt->mnt_sem); unlock_dquot(dquot); return; } } else { int p = dqoff(dquot->dq_id); if(p>=0) filp->f_pos = p; } fs = get_fs(); set_fs(KERNEL_DS); if (filp->f_op->write(filp->f_inode, filp, (char *)&dquot->dq_dqb, sizeof(struct dqblk)) == sizeof(struct dqblk)) dquot->dq_flags &= ~DQ_MOD; up(&dquot->dq_mnt->mnt_sem); set_fs(fs); unlock_dquot(dquot); dqstats.writes++;}static void read_dquot(struct dquot *dquot){ short type = dquot->dq_type; struct file *filp = dquot->dq_mnt->mnt_quotas[type]; unsigned short fs; if (filp == (struct file *)NULL) return; lock_dquot(dquot); down(&dquot->dq_mnt->mnt_sem); if (filp->f_op->lseek) { if (filp->f_op->lseek(filp->f_inode, filp, dqoff(dquot->dq_id), 0) != dqoff(dquot->dq_id)) { up(&dquot->dq_mnt->mnt_sem); unlock_dquot(dquot); return; } } else filp->f_pos = dqoff(dquot->dq_id); fs = get_fs(); set_fs(KERNEL_DS); filp->f_op->read(filp->f_inode, filp, (char *)&dquot->dq_dqb, sizeof(struct dqblk)); up(&dquot->dq_mnt->mnt_sem); set_fs(fs); if (dquot->dq_bhardlimit == 0 && dquot->dq_bsoftlimit == 0 && dquot->dq_ihardlimit == 0 && dquot->dq_isoftlimit == 0) dquot->dq_flags |= DQ_FAKE; unlock_dquot(dquot); dqstats.reads++;}int sync_dquots(kdev_t dev, short type){ struct dquot *dquot = first_dquot; int i; dqstats.syncs++; for (i = 0; i < nr_dquots * 2; i++, dquot = dquot->dq_next) { if (dev == NODEV || dquot->dq_count == 0 || dquot->dq_dev != dev) continue; if (type != -1 && dquot->dq_type != type) continue; wait_on_dquot(dquot); if (dquot->dq_flags & DQ_MOD) write_dquot(dquot); } return(0);}/* * Trash the cache for a certain type on a device. */void invalidate_dquots(kdev_t dev, short type){ struct dquot *dquot, *next; int cnt; next = first_dquot; for (cnt = nr_dquots ; cnt > 0 ; cnt--) { dquot = next; next = dquot->dq_next; if (dquot->dq_dev != dev || dquot->dq_type != type) continue; if (dquot->dq_flags & DQ_LOCKED) { printk("VFS: dquot busy on removed device %s\n", kdevname(dev)); continue; } if (dquot->dq_flags & DQ_MOD) write_dquot(dquot); dqstats.drops++; clear_dquot(dquot); }}static inline void dquot_incr_inodes(struct dquot *dquot, unsigned long number){ lock_dquot(dquot); dquot->dq_curinodes += number; dquot->dq_flags |= DQ_MOD; unlock_dquot(dquot);}static inline void dquot_incr_blocks(struct dquot *dquot, unsigned long number){ lock_dquot(dquot); dquot->dq_curblocks += number; dquot->dq_flags |= DQ_MOD; unlock_dquot(dquot);}static inline void dquot_decr_inodes(struct dquot *dquot, unsigned long number){ lock_dquot(dquot); if (dquot->dq_curinodes > number) dquot->dq_curinodes -= number; else dquot->dq_curinodes = 0; if (dquot->dq_curinodes < dquot->dq_isoftlimit) dquot->dq_itime = (time_t) 0; dquot->dq_flags &= ~DQ_INODES; dquot->dq_flags |= DQ_MOD; unlock_dquot(dquot);}static inline void dquot_decr_blocks(struct dquot *dquot, unsigned long number){ lock_dquot(dquot); if (dquot->dq_curblocks > number) dquot->dq_curblocks -= number; else dquot->dq_curblocks = 0; if (dquot->dq_curblocks < dquot->dq_bsoftlimit) dquot->dq_btime = (time_t) 0; dquot->dq_flags &= ~DQ_BLKS; dquot->dq_flags |= DQ_MOD; unlock_dquot(dquot);}static inline int need_print_warning(short type, struct dquot *dquot){ switch (type) { case USRQUOTA: return(current->fsuid == dquot->dq_id); case GRPQUOTA: return(current->fsgid == dquot->dq_id); } return(0);}static int check_idq(struct dquot *dquot, short type, u_long short inodes){ if (inodes <= 0 || dquot->dq_flags & DQ_FAKE) return(QUOTA_OK); if (dquot->dq_ihardlimit && (dquot->dq_curinodes + inodes) > dquot->dq_ihardlimit && !fsuser()) { if ((dquot->dq_flags & DQ_INODES) == 0 && need_print_warning(type, dquot)) { sprintf(quotamessage, "%s: write failed, %s file limit reached\r\n", dquot->dq_mnt->mnt_dirname, quotatypes[type]); tty_write_message(current->tty, quotamessage); dquot->dq_flags |= DQ_INODES; } return(NO_QUOTA); } if (dquot->dq_isoftlimit && (dquot->dq_curinodes + inodes) > dquot->dq_isoftlimit && dquot->dq_itime && CURRENT_TIME >= dquot->dq_itime && !fsuser()) { if (need_print_warning(type, dquot)) { sprintf(quotamessage, "%s: warning, %s file quota exceeded too long.\r\n", dquot->dq_mnt->mnt_dirname, quotatypes[type]); tty_write_message(current->tty, quotamessage); } return(NO_QUOTA); } if (dquot->dq_isoftlimit && (dquot->dq_curinodes + inodes) > dquot->dq_isoftlimit && dquot->dq_itime == 0 && !fsuser()) { if (need_print_warning(type, dquot)) { sprintf(quotamessage, "%s: warning, %s file quota exceeded\r\n", dquot->dq_mnt->mnt_dirname, quotatypes[type]); tty_write_message(current->tty, quotamessage); } dquot->dq_itime = CURRENT_TIME + dquot->dq_mnt->mnt_iexp[type]; } return(QUOTA_OK);}static int check_bdq(struct dquot *dquot, short type, u_long blocks){ if (blocks <= 0 || dquot->dq_flags & DQ_FAKE) return(QUOTA_OK); if (dquot->dq_bhardlimit && (dquot->dq_curblocks + blocks) > dquot->dq_bhardlimit && !fsuser()) { if ((dquot->dq_flags & DQ_BLKS) == 0 && need_print_warning(type, dquot)) { sprintf(quotamessage, "%s: write failed, %s disk limit reached.\r\n", dquot->dq_mnt->mnt_dirname, quotatypes[type]); tty_write_message(current->tty, quotamessage); dquot->dq_flags |= DQ_BLKS; } return(NO_QUOTA); } if (dquot->dq_bsoftlimit && (dquot->dq_curblocks + blocks) > dquot->dq_bsoftlimit && dquot->dq_btime && CURRENT_TIME >= dquot->dq_btime && !fsuser()) { if (need_print_warning(type, dquot)) { sprintf(quotamessage, "%s: write failed, %s disk quota exceeded too long.\r\n", dquot->dq_mnt->mnt_dirname, quotatypes[type]); tty_write_message(current->tty, quotamessage); } return(NO_QUOTA); } if (dquot->dq_bsoftlimit && (dquot->dq_curblocks + blocks) > dquot->dq_bsoftlimit && dquot->dq_btime == 0 && !fsuser()) { if (need_print_warning(type, dquot)) { sprintf(quotamessage, "%s: warning, %s disk quota exceeded\r\n", dquot->dq_mnt->mnt_dirname, quotatypes[type]); tty_write_message(current->tty, quotamessage); } dquot->dq_btime = CURRENT_TIME + dquot->dq_mnt->mnt_bexp[type]; } return(QUOTA_OK);}static void dqput(struct dquot *dquot){ if (!dquot) return; /* * If the dq_mnt pointer isn't initialized this entry needs no * checking and doesn't need to be written. It just an empty * dquot that is put back into the freelist. */ if (dquot->dq_mnt != (struct vfsmount *)NULL) { dqstats.drops++; wait_on_dquot(dquot); if (!dquot->dq_count) { printk("VFS: dqput: trying to free free dquot\n"); printk("VFS: device %s, dquot of %s %d\n", kdevname(dquot->dq_dev), quotatypes[dquot->dq_type], dquot->dq_id); return; }repeat: if (dquot->dq_count > 1) { dquot->dq_count--; return; } wake_up(&dquot_wait); if (dquot->dq_flags & DQ_MOD) { write_dquot(dquot); /* we can sleep - so do again */ wait_on_dquot(dquot); goto repeat; } } if (dquot->dq_count) { dquot->dq_count--; nr_free_dquots++; } return;}static struct dquot *get_empty_dquot(void){ struct dquot *dquot, *best; int cnt; if (nr_dquots < NR_DQUOTS && nr_free_dquots < (nr_dquots >> 2)) grow_dquots();repeat: dquot = first_dquot; best = NODQUOT; for (cnt = 0; cnt < nr_dquots; dquot = dquot->dq_next, cnt++) { if (!dquot->dq_count) { if (!best) best = dquot; if (!(dquot->dq_flags & DQ_MOD) && !(dquot->dq_flags & DQ_LOCKED)) { best = dquot; break; } } } if (!best || best->dq_flags & DQ_MOD || best->dq_flags & DQ_LOCKED) if (nr_dquots < NR_DQUOTS) { grow_dquots(); goto repeat; } dquot = best; if (!dquot) { printk("VFS: No free dquots - contact mvw@mcs.ow.org\n"); sleep_on(&dquot_wait); goto repeat; } if (dquot->dq_flags & DQ_LOCKED) { wait_on_dquot(dquot); goto repeat; } if (dquot->dq_flags & DQ_MOD) { write_dquot(dquot); goto repeat; } if (dquot->dq_count) goto repeat; clear_dquot(dquot); dquot->dq_count = 1; nr_free_dquots--; if (nr_free_dquots < 0) { printk ("VFS: get_empty_dquot: bad free dquot count.\n"); nr_free_dquots = 0; } return(dquot);}static struct dquot *dqget(kdev_t dev, unsigned int id, short type)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -