1 |
1275 |
phoenix |
/*
|
2 |
|
|
* Implementation of the diskquota system for the LINUX operating
|
3 |
|
|
* system. QUOTA is implemented using the BSD system call interface as
|
4 |
|
|
* the means of communication with the user level. Currently only the
|
5 |
|
|
* ext2 filesystem has support for disk quotas. Other filesystems may
|
6 |
|
|
* be added in the future. This file contains the generic routines
|
7 |
|
|
* called by the different filesystems on allocation of an inode or
|
8 |
|
|
* block. These routines take care of the administration needed to
|
9 |
|
|
* have a consistent diskquota tracking system. The ideas of both
|
10 |
|
|
* user and group quotas are based on the Melbourne quota system as
|
11 |
|
|
* used on BSD derived systems. The internal implementation is
|
12 |
|
|
* based on one of the several variants of the LINUX inode-subsystem
|
13 |
|
|
* with added complexity of the diskquota system.
|
14 |
|
|
*
|
15 |
|
|
* Version: $Id: dquot.c,v 1.1.1.1 2004-04-15 01:03:03 phoenix Exp $
|
16 |
|
|
*
|
17 |
|
|
* Author: Marco van Wieringen <mvw@planets.elm.net>
|
18 |
|
|
*
|
19 |
|
|
* Fixes: Dmitry Gorodchanin <pgmdsg@ibi.com>, 11 Feb 96
|
20 |
|
|
*
|
21 |
|
|
* Revised list management to avoid races
|
22 |
|
|
* -- Bill Hawes, <whawes@star.net>, 9/98
|
23 |
|
|
*
|
24 |
|
|
* Fixed races in dquot_transfer(), dqget() and dquot_alloc_...().
|
25 |
|
|
* As the consequence the locking was moved from dquot_decr_...(),
|
26 |
|
|
* dquot_incr_...() to calling functions.
|
27 |
|
|
* invalidate_dquots() now writes modified dquots.
|
28 |
|
|
* Serialized quota_off() and quota_on() for mount point.
|
29 |
|
|
* Fixed a few bugs in grow_dquots().
|
30 |
|
|
* Fixed deadlock in write_dquot() - we no longer account quotas on
|
31 |
|
|
* quota files
|
32 |
|
|
* remove_dquot_ref() moved to inode.c - it now traverses through inodes
|
33 |
|
|
* add_dquot_ref() restarts after blocking
|
34 |
|
|
* Added check for bogus uid and fixed check for group in quotactl.
|
35 |
|
|
* Jan Kara, <jack@suse.cz>, sponsored by SuSE CR, 10-11/99
|
36 |
|
|
*
|
37 |
|
|
* Used struct list_head instead of own list struct
|
38 |
|
|
* Invalidation of referenced dquots is no longer possible
|
39 |
|
|
* Improved free_dquots list management
|
40 |
|
|
* Quota and i_blocks are now updated in one place to avoid races
|
41 |
|
|
* Warnings are now delayed so we won't block in critical section
|
42 |
|
|
* Write updated not to require dquot lock
|
43 |
|
|
* Jan Kara, <jack@suse.cz>, 9/2000
|
44 |
|
|
*
|
45 |
|
|
* Added dynamic quota structure allocation
|
46 |
|
|
* Jan Kara <jack@suse.cz> 12/2000
|
47 |
|
|
*
|
48 |
|
|
* Rewritten quota interface. Implemented new quota format and
|
49 |
|
|
* formats registering.
|
50 |
|
|
* Jan Kara, <jack@suse.cz>, 2001,2002
|
51 |
|
|
*
|
52 |
|
|
* (C) Copyright 1994 - 1997 Marco van Wieringen
|
53 |
|
|
*/
|
54 |
|
|
|
55 |
|
|
#include <linux/errno.h>
|
56 |
|
|
#include <linux/kernel.h>
|
57 |
|
|
#include <linux/fs.h>
|
58 |
|
|
#include <linux/sched.h>
|
59 |
|
|
#include <linux/types.h>
|
60 |
|
|
#include <linux/string.h>
|
61 |
|
|
#include <linux/fcntl.h>
|
62 |
|
|
#include <linux/stat.h>
|
63 |
|
|
#include <linux/tty.h>
|
64 |
|
|
#include <linux/file.h>
|
65 |
|
|
#include <linux/slab.h>
|
66 |
|
|
#include <linux/sysctl.h>
|
67 |
|
|
#include <linux/smp_lock.h>
|
68 |
|
|
#include <linux/init.h>
|
69 |
|
|
#include <linux/module.h>
|
70 |
|
|
#include <linux/proc_fs.h>
|
71 |
|
|
|
72 |
|
|
#include <asm/uaccess.h>
|
73 |
|
|
|
74 |
|
|
static char *quotatypes[] = INITQFNAMES;
|
75 |
|
|
static struct quota_format_type *quota_formats; /* List of registered formats */
|
76 |
|
|
|
77 |
|
|
int register_quota_format(struct quota_format_type *fmt)
|
78 |
|
|
{
|
79 |
|
|
lock_kernel();
|
80 |
|
|
fmt->qf_next = quota_formats;
|
81 |
|
|
quota_formats = fmt;
|
82 |
|
|
unlock_kernel();
|
83 |
|
|
return 0;
|
84 |
|
|
}
|
85 |
|
|
|
86 |
|
|
void unregister_quota_format(struct quota_format_type *fmt)
|
87 |
|
|
{
|
88 |
|
|
struct quota_format_type **actqf;
|
89 |
|
|
|
90 |
|
|
lock_kernel();
|
91 |
|
|
for (actqf = "a_formats; *actqf && *actqf != fmt; actqf = &(*actqf)->qf_next);
|
92 |
|
|
if (*actqf)
|
93 |
|
|
*actqf = (*actqf)->qf_next;
|
94 |
|
|
unlock_kernel();
|
95 |
|
|
}
|
96 |
|
|
|
97 |
|
|
static struct quota_format_type *find_quota_format(int id)
|
98 |
|
|
{
|
99 |
|
|
struct quota_format_type *actqf;
|
100 |
|
|
|
101 |
|
|
lock_kernel();
|
102 |
|
|
for (actqf = quota_formats; actqf && actqf->qf_fmt_id != id; actqf = actqf->qf_next);
|
103 |
|
|
if (actqf && !try_inc_mod_count(actqf->qf_owner))
|
104 |
|
|
actqf = NULL;
|
105 |
|
|
unlock_kernel();
|
106 |
|
|
return actqf;
|
107 |
|
|
}
|
108 |
|
|
|
109 |
|
|
static void put_quota_format(struct quota_format_type *fmt)
|
110 |
|
|
{
|
111 |
|
|
if (fmt->qf_owner)
|
112 |
|
|
__MOD_DEC_USE_COUNT(fmt->qf_owner);
|
113 |
|
|
}
|
114 |
|
|
|
115 |
|
|
/*
|
116 |
|
|
* Dquot List Management:
|
117 |
|
|
* The quota code uses three lists for dquot management: the inuse_list,
|
118 |
|
|
* free_dquots, and dquot_hash[] array. A single dquot structure may be
|
119 |
|
|
* on all three lists, depending on its current state.
|
120 |
|
|
*
|
121 |
|
|
* All dquots are placed to the end of inuse_list when first created, and this
|
122 |
|
|
* list is used for the sync and invalidate operations, which must look
|
123 |
|
|
* at every dquot.
|
124 |
|
|
*
|
125 |
|
|
* Unused dquots (dq_count == 0) are added to the free_dquots list when freed,
|
126 |
|
|
* and this list is searched whenever we need an available dquot. Dquots are
|
127 |
|
|
* removed from the list as soon as they are used again, and
|
128 |
|
|
* dqstats.free_dquots gives the number of dquots on the list. When
|
129 |
|
|
* dquot is invalidated it's completely released from memory.
|
130 |
|
|
*
|
131 |
|
|
* Dquots with a specific identity (device, type and id) are placed on
|
132 |
|
|
* one of the dquot_hash[] hash chains. The provides an efficient search
|
133 |
|
|
* mechanism to locate a specific dquot.
|
134 |
|
|
*/
|
135 |
|
|
|
136 |
|
|
/*
|
137 |
|
|
* Note that any operation which operates on dquot data (ie. dq_dqb) mustn't
|
138 |
|
|
* block while it's updating/reading it. Otherwise races would occur.
|
139 |
|
|
*
|
140 |
|
|
* Locked dquots might not be referenced in inodes - operations like
|
141 |
|
|
* add_dquot_space() does dqduplicate() and would complain. Currently
|
142 |
|
|
* dquot it locked only once in its existence - when it's being read
|
143 |
|
|
* to memory on first dqget() and at that time it can't be referenced
|
144 |
|
|
* from inode. Write operations on dquots don't hold dquot lock as they
|
145 |
|
|
* copy data to internal buffers before writing anyway and copying as well
|
146 |
|
|
* as any data update should be atomic. Also nobody can change used
|
147 |
|
|
* entries in dquot structure as this is done only when quota is destroyed
|
148 |
|
|
* and invalidate_dquots() waits for dquot to have dq_count == 0.
|
149 |
|
|
*/
|
150 |
|
|
|
151 |
|
|
static LIST_HEAD(inuse_list);
|
152 |
|
|
static LIST_HEAD(free_dquots);
|
153 |
|
|
static struct list_head dquot_hash[NR_DQHASH];
|
154 |
|
|
|
155 |
|
|
static void dqput(struct dquot *);
|
156 |
|
|
static struct dquot *dqduplicate(struct dquot *);
|
157 |
|
|
|
158 |
|
|
static inline void get_dquot_ref(struct dquot *dquot)
|
159 |
|
|
{
|
160 |
|
|
dquot->dq_count++;
|
161 |
|
|
}
|
162 |
|
|
|
163 |
|
|
static inline void put_dquot_ref(struct dquot *dquot)
|
164 |
|
|
{
|
165 |
|
|
dquot->dq_count--;
|
166 |
|
|
}
|
167 |
|
|
|
168 |
|
|
static inline void get_dquot_dup_ref(struct dquot *dquot)
|
169 |
|
|
{
|
170 |
|
|
dquot->dq_dup_ref++;
|
171 |
|
|
}
|
172 |
|
|
|
173 |
|
|
static inline void put_dquot_dup_ref(struct dquot *dquot)
|
174 |
|
|
{
|
175 |
|
|
dquot->dq_dup_ref--;
|
176 |
|
|
}
|
177 |
|
|
|
178 |
|
|
static inline int const hashfn(struct super_block *sb, unsigned int id, int type)
|
179 |
|
|
{
|
180 |
|
|
return((HASHDEV(sb->s_dev) ^ id) * (MAXQUOTAS - type)) % NR_DQHASH;
|
181 |
|
|
}
|
182 |
|
|
|
183 |
|
|
static inline void insert_dquot_hash(struct dquot *dquot)
|
184 |
|
|
{
|
185 |
|
|
struct list_head *head = dquot_hash + hashfn(dquot->dq_sb, dquot->dq_id, dquot->dq_type);
|
186 |
|
|
list_add(&dquot->dq_hash, head);
|
187 |
|
|
}
|
188 |
|
|
|
189 |
|
|
static inline void remove_dquot_hash(struct dquot *dquot)
|
190 |
|
|
{
|
191 |
|
|
list_del(&dquot->dq_hash);
|
192 |
|
|
INIT_LIST_HEAD(&dquot->dq_hash);
|
193 |
|
|
}
|
194 |
|
|
|
195 |
|
|
static inline struct dquot *find_dquot(unsigned int hashent, struct super_block *sb, unsigned int id, int type)
|
196 |
|
|
{
|
197 |
|
|
struct list_head *head;
|
198 |
|
|
struct dquot *dquot;
|
199 |
|
|
|
200 |
|
|
for (head = dquot_hash[hashent].next; head != dquot_hash+hashent; head = head->next) {
|
201 |
|
|
dquot = list_entry(head, struct dquot, dq_hash);
|
202 |
|
|
if (dquot->dq_sb == sb && dquot->dq_id == id && dquot->dq_type == type)
|
203 |
|
|
return dquot;
|
204 |
|
|
}
|
205 |
|
|
return NODQUOT;
|
206 |
|
|
}
|
207 |
|
|
|
208 |
|
|
/* Add a dquot to the head of the free list */
|
209 |
|
|
static inline void put_dquot_head(struct dquot *dquot)
|
210 |
|
|
{
|
211 |
|
|
list_add(&dquot->dq_free, &free_dquots);
|
212 |
|
|
dqstats.free_dquots++;
|
213 |
|
|
}
|
214 |
|
|
|
215 |
|
|
/* Add a dquot to the tail of the free list */
|
216 |
|
|
static inline void put_dquot_last(struct dquot *dquot)
|
217 |
|
|
{
|
218 |
|
|
list_add(&dquot->dq_free, free_dquots.prev);
|
219 |
|
|
dqstats.free_dquots++;
|
220 |
|
|
}
|
221 |
|
|
|
222 |
|
|
/* Move dquot to the head of free list (it must be already on it) */
|
223 |
|
|
static inline void move_dquot_head(struct dquot *dquot)
|
224 |
|
|
{
|
225 |
|
|
list_del(&dquot->dq_free);
|
226 |
|
|
list_add(&dquot->dq_free, &free_dquots);
|
227 |
|
|
}
|
228 |
|
|
|
229 |
|
|
static inline void remove_free_dquot(struct dquot *dquot)
|
230 |
|
|
{
|
231 |
|
|
if (list_empty(&dquot->dq_free))
|
232 |
|
|
return;
|
233 |
|
|
list_del(&dquot->dq_free);
|
234 |
|
|
INIT_LIST_HEAD(&dquot->dq_free);
|
235 |
|
|
dqstats.free_dquots--;
|
236 |
|
|
}
|
237 |
|
|
|
238 |
|
|
static inline void put_inuse(struct dquot *dquot)
|
239 |
|
|
{
|
240 |
|
|
/* We add to the back of inuse list so we don't have to restart
|
241 |
|
|
* when traversing this list and we block */
|
242 |
|
|
list_add(&dquot->dq_inuse, inuse_list.prev);
|
243 |
|
|
dqstats.allocated_dquots++;
|
244 |
|
|
}
|
245 |
|
|
|
246 |
|
|
static inline void remove_inuse(struct dquot *dquot)
|
247 |
|
|
{
|
248 |
|
|
dqstats.allocated_dquots--;
|
249 |
|
|
list_del(&dquot->dq_inuse);
|
250 |
|
|
}
|
251 |
|
|
|
252 |
|
|
static void __wait_on_dquot(struct dquot *dquot)
|
253 |
|
|
{
|
254 |
|
|
DECLARE_WAITQUEUE(wait, current);
|
255 |
|
|
|
256 |
|
|
add_wait_queue(&dquot->dq_wait_lock, &wait);
|
257 |
|
|
repeat:
|
258 |
|
|
set_current_state(TASK_UNINTERRUPTIBLE);
|
259 |
|
|
if (dquot->dq_flags & DQ_LOCKED) {
|
260 |
|
|
schedule();
|
261 |
|
|
goto repeat;
|
262 |
|
|
}
|
263 |
|
|
remove_wait_queue(&dquot->dq_wait_lock, &wait);
|
264 |
|
|
current->state = TASK_RUNNING;
|
265 |
|
|
}
|
266 |
|
|
|
267 |
|
|
static inline void wait_on_dquot(struct dquot *dquot)
|
268 |
|
|
{
|
269 |
|
|
if (dquot->dq_flags & DQ_LOCKED)
|
270 |
|
|
__wait_on_dquot(dquot);
|
271 |
|
|
}
|
272 |
|
|
|
273 |
|
|
static inline void lock_dquot(struct dquot *dquot)
|
274 |
|
|
{
|
275 |
|
|
wait_on_dquot(dquot);
|
276 |
|
|
dquot->dq_flags |= DQ_LOCKED;
|
277 |
|
|
}
|
278 |
|
|
|
279 |
|
|
static inline void unlock_dquot(struct dquot *dquot)
|
280 |
|
|
{
|
281 |
|
|
dquot->dq_flags &= ~DQ_LOCKED;
|
282 |
|
|
wake_up(&dquot->dq_wait_lock);
|
283 |
|
|
}
|
284 |
|
|
|
285 |
|
|
/* Wait for dquot to be unused */
|
286 |
|
|
static void __wait_dquot_unused(struct dquot *dquot)
|
287 |
|
|
{
|
288 |
|
|
DECLARE_WAITQUEUE(wait, current);
|
289 |
|
|
|
290 |
|
|
add_wait_queue(&dquot->dq_wait_free, &wait);
|
291 |
|
|
repeat:
|
292 |
|
|
set_current_state(TASK_UNINTERRUPTIBLE);
|
293 |
|
|
if (dquot->dq_count) {
|
294 |
|
|
schedule();
|
295 |
|
|
goto repeat;
|
296 |
|
|
}
|
297 |
|
|
remove_wait_queue(&dquot->dq_wait_free, &wait);
|
298 |
|
|
current->state = TASK_RUNNING;
|
299 |
|
|
}
|
300 |
|
|
|
301 |
|
|
/* Wait for all duplicated dquot references to be dropped */
|
302 |
|
|
static void __wait_dup_drop(struct dquot *dquot)
|
303 |
|
|
{
|
304 |
|
|
DECLARE_WAITQUEUE(wait, current);
|
305 |
|
|
|
306 |
|
|
add_wait_queue(&dquot->dq_wait_free, &wait);
|
307 |
|
|
repeat:
|
308 |
|
|
set_current_state(TASK_UNINTERRUPTIBLE);
|
309 |
|
|
if (dquot->dq_dup_ref) {
|
310 |
|
|
schedule();
|
311 |
|
|
goto repeat;
|
312 |
|
|
}
|
313 |
|
|
remove_wait_queue(&dquot->dq_wait_free, &wait);
|
314 |
|
|
current->state = TASK_RUNNING;
|
315 |
|
|
}
|
316 |
|
|
|
317 |
|
|
static int read_dqblk(struct dquot *dquot)
|
318 |
|
|
{
|
319 |
|
|
int ret;
|
320 |
|
|
struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
|
321 |
|
|
|
322 |
|
|
lock_dquot(dquot);
|
323 |
|
|
down(&dqopt->dqio_sem);
|
324 |
|
|
ret = dqopt->ops[dquot->dq_type]->read_dqblk(dquot);
|
325 |
|
|
up(&dqopt->dqio_sem);
|
326 |
|
|
unlock_dquot(dquot);
|
327 |
|
|
return ret;
|
328 |
|
|
}
|
329 |
|
|
|
330 |
|
|
static int commit_dqblk(struct dquot *dquot)
|
331 |
|
|
{
|
332 |
|
|
int ret;
|
333 |
|
|
struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
|
334 |
|
|
|
335 |
|
|
down(&dqopt->dqio_sem);
|
336 |
|
|
ret = dqopt->ops[dquot->dq_type]->commit_dqblk(dquot);
|
337 |
|
|
up(&dqopt->dqio_sem);
|
338 |
|
|
return ret;
|
339 |
|
|
}
|
340 |
|
|
|
341 |
|
|
/* Invalidate all dquots on the list, wait for all users. Note that this function is called
|
342 |
|
|
* after quota is disabled so no new quota might be created. As we only insert to the end of
|
343 |
|
|
* inuse list, we don't have to restart searching... */
|
344 |
|
|
static void invalidate_dquots(struct super_block *sb, int type)
|
345 |
|
|
{
|
346 |
|
|
struct dquot *dquot;
|
347 |
|
|
struct list_head *head;
|
348 |
|
|
|
349 |
|
|
restart:
|
350 |
|
|
list_for_each(head, &inuse_list) {
|
351 |
|
|
dquot = list_entry(head, struct dquot, dq_inuse);
|
352 |
|
|
if (dquot->dq_sb != sb)
|
353 |
|
|
continue;
|
354 |
|
|
if (dquot->dq_type != type)
|
355 |
|
|
continue;
|
356 |
|
|
dquot->dq_flags |= DQ_INVAL;
|
357 |
|
|
if (dquot->dq_count)
|
358 |
|
|
/*
|
359 |
|
|
* Wait for any users of quota. As we have already cleared the flags in
|
360 |
|
|
* superblock and cleared all pointers from inodes we are assured
|
361 |
|
|
* that there will be no new users of this quota.
|
362 |
|
|
*/
|
363 |
|
|
__wait_dquot_unused(dquot);
|
364 |
|
|
/* Quota now have no users and it has been written on last dqput() */
|
365 |
|
|
remove_dquot_hash(dquot);
|
366 |
|
|
remove_free_dquot(dquot);
|
367 |
|
|
remove_inuse(dquot);
|
368 |
|
|
kmem_cache_free(dquot_cachep, dquot);
|
369 |
|
|
goto restart;
|
370 |
|
|
}
|
371 |
|
|
}
|
372 |
|
|
|
373 |
|
|
static int vfs_quota_sync(struct super_block *sb, int type)
|
374 |
|
|
{
|
375 |
|
|
struct list_head *head;
|
376 |
|
|
struct dquot *dquot;
|
377 |
|
|
struct quota_info *dqopt = sb_dqopt(sb);
|
378 |
|
|
int cnt;
|
379 |
|
|
|
380 |
|
|
restart:
|
381 |
|
|
list_for_each(head, &inuse_list) {
|
382 |
|
|
dquot = list_entry(head, struct dquot, dq_inuse);
|
383 |
|
|
if (sb && dquot->dq_sb != sb)
|
384 |
|
|
continue;
|
385 |
|
|
if (type != -1 && dquot->dq_type != type)
|
386 |
|
|
continue;
|
387 |
|
|
if (!dquot->dq_sb) /* Invalidated? */
|
388 |
|
|
continue;
|
389 |
|
|
if (!dquot_dirty(dquot) && !(dquot->dq_flags & DQ_LOCKED))
|
390 |
|
|
continue;
|
391 |
|
|
/* Get reference to quota so it won't be invalidated. get_dquot_ref()
|
392 |
|
|
* is enough since if dquot is locked/modified it can't be
|
393 |
|
|
* on the free list */
|
394 |
|
|
get_dquot_ref(dquot);
|
395 |
|
|
dqstats.lookups++;
|
396 |
|
|
if (dquot->dq_flags & DQ_LOCKED)
|
397 |
|
|
wait_on_dquot(dquot);
|
398 |
|
|
if (dquot_dirty(dquot))
|
399 |
|
|
sb->dq_op->write_dquot(dquot);
|
400 |
|
|
dqput(dquot);
|
401 |
|
|
goto restart;
|
402 |
|
|
}
|
403 |
|
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++)
|
404 |
|
|
if ((cnt == type || type == -1) && sb_has_quota_enabled(sb, cnt))
|
405 |
|
|
dqopt->info[cnt].dqi_flags &= ~DQF_ANY_DQUOT_DIRTY;
|
406 |
|
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++)
|
407 |
|
|
if ((cnt == type || type == -1) && sb_has_quota_enabled(sb, cnt) && info_dirty(&dqopt->info[cnt]))
|
408 |
|
|
dqopt->ops[cnt]->write_file_info(sb, cnt);
|
409 |
|
|
dqstats.syncs++;
|
410 |
|
|
|
411 |
|
|
return 0;
|
412 |
|
|
}
|
413 |
|
|
|
414 |
|
|
static struct super_block *get_super_to_sync(int type)
|
415 |
|
|
{
|
416 |
|
|
struct list_head *head;
|
417 |
|
|
int cnt, dirty;
|
418 |
|
|
|
419 |
|
|
restart:
|
420 |
|
|
spin_lock(&sb_lock);
|
421 |
|
|
list_for_each(head, &super_blocks) {
|
422 |
|
|
struct super_block *sb = list_entry(head, struct super_block, s_list);
|
423 |
|
|
|
424 |
|
|
for (cnt = 0, dirty = 0; cnt < MAXQUOTAS; cnt++)
|
425 |
|
|
if ((type == cnt || type == -1) && sb_has_quota_enabled(sb, cnt)
|
426 |
|
|
&& sb_dqopt(sb)->info[cnt].dqi_flags & DQF_ANY_DQUOT_DIRTY)
|
427 |
|
|
dirty = 1;
|
428 |
|
|
if (!dirty)
|
429 |
|
|
continue;
|
430 |
|
|
sb->s_count++;
|
431 |
|
|
spin_unlock(&sb_lock);
|
432 |
|
|
down_read(&sb->s_umount);
|
433 |
|
|
if (!sb->s_root) {
|
434 |
|
|
drop_super(sb);
|
435 |
|
|
goto restart;
|
436 |
|
|
}
|
437 |
|
|
return sb;
|
438 |
|
|
}
|
439 |
|
|
spin_unlock(&sb_lock);
|
440 |
|
|
return NULL;
|
441 |
|
|
}
|
442 |
|
|
|
443 |
|
|
void sync_dquots_dev(kdev_t dev, int type)
|
444 |
|
|
{
|
445 |
|
|
struct super_block *sb;
|
446 |
|
|
|
447 |
|
|
if (dev) {
|
448 |
|
|
if ((sb = get_super(dev))) {
|
449 |
|
|
lock_kernel();
|
450 |
|
|
if (sb->s_qcop->quota_sync)
|
451 |
|
|
sb->s_qcop->quota_sync(sb, type);
|
452 |
|
|
unlock_kernel();
|
453 |
|
|
drop_super(sb);
|
454 |
|
|
}
|
455 |
|
|
}
|
456 |
|
|
else {
|
457 |
|
|
while ((sb = get_super_to_sync(type))) {
|
458 |
|
|
lock_kernel();
|
459 |
|
|
if (sb->s_qcop->quota_sync)
|
460 |
|
|
sb->s_qcop->quota_sync(sb, type);
|
461 |
|
|
unlock_kernel();
|
462 |
|
|
drop_super(sb);
|
463 |
|
|
}
|
464 |
|
|
}
|
465 |
|
|
}
|
466 |
|
|
|
467 |
|
|
void sync_dquots_sb(struct super_block *sb, int type)
|
468 |
|
|
{
|
469 |
|
|
lock_kernel();
|
470 |
|
|
if (sb->s_qcop->quota_sync)
|
471 |
|
|
sb->s_qcop->quota_sync(sb, type);
|
472 |
|
|
unlock_kernel();
|
473 |
|
|
}
|
474 |
|
|
|
475 |
|
|
/* Free unused dquots from cache */
|
476 |
|
|
static void prune_dqcache(int count)
|
477 |
|
|
{
|
478 |
|
|
struct list_head *head;
|
479 |
|
|
struct dquot *dquot;
|
480 |
|
|
|
481 |
|
|
head = free_dquots.prev;
|
482 |
|
|
while (head != &free_dquots && count) {
|
483 |
|
|
dquot = list_entry(head, struct dquot, dq_free);
|
484 |
|
|
remove_dquot_hash(dquot);
|
485 |
|
|
remove_free_dquot(dquot);
|
486 |
|
|
remove_inuse(dquot);
|
487 |
|
|
kmem_cache_free(dquot_cachep, dquot);
|
488 |
|
|
count--;
|
489 |
|
|
head = free_dquots.prev;
|
490 |
|
|
}
|
491 |
|
|
}
|
492 |
|
|
|
493 |
|
|
/*
|
494 |
|
|
* This is called from kswapd when we think we need some
|
495 |
|
|
* more memory, but aren't really sure how much. So we
|
496 |
|
|
* carefully try to free a _bit_ of our dqcache, but not
|
497 |
|
|
* too much.
|
498 |
|
|
*
|
499 |
|
|
* Priority:
|
500 |
|
|
* 1 - very urgent: shrink everything
|
501 |
|
|
* ...
|
502 |
|
|
* 6 - base-level: try to shrink a bit.
|
503 |
|
|
*/
|
504 |
|
|
|
505 |
|
|
int shrink_dqcache_memory(int priority, unsigned int gfp_mask)
|
506 |
|
|
{
|
507 |
|
|
int count = 0;
|
508 |
|
|
|
509 |
|
|
lock_kernel();
|
510 |
|
|
count = dqstats.free_dquots / priority;
|
511 |
|
|
prune_dqcache(count);
|
512 |
|
|
unlock_kernel();
|
513 |
|
|
return kmem_cache_shrink(dquot_cachep);
|
514 |
|
|
}
|
515 |
|
|
|
516 |
|
|
/*
|
517 |
|
|
* Put reference to dquot
|
518 |
|
|
* NOTE: If you change this function please check whether dqput_blocks() works right...
|
519 |
|
|
*/
|
520 |
|
|
static void dqput(struct dquot *dquot)
|
521 |
|
|
{
|
522 |
|
|
if (!dquot)
|
523 |
|
|
return;
|
524 |
|
|
#ifdef __DQUOT_PARANOIA
|
525 |
|
|
if (!dquot->dq_count) {
|
526 |
|
|
printk("VFS: dqput: trying to free free dquot\n");
|
527 |
|
|
printk("VFS: device %s, dquot of %s %d\n",
|
528 |
|
|
kdevname(dquot->dq_dev), quotatypes[dquot->dq_type],
|
529 |
|
|
dquot->dq_id);
|
530 |
|
|
return;
|
531 |
|
|
}
|
532 |
|
|
#endif
|
533 |
|
|
|
534 |
|
|
dqstats.drops++;
|
535 |
|
|
we_slept:
|
536 |
|
|
if (dquot->dq_dup_ref && dquot->dq_count - dquot->dq_dup_ref <= 1) { /* Last unduplicated reference? */
|
537 |
|
|
__wait_dup_drop(dquot);
|
538 |
|
|
goto we_slept;
|
539 |
|
|
}
|
540 |
|
|
if (dquot->dq_count > 1) {
|
541 |
|
|
/* We have more than one user... We can simply decrement use count */
|
542 |
|
|
put_dquot_ref(dquot);
|
543 |
|
|
return;
|
544 |
|
|
}
|
545 |
|
|
if (dquot_dirty(dquot)) {
|
546 |
|
|
dquot->dq_sb->dq_op->write_dquot(dquot);
|
547 |
|
|
goto we_slept;
|
548 |
|
|
}
|
549 |
|
|
|
550 |
|
|
/* sanity check */
|
551 |
|
|
if (!list_empty(&dquot->dq_free)) {
|
552 |
|
|
printk(KERN_ERR "dqput: dquot already on free list??\n");
|
553 |
|
|
put_dquot_ref(dquot);
|
554 |
|
|
return;
|
555 |
|
|
}
|
556 |
|
|
put_dquot_ref(dquot);
|
557 |
|
|
/* If dquot is going to be invalidated invalidate_dquots() is going to free it so */
|
558 |
|
|
if (!(dquot->dq_flags & DQ_INVAL))
|
559 |
|
|
put_dquot_last(dquot); /* Place at end of LRU free queue */
|
560 |
|
|
wake_up(&dquot->dq_wait_free);
|
561 |
|
|
}
|
562 |
|
|
|
563 |
|
|
static struct dquot *get_empty_dquot(struct super_block *sb, int type)
|
564 |
|
|
{
|
565 |
|
|
struct dquot *dquot;
|
566 |
|
|
|
567 |
|
|
dquot = kmem_cache_alloc(dquot_cachep, SLAB_KERNEL);
|
568 |
|
|
if(!dquot)
|
569 |
|
|
return NODQUOT;
|
570 |
|
|
|
571 |
|
|
memset((caddr_t)dquot, 0, sizeof(struct dquot));
|
572 |
|
|
init_waitqueue_head(&dquot->dq_wait_free);
|
573 |
|
|
init_waitqueue_head(&dquot->dq_wait_lock);
|
574 |
|
|
INIT_LIST_HEAD(&dquot->dq_free);
|
575 |
|
|
INIT_LIST_HEAD(&dquot->dq_inuse);
|
576 |
|
|
INIT_LIST_HEAD(&dquot->dq_hash);
|
577 |
|
|
dquot->dq_sb = sb;
|
578 |
|
|
dquot->dq_dev = sb->s_dev;
|
579 |
|
|
dquot->dq_type = type;
|
580 |
|
|
dquot->dq_count = 1;
|
581 |
|
|
/* all dquots go on the inuse_list */
|
582 |
|
|
put_inuse(dquot);
|
583 |
|
|
|
584 |
|
|
return dquot;
|
585 |
|
|
}
|
586 |
|
|
|
587 |
|
|
static struct dquot *dqget(struct super_block *sb, unsigned int id, int type)
|
588 |
|
|
{
|
589 |
|
|
unsigned int hashent = hashfn(sb, id, type);
|
590 |
|
|
struct dquot *dquot, *empty = NODQUOT;
|
591 |
|
|
struct quota_info *dqopt = sb_dqopt(sb);
|
592 |
|
|
|
593 |
|
|
we_slept:
|
594 |
|
|
if (!is_enabled(dqopt, type)) {
|
595 |
|
|
if (empty)
|
596 |
|
|
dqput(empty);
|
597 |
|
|
return NODQUOT;
|
598 |
|
|
}
|
599 |
|
|
|
600 |
|
|
if ((dquot = find_dquot(hashent, sb, id, type)) == NODQUOT) {
|
601 |
|
|
if (empty == NODQUOT) {
|
602 |
|
|
if ((empty = get_empty_dquot(sb, type)) == NODQUOT)
|
603 |
|
|
schedule(); /* Try to wait for a moment... */
|
604 |
|
|
goto we_slept;
|
605 |
|
|
}
|
606 |
|
|
dquot = empty;
|
607 |
|
|
dquot->dq_id = id;
|
608 |
|
|
/* hash it first so it can be found */
|
609 |
|
|
insert_dquot_hash(dquot);
|
610 |
|
|
read_dqblk(dquot);
|
611 |
|
|
} else {
|
612 |
|
|
if (!dquot->dq_count)
|
613 |
|
|
remove_free_dquot(dquot);
|
614 |
|
|
get_dquot_ref(dquot);
|
615 |
|
|
dqstats.cache_hits++;
|
616 |
|
|
wait_on_dquot(dquot);
|
617 |
|
|
if (empty)
|
618 |
|
|
dqput(empty);
|
619 |
|
|
}
|
620 |
|
|
|
621 |
|
|
if (!dquot->dq_sb) { /* Has somebody invalidated entry under us? */
|
622 |
|
|
printk(KERN_ERR "VFS: dqget(): Quota invalidated in dqget()!\n");
|
623 |
|
|
dqput(dquot);
|
624 |
|
|
return NODQUOT;
|
625 |
|
|
}
|
626 |
|
|
dqstats.lookups++;
|
627 |
|
|
|
628 |
|
|
return dquot;
|
629 |
|
|
}
|
630 |
|
|
|
631 |
|
|
/* Duplicate reference to dquot got from inode */
|
632 |
|
|
static struct dquot *dqduplicate(struct dquot *dquot)
|
633 |
|
|
{
|
634 |
|
|
if (dquot == NODQUOT)
|
635 |
|
|
return NODQUOT;
|
636 |
|
|
get_dquot_ref(dquot);
|
637 |
|
|
if (!dquot->dq_sb) {
|
638 |
|
|
printk(KERN_ERR "VFS: dqduplicate(): Invalidated quota to be duplicated!\n");
|
639 |
|
|
put_dquot_ref(dquot);
|
640 |
|
|
return NODQUOT;
|
641 |
|
|
}
|
642 |
|
|
if (dquot->dq_flags & DQ_LOCKED)
|
643 |
|
|
printk(KERN_ERR "VFS: dqduplicate(): Locked quota to be duplicated!\n");
|
644 |
|
|
get_dquot_dup_ref(dquot);
|
645 |
|
|
dqstats.lookups++;
|
646 |
|
|
|
647 |
|
|
return dquot;
|
648 |
|
|
}
|
649 |
|
|
|
650 |
|
|
/* Put duplicated reference */
|
651 |
|
|
static void dqputduplicate(struct dquot *dquot)
|
652 |
|
|
{
|
653 |
|
|
if (!dquot->dq_dup_ref) {
|
654 |
|
|
printk(KERN_ERR "VFS: dqputduplicate(): Duplicated dquot put without duplicate reference.\n");
|
655 |
|
|
return;
|
656 |
|
|
}
|
657 |
|
|
put_dquot_dup_ref(dquot);
|
658 |
|
|
if (!dquot->dq_dup_ref)
|
659 |
|
|
wake_up(&dquot->dq_wait_free);
|
660 |
|
|
put_dquot_ref(dquot);
|
661 |
|
|
dqstats.drops++;
|
662 |
|
|
}
|
663 |
|
|
|
664 |
|
|
static int dqinit_needed(struct inode *inode, int type)
|
665 |
|
|
{
|
666 |
|
|
int cnt;
|
667 |
|
|
|
668 |
|
|
if (IS_NOQUOTA(inode))
|
669 |
|
|
return 0;
|
670 |
|
|
if (type != -1)
|
671 |
|
|
return inode->i_dquot[type] == NODQUOT;
|
672 |
|
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++)
|
673 |
|
|
if (inode->i_dquot[cnt] == NODQUOT)
|
674 |
|
|
return 1;
|
675 |
|
|
return 0;
|
676 |
|
|
}
|
677 |
|
|
|
678 |
|
|
static void add_dquot_ref(struct super_block *sb, int type)
|
679 |
|
|
{
|
680 |
|
|
struct list_head *p;
|
681 |
|
|
|
682 |
|
|
restart:
|
683 |
|
|
file_list_lock();
|
684 |
|
|
list_for_each(p, &sb->s_files) {
|
685 |
|
|
struct file *filp = list_entry(p, struct file, f_list);
|
686 |
|
|
struct inode *inode = filp->f_dentry->d_inode;
|
687 |
|
|
if (filp->f_mode & FMODE_WRITE && dqinit_needed(inode, type)) {
|
688 |
|
|
struct vfsmount *mnt = mntget(filp->f_vfsmnt);
|
689 |
|
|
struct dentry *dentry = dget(filp->f_dentry);
|
690 |
|
|
file_list_unlock();
|
691 |
|
|
sb->dq_op->initialize(inode, type);
|
692 |
|
|
dput(dentry);
|
693 |
|
|
mntput(mnt);
|
694 |
|
|
/* As we may have blocked we had better restart... */
|
695 |
|
|
goto restart;
|
696 |
|
|
}
|
697 |
|
|
}
|
698 |
|
|
file_list_unlock();
|
699 |
|
|
}
|
700 |
|
|
|
701 |
|
|
/* Return 0 if dqput() won't block (note that 1 doesn't necessarily mean blocking) */
|
702 |
|
|
static inline int dqput_blocks(struct dquot *dquot)
|
703 |
|
|
{
|
704 |
|
|
if (dquot->dq_dup_ref && dquot->dq_count - dquot->dq_dup_ref <= 1)
|
705 |
|
|
return 1;
|
706 |
|
|
if (dquot->dq_count <= 1 && dquot->dq_flags & DQ_MOD)
|
707 |
|
|
return 1;
|
708 |
|
|
return 0;
|
709 |
|
|
}
|
710 |
|
|
|
711 |
|
|
/* Remove references to dquots from inode - add dquot to list for freeing if needed */
|
712 |
|
|
int remove_inode_dquot_ref(struct inode *inode, int type, struct list_head *tofree_head)
|
713 |
|
|
{
|
714 |
|
|
struct dquot *dquot = inode->i_dquot[type];
|
715 |
|
|
int cnt;
|
716 |
|
|
|
717 |
|
|
inode->i_dquot[type] = NODQUOT;
|
718 |
|
|
/* any other quota in use? */
|
719 |
|
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
720 |
|
|
if (inode->i_dquot[cnt] != NODQUOT)
|
721 |
|
|
goto put_it;
|
722 |
|
|
}
|
723 |
|
|
inode->i_flags &= ~S_QUOTA;
|
724 |
|
|
put_it:
|
725 |
|
|
if (dquot != NODQUOT) {
|
726 |
|
|
if (dqput_blocks(dquot)) {
|
727 |
|
|
if (dquot->dq_count != 1)
|
728 |
|
|
printk(KERN_WARNING "VFS: Adding dquot with dq_count %d to dispose list.\n", dquot->dq_count);
|
729 |
|
|
list_add(&dquot->dq_free, tofree_head); /* As dquot must have currently users it can't be on the free list... */
|
730 |
|
|
return 1;
|
731 |
|
|
}
|
732 |
|
|
else
|
733 |
|
|
dqput(dquot); /* We have guaranteed we won't block */
|
734 |
|
|
}
|
735 |
|
|
return 0;
|
736 |
|
|
}
|
737 |
|
|
|
738 |
|
|
/* Free list of dquots - called from inode.c */
|
739 |
|
|
void put_dquot_list(struct list_head *tofree_head)
|
740 |
|
|
{
|
741 |
|
|
struct list_head *act_head;
|
742 |
|
|
struct dquot *dquot;
|
743 |
|
|
|
744 |
|
|
lock_kernel();
|
745 |
|
|
act_head = tofree_head->next;
|
746 |
|
|
/* So now we have dquots on the list... Just free them */
|
747 |
|
|
while (act_head != tofree_head) {
|
748 |
|
|
dquot = list_entry(act_head, struct dquot, dq_free);
|
749 |
|
|
act_head = act_head->next;
|
750 |
|
|
list_del(&dquot->dq_free); /* Remove dquot from the list so we won't have problems... */
|
751 |
|
|
INIT_LIST_HEAD(&dquot->dq_free);
|
752 |
|
|
dqput(dquot);
|
753 |
|
|
}
|
754 |
|
|
unlock_kernel();
|
755 |
|
|
}
|
756 |
|
|
|
757 |
|
|
static inline void dquot_incr_inodes(struct dquot *dquot, unsigned long number)
|
758 |
|
|
{
|
759 |
|
|
dquot->dq_dqb.dqb_curinodes += number;
|
760 |
|
|
mark_dquot_dirty(dquot);
|
761 |
|
|
}
|
762 |
|
|
|
763 |
|
|
static inline void dquot_incr_space(struct dquot *dquot, qsize_t number)
|
764 |
|
|
{
|
765 |
|
|
dquot->dq_dqb.dqb_curspace += number;
|
766 |
|
|
mark_dquot_dirty(dquot);
|
767 |
|
|
}
|
768 |
|
|
|
769 |
|
|
static inline void dquot_decr_inodes(struct dquot *dquot, unsigned long number)
|
770 |
|
|
{
|
771 |
|
|
if (dquot->dq_dqb.dqb_curinodes > number)
|
772 |
|
|
dquot->dq_dqb.dqb_curinodes -= number;
|
773 |
|
|
else
|
774 |
|
|
dquot->dq_dqb.dqb_curinodes = 0;
|
775 |
|
|
if (dquot->dq_dqb.dqb_curinodes < dquot->dq_dqb.dqb_isoftlimit)
|
776 |
|
|
dquot->dq_dqb.dqb_itime = (time_t) 0;
|
777 |
|
|
dquot->dq_flags &= ~DQ_INODES;
|
778 |
|
|
mark_dquot_dirty(dquot);
|
779 |
|
|
}
|
780 |
|
|
|
781 |
|
|
static inline void dquot_decr_space(struct dquot *dquot, qsize_t number)
|
782 |
|
|
{
|
783 |
|
|
if (dquot->dq_dqb.dqb_curspace > number)
|
784 |
|
|
dquot->dq_dqb.dqb_curspace -= number;
|
785 |
|
|
else
|
786 |
|
|
dquot->dq_dqb.dqb_curspace = 0;
|
787 |
|
|
if (toqb(dquot->dq_dqb.dqb_curspace) < dquot->dq_dqb.dqb_bsoftlimit)
|
788 |
|
|
dquot->dq_dqb.dqb_btime = (time_t) 0;
|
789 |
|
|
dquot->dq_flags &= ~DQ_BLKS;
|
790 |
|
|
mark_dquot_dirty(dquot);
|
791 |
|
|
}
|
792 |
|
|
|
793 |
|
|
static inline int need_print_warning(struct dquot *dquot, int flag)
|
794 |
|
|
{
|
795 |
|
|
switch (dquot->dq_type) {
|
796 |
|
|
case USRQUOTA:
|
797 |
|
|
return current->fsuid == dquot->dq_id && !(dquot->dq_flags & flag);
|
798 |
|
|
case GRPQUOTA:
|
799 |
|
|
return in_group_p(dquot->dq_id) && !(dquot->dq_flags & flag);
|
800 |
|
|
}
|
801 |
|
|
return 0;
|
802 |
|
|
}
|
803 |
|
|
|
804 |
|
|
/* Values of warnings */
|
805 |
|
|
#define NOWARN 0
|
806 |
|
|
#define IHARDWARN 1
|
807 |
|
|
#define ISOFTLONGWARN 2
|
808 |
|
|
#define ISOFTWARN 3
|
809 |
|
|
#define BHARDWARN 4
|
810 |
|
|
#define BSOFTLONGWARN 5
|
811 |
|
|
#define BSOFTWARN 6
|
812 |
|
|
|
813 |
|
|
/* Print warning to user which exceeded quota */
|
814 |
|
|
static void print_warning(struct dquot *dquot, const char warntype)
|
815 |
|
|
{
|
816 |
|
|
char *msg = NULL;
|
817 |
|
|
int flag = (warntype == BHARDWARN || warntype == BSOFTLONGWARN) ? DQ_BLKS :
|
818 |
|
|
((warntype == IHARDWARN || warntype == ISOFTLONGWARN) ? DQ_INODES : 0);
|
819 |
|
|
|
820 |
|
|
if (!need_print_warning(dquot, flag))
|
821 |
|
|
return;
|
822 |
|
|
dquot->dq_flags |= flag;
|
823 |
|
|
tty_write_message(current->tty, (char *)bdevname(dquot->dq_sb->s_dev));
|
824 |
|
|
if (warntype == ISOFTWARN || warntype == BSOFTWARN)
|
825 |
|
|
tty_write_message(current->tty, ": warning, ");
|
826 |
|
|
else
|
827 |
|
|
tty_write_message(current->tty, ": write failed, ");
|
828 |
|
|
tty_write_message(current->tty, quotatypes[dquot->dq_type]);
|
829 |
|
|
switch (warntype) {
|
830 |
|
|
case IHARDWARN:
|
831 |
|
|
msg = " file limit reached.\r\n";
|
832 |
|
|
break;
|
833 |
|
|
case ISOFTLONGWARN:
|
834 |
|
|
msg = " file quota exceeded too long.\r\n";
|
835 |
|
|
break;
|
836 |
|
|
case ISOFTWARN:
|
837 |
|
|
msg = " file quota exceeded.\r\n";
|
838 |
|
|
break;
|
839 |
|
|
case BHARDWARN:
|
840 |
|
|
msg = " block limit reached.\r\n";
|
841 |
|
|
break;
|
842 |
|
|
case BSOFTLONGWARN:
|
843 |
|
|
msg = " block quota exceeded too long.\r\n";
|
844 |
|
|
break;
|
845 |
|
|
case BSOFTWARN:
|
846 |
|
|
msg = " block quota exceeded.\r\n";
|
847 |
|
|
break;
|
848 |
|
|
}
|
849 |
|
|
tty_write_message(current->tty, msg);
|
850 |
|
|
}
|
851 |
|
|
|
852 |
|
|
static inline void flush_warnings(struct dquot **dquots, char *warntype)
|
853 |
|
|
{
|
854 |
|
|
int i;
|
855 |
|
|
|
856 |
|
|
for (i = 0; i < MAXQUOTAS; i++)
|
857 |
|
|
if (dquots[i] != NODQUOT && warntype[i] != NOWARN)
|
858 |
|
|
print_warning(dquots[i], warntype[i]);
|
859 |
|
|
}
|
860 |
|
|
|
861 |
|
|
static inline char ignore_hardlimit(struct dquot *dquot)
|
862 |
|
|
{
|
863 |
|
|
struct mem_dqinfo *info = &sb_dqopt(dquot->dq_sb)->info[dquot->dq_type];
|
864 |
|
|
|
865 |
|
|
return capable(CAP_SYS_RESOURCE) &&
|
866 |
|
|
(info->dqi_format->qf_fmt_id != QFMT_VFS_OLD || !(info->dqi_flags & V1_DQF_RSQUASH));
|
867 |
|
|
}
|
868 |
|
|
|
869 |
|
|
static int check_idq(struct dquot *dquot, ulong inodes, char *warntype)
|
870 |
|
|
{
|
871 |
|
|
*warntype = NOWARN;
|
872 |
|
|
if (inodes <= 0 || dquot->dq_flags & DQ_FAKE)
|
873 |
|
|
return QUOTA_OK;
|
874 |
|
|
|
875 |
|
|
if (dquot->dq_dqb.dqb_ihardlimit &&
|
876 |
|
|
(dquot->dq_dqb.dqb_curinodes + inodes) > dquot->dq_dqb.dqb_ihardlimit &&
|
877 |
|
|
!ignore_hardlimit(dquot)) {
|
878 |
|
|
*warntype = IHARDWARN;
|
879 |
|
|
return NO_QUOTA;
|
880 |
|
|
}
|
881 |
|
|
|
882 |
|
|
if (dquot->dq_dqb.dqb_isoftlimit &&
|
883 |
|
|
(dquot->dq_dqb.dqb_curinodes + inodes) > dquot->dq_dqb.dqb_isoftlimit &&
|
884 |
|
|
dquot->dq_dqb.dqb_itime && CURRENT_TIME >= dquot->dq_dqb.dqb_itime &&
|
885 |
|
|
!ignore_hardlimit(dquot)) {
|
886 |
|
|
*warntype = ISOFTLONGWARN;
|
887 |
|
|
return NO_QUOTA;
|
888 |
|
|
}
|
889 |
|
|
|
890 |
|
|
if (dquot->dq_dqb.dqb_isoftlimit &&
|
891 |
|
|
(dquot->dq_dqb.dqb_curinodes + inodes) > dquot->dq_dqb.dqb_isoftlimit &&
|
892 |
|
|
dquot->dq_dqb.dqb_itime == 0) {
|
893 |
|
|
*warntype = ISOFTWARN;
|
894 |
|
|
dquot->dq_dqb.dqb_itime = CURRENT_TIME + sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_igrace;
|
895 |
|
|
}
|
896 |
|
|
|
897 |
|
|
return QUOTA_OK;
|
898 |
|
|
}
|
899 |
|
|
|
900 |
|
|
static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *warntype)
|
901 |
|
|
{
|
902 |
|
|
*warntype = 0;
|
903 |
|
|
if (space <= 0 || dquot->dq_flags & DQ_FAKE)
|
904 |
|
|
return QUOTA_OK;
|
905 |
|
|
|
906 |
|
|
if (dquot->dq_dqb.dqb_bhardlimit &&
|
907 |
|
|
toqb(dquot->dq_dqb.dqb_curspace + space) > dquot->dq_dqb.dqb_bhardlimit &&
|
908 |
|
|
!ignore_hardlimit(dquot)) {
|
909 |
|
|
if (!prealloc)
|
910 |
|
|
*warntype = BHARDWARN;
|
911 |
|
|
return NO_QUOTA;
|
912 |
|
|
}
|
913 |
|
|
|
914 |
|
|
if (dquot->dq_dqb.dqb_bsoftlimit &&
|
915 |
|
|
toqb(dquot->dq_dqb.dqb_curspace + space) > dquot->dq_dqb.dqb_bsoftlimit &&
|
916 |
|
|
dquot->dq_dqb.dqb_btime && CURRENT_TIME >= dquot->dq_dqb.dqb_btime &&
|
917 |
|
|
!ignore_hardlimit(dquot)) {
|
918 |
|
|
if (!prealloc)
|
919 |
|
|
*warntype = BSOFTLONGWARN;
|
920 |
|
|
return NO_QUOTA;
|
921 |
|
|
}
|
922 |
|
|
|
923 |
|
|
if (dquot->dq_dqb.dqb_bsoftlimit &&
|
924 |
|
|
toqb(dquot->dq_dqb.dqb_curspace + space) > dquot->dq_dqb.dqb_bsoftlimit &&
|
925 |
|
|
dquot->dq_dqb.dqb_btime == 0) {
|
926 |
|
|
if (!prealloc) {
|
927 |
|
|
*warntype = BSOFTWARN;
|
928 |
|
|
dquot->dq_dqb.dqb_btime = CURRENT_TIME + sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_bgrace;
|
929 |
|
|
}
|
930 |
|
|
else
|
931 |
|
|
/*
|
932 |
|
|
* We don't allow preallocation to exceed softlimit so exceeding will
|
933 |
|
|
* be always printed
|
934 |
|
|
*/
|
935 |
|
|
return NO_QUOTA;
|
936 |
|
|
}
|
937 |
|
|
|
938 |
|
|
return QUOTA_OK;
|
939 |
|
|
}
|
940 |
|
|
|
941 |
|
|
/*
|
942 |
|
|
* Externally referenced functions through dquot_operations in inode.
|
943 |
|
|
*
|
944 |
|
|
* Note: this is a blocking operation.
|
945 |
|
|
*/
|
946 |
|
|
void dquot_initialize(struct inode *inode, int type)
|
947 |
|
|
{
|
948 |
|
|
struct dquot *dquot[MAXQUOTAS];
|
949 |
|
|
unsigned int id = 0;
|
950 |
|
|
int cnt;
|
951 |
|
|
|
952 |
|
|
if (IS_NOQUOTA(inode))
|
953 |
|
|
return;
|
954 |
|
|
/* Build list of quotas to initialize... We can block here */
|
955 |
|
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
956 |
|
|
dquot[cnt] = NODQUOT;
|
957 |
|
|
if (type != -1 && cnt != type)
|
958 |
|
|
continue;
|
959 |
|
|
if (!sb_has_quota_enabled(inode->i_sb, cnt))
|
960 |
|
|
continue;
|
961 |
|
|
if (inode->i_dquot[cnt] == NODQUOT) {
|
962 |
|
|
switch (cnt) {
|
963 |
|
|
case USRQUOTA:
|
964 |
|
|
id = inode->i_uid;
|
965 |
|
|
break;
|
966 |
|
|
case GRPQUOTA:
|
967 |
|
|
id = inode->i_gid;
|
968 |
|
|
break;
|
969 |
|
|
}
|
970 |
|
|
dquot[cnt] = dqget(inode->i_sb, id, cnt);
|
971 |
|
|
}
|
972 |
|
|
}
|
973 |
|
|
/* NOBLOCK START: Here we shouldn't block */
|
974 |
|
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
975 |
|
|
if (dquot[cnt] == NODQUOT || !sb_has_quota_enabled(inode->i_sb, cnt) || inode->i_dquot[cnt] != NODQUOT)
|
976 |
|
|
continue;
|
977 |
|
|
inode->i_dquot[cnt] = dquot[cnt];
|
978 |
|
|
dquot[cnt] = NODQUOT;
|
979 |
|
|
inode->i_flags |= S_QUOTA;
|
980 |
|
|
}
|
981 |
|
|
/* NOBLOCK END */
|
982 |
|
|
/* Put quotas which we didn't use */
|
983 |
|
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++)
|
984 |
|
|
if (dquot[cnt] != NODQUOT)
|
985 |
|
|
dqput(dquot[cnt]);
|
986 |
|
|
}
|
987 |
|
|
|
988 |
|
|
/*
|
989 |
|
|
* Release all quota for the specified inode.
|
990 |
|
|
*
|
991 |
|
|
* Note: this is a blocking operation.
|
992 |
|
|
*/
|
993 |
|
|
void dquot_drop(struct inode *inode)
|
994 |
|
|
{
|
995 |
|
|
struct dquot *dquot;
|
996 |
|
|
int cnt;
|
997 |
|
|
|
998 |
|
|
inode->i_flags &= ~S_QUOTA;
|
999 |
|
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
1000 |
|
|
if (inode->i_dquot[cnt] == NODQUOT)
|
1001 |
|
|
continue;
|
1002 |
|
|
dquot = inode->i_dquot[cnt];
|
1003 |
|
|
inode->i_dquot[cnt] = NODQUOT;
|
1004 |
|
|
dqput(dquot);
|
1005 |
|
|
}
|
1006 |
|
|
}
|
1007 |
|
|
|
1008 |
|
|
/*
|
1009 |
|
|
* This operation can block, but only after everything is updated
|
1010 |
|
|
*/
|
1011 |
|
|
int dquot_alloc_space(struct inode *inode, qsize_t number, int warn)
|
1012 |
|
|
{
|
1013 |
|
|
int cnt, ret = NO_QUOTA;
|
1014 |
|
|
struct dquot *dquot[MAXQUOTAS];
|
1015 |
|
|
char warntype[MAXQUOTAS];
|
1016 |
|
|
|
1017 |
|
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
1018 |
|
|
dquot[cnt] = NODQUOT;
|
1019 |
|
|
warntype[cnt] = NOWARN;
|
1020 |
|
|
}
|
1021 |
|
|
/* NOBLOCK Start */
|
1022 |
|
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
1023 |
|
|
dquot[cnt] = dqduplicate(inode->i_dquot[cnt]);
|
1024 |
|
|
if (dquot[cnt] == NODQUOT)
|
1025 |
|
|
continue;
|
1026 |
|
|
if (check_bdq(dquot[cnt], number, warn, warntype+cnt) == NO_QUOTA)
|
1027 |
|
|
goto warn_put_all;
|
1028 |
|
|
}
|
1029 |
|
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
1030 |
|
|
if (dquot[cnt] == NODQUOT)
|
1031 |
|
|
continue;
|
1032 |
|
|
dquot_incr_space(dquot[cnt], number);
|
1033 |
|
|
}
|
1034 |
|
|
inode_add_bytes(inode, number);
|
1035 |
|
|
/* NOBLOCK End */
|
1036 |
|
|
ret = QUOTA_OK;
|
1037 |
|
|
warn_put_all:
|
1038 |
|
|
flush_warnings(dquot, warntype);
|
1039 |
|
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++)
|
1040 |
|
|
if (dquot[cnt] != NODQUOT)
|
1041 |
|
|
dqputduplicate(dquot[cnt]);
|
1042 |
|
|
return ret;
|
1043 |
|
|
}
|
1044 |
|
|
|
1045 |
|
|
/*
|
1046 |
|
|
* This operation can block, but only after everything is updated
|
1047 |
|
|
*/
|
1048 |
|
|
int dquot_alloc_inode(const struct inode *inode, unsigned long number)
|
1049 |
|
|
{
|
1050 |
|
|
int cnt, ret = NO_QUOTA;
|
1051 |
|
|
struct dquot *dquot[MAXQUOTAS];
|
1052 |
|
|
char warntype[MAXQUOTAS];
|
1053 |
|
|
|
1054 |
|
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
1055 |
|
|
dquot[cnt] = NODQUOT;
|
1056 |
|
|
warntype[cnt] = NOWARN;
|
1057 |
|
|
}
|
1058 |
|
|
/* NOBLOCK Start */
|
1059 |
|
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
1060 |
|
|
dquot[cnt] = dqduplicate(inode -> i_dquot[cnt]);
|
1061 |
|
|
if (dquot[cnt] == NODQUOT)
|
1062 |
|
|
continue;
|
1063 |
|
|
if (check_idq(dquot[cnt], number, warntype+cnt) == NO_QUOTA)
|
1064 |
|
|
goto warn_put_all;
|
1065 |
|
|
}
|
1066 |
|
|
|
1067 |
|
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
1068 |
|
|
if (dquot[cnt] == NODQUOT)
|
1069 |
|
|
continue;
|
1070 |
|
|
dquot_incr_inodes(dquot[cnt], number);
|
1071 |
|
|
}
|
1072 |
|
|
/* NOBLOCK End */
|
1073 |
|
|
ret = QUOTA_OK;
|
1074 |
|
|
warn_put_all:
|
1075 |
|
|
flush_warnings(dquot, warntype);
|
1076 |
|
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++)
|
1077 |
|
|
if (dquot[cnt] != NODQUOT)
|
1078 |
|
|
dqputduplicate(dquot[cnt]);
|
1079 |
|
|
return ret;
|
1080 |
|
|
}
|
1081 |
|
|
|
1082 |
|
|
/*
|
1083 |
|
|
* This is a non-blocking operation.
|
1084 |
|
|
*/
|
1085 |
|
|
void dquot_free_space(struct inode *inode, qsize_t number)
|
1086 |
|
|
{
|
1087 |
|
|
unsigned int cnt;
|
1088 |
|
|
struct dquot *dquot;
|
1089 |
|
|
|
1090 |
|
|
/* NOBLOCK Start */
|
1091 |
|
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
1092 |
|
|
dquot = dqduplicate(inode->i_dquot[cnt]);
|
1093 |
|
|
if (dquot == NODQUOT)
|
1094 |
|
|
continue;
|
1095 |
|
|
dquot_decr_space(dquot, number);
|
1096 |
|
|
dqputduplicate(dquot);
|
1097 |
|
|
}
|
1098 |
|
|
inode_sub_bytes(inode, number);
|
1099 |
|
|
/* NOBLOCK End */
|
1100 |
|
|
}
|
1101 |
|
|
|
1102 |
|
|
/*
|
1103 |
|
|
* This is a non-blocking operation.
|
1104 |
|
|
*/
|
1105 |
|
|
void dquot_free_inode(const struct inode *inode, unsigned long number)
|
1106 |
|
|
{
|
1107 |
|
|
unsigned int cnt;
|
1108 |
|
|
struct dquot *dquot;
|
1109 |
|
|
|
1110 |
|
|
/* NOBLOCK Start */
|
1111 |
|
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
1112 |
|
|
dquot = dqduplicate(inode->i_dquot[cnt]);
|
1113 |
|
|
if (dquot == NODQUOT)
|
1114 |
|
|
continue;
|
1115 |
|
|
dquot_decr_inodes(dquot, number);
|
1116 |
|
|
dqputduplicate(dquot);
|
1117 |
|
|
}
|
1118 |
|
|
/* NOBLOCK End */
|
1119 |
|
|
}
|
1120 |
|
|
|
1121 |
|
|
/*
|
1122 |
|
|
* Transfer the number of inode and blocks from one diskquota to an other.
|
1123 |
|
|
*
|
1124 |
|
|
* This operation can block, but only after everything is updated
|
1125 |
|
|
*/
|
1126 |
|
|
int dquot_transfer(struct inode *inode, struct iattr *iattr)
|
1127 |
|
|
{
|
1128 |
|
|
qsize_t space;
|
1129 |
|
|
struct dquot *transfer_from[MAXQUOTAS];
|
1130 |
|
|
struct dquot *transfer_to[MAXQUOTAS];
|
1131 |
|
|
int cnt, ret = NO_QUOTA, chuid = (iattr->ia_valid & ATTR_UID) && inode->i_uid != iattr->ia_uid,
|
1132 |
|
|
chgid = (iattr->ia_valid & ATTR_GID) && inode->i_gid != iattr->ia_gid;
|
1133 |
|
|
char warntype[MAXQUOTAS];
|
1134 |
|
|
|
1135 |
|
|
/* Clear the arrays */
|
1136 |
|
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
1137 |
|
|
transfer_to[cnt] = transfer_from[cnt] = NODQUOT;
|
1138 |
|
|
warntype[cnt] = NOWARN;
|
1139 |
|
|
}
|
1140 |
|
|
/* First build the transfer_to list - here we can block on reading of dquots... */
|
1141 |
|
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
1142 |
|
|
if (!sb_has_quota_enabled(inode->i_sb, cnt))
|
1143 |
|
|
continue;
|
1144 |
|
|
switch (cnt) {
|
1145 |
|
|
case USRQUOTA:
|
1146 |
|
|
if (!chuid)
|
1147 |
|
|
continue;
|
1148 |
|
|
transfer_to[cnt] = dqget(inode->i_sb, iattr->ia_uid, cnt);
|
1149 |
|
|
break;
|
1150 |
|
|
case GRPQUOTA:
|
1151 |
|
|
if (!chgid)
|
1152 |
|
|
continue;
|
1153 |
|
|
transfer_to[cnt] = dqget(inode->i_sb, iattr->ia_gid, cnt);
|
1154 |
|
|
break;
|
1155 |
|
|
}
|
1156 |
|
|
}
|
1157 |
|
|
/* NOBLOCK START: From now on we shouldn't block */
|
1158 |
|
|
space = inode_get_bytes(inode);
|
1159 |
|
|
/* Build the transfer_from list and check the limits */
|
1160 |
|
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
1161 |
|
|
/* The second test can fail when quotaoff is in progress... */
|
1162 |
|
|
if (transfer_to[cnt] == NODQUOT || !sb_has_quota_enabled(inode->i_sb, cnt))
|
1163 |
|
|
continue;
|
1164 |
|
|
transfer_from[cnt] = dqduplicate(inode->i_dquot[cnt]);
|
1165 |
|
|
if (transfer_from[cnt] == NODQUOT) /* Can happen on quotafiles (quota isn't initialized on them)... */
|
1166 |
|
|
continue;
|
1167 |
|
|
if (check_idq(transfer_to[cnt], 1, warntype+cnt) == NO_QUOTA ||
|
1168 |
|
|
check_bdq(transfer_to[cnt], space, 0, warntype+cnt) == NO_QUOTA)
|
1169 |
|
|
goto warn_put_all;
|
1170 |
|
|
}
|
1171 |
|
|
|
1172 |
|
|
/*
|
1173 |
|
|
* Finally perform the needed transfer from transfer_from to transfer_to
|
1174 |
|
|
*/
|
1175 |
|
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
1176 |
|
|
/*
|
1177 |
|
|
* Skip changes for same uid or gid or for non-existing quota-type.
|
1178 |
|
|
*/
|
1179 |
|
|
if (transfer_from[cnt] == NODQUOT || transfer_to[cnt] == NODQUOT)
|
1180 |
|
|
continue;
|
1181 |
|
|
|
1182 |
|
|
dquot_decr_inodes(transfer_from[cnt], 1);
|
1183 |
|
|
dquot_decr_space(transfer_from[cnt], space);
|
1184 |
|
|
|
1185 |
|
|
dquot_incr_inodes(transfer_to[cnt], 1);
|
1186 |
|
|
dquot_incr_space(transfer_to[cnt], space);
|
1187 |
|
|
|
1188 |
|
|
if (inode->i_dquot[cnt] == NODQUOT)
|
1189 |
|
|
BUG();
|
1190 |
|
|
inode->i_dquot[cnt] = transfer_to[cnt];
|
1191 |
|
|
/*
|
1192 |
|
|
* We've got to release transfer_from[] twice - once for dquot_transfer() and
|
1193 |
|
|
* once for inode. We don't want to release transfer_to[] as it's now placed in inode
|
1194 |
|
|
*/
|
1195 |
|
|
transfer_to[cnt] = transfer_from[cnt];
|
1196 |
|
|
}
|
1197 |
|
|
/* NOBLOCK END. From now on we can block as we wish */
|
1198 |
|
|
ret = QUOTA_OK;
|
1199 |
|
|
warn_put_all:
|
1200 |
|
|
flush_warnings(transfer_to, warntype);
|
1201 |
|
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
1202 |
|
|
/* First we must put duplicate - otherwise we might deadlock */
|
1203 |
|
|
if (transfer_from[cnt] != NODQUOT)
|
1204 |
|
|
dqputduplicate(transfer_from[cnt]);
|
1205 |
|
|
if (transfer_to[cnt] != NODQUOT)
|
1206 |
|
|
dqput(transfer_to[cnt]);
|
1207 |
|
|
}
|
1208 |
|
|
return ret;
|
1209 |
|
|
}
|
1210 |
|
|
|
1211 |
|
|
/*
|
1212 |
|
|
* Definitions of diskquota operations.
|
1213 |
|
|
*/
|
1214 |
|
|
struct dquot_operations dquot_operations = {
|
1215 |
|
|
initialize: dquot_initialize, /* mandatory */
|
1216 |
|
|
drop: dquot_drop, /* mandatory */
|
1217 |
|
|
alloc_space: dquot_alloc_space,
|
1218 |
|
|
alloc_inode: dquot_alloc_inode,
|
1219 |
|
|
free_space: dquot_free_space,
|
1220 |
|
|
free_inode: dquot_free_inode,
|
1221 |
|
|
transfer: dquot_transfer,
|
1222 |
|
|
write_dquot: commit_dqblk
|
1223 |
|
|
};
|
1224 |
|
|
|
1225 |
|
|
/* Function used by filesystems for initializing the dquot_operations structure */
|
1226 |
|
|
void init_dquot_operations(struct dquot_operations *fsdqops)
|
1227 |
|
|
{
|
1228 |
|
|
memcpy(fsdqops, &dquot_operations, sizeof(dquot_operations));
|
1229 |
|
|
}
|
1230 |
|
|
|
1231 |
|
|
static inline void set_enable_flags(struct quota_info *dqopt, int type)
|
1232 |
|
|
{
|
1233 |
|
|
switch (type) {
|
1234 |
|
|
case USRQUOTA:
|
1235 |
|
|
dqopt->flags |= DQUOT_USR_ENABLED;
|
1236 |
|
|
break;
|
1237 |
|
|
case GRPQUOTA:
|
1238 |
|
|
dqopt->flags |= DQUOT_GRP_ENABLED;
|
1239 |
|
|
break;
|
1240 |
|
|
}
|
1241 |
|
|
}
|
1242 |
|
|
|
1243 |
|
|
static inline void reset_enable_flags(struct quota_info *dqopt, int type)
|
1244 |
|
|
{
|
1245 |
|
|
switch (type) {
|
1246 |
|
|
case USRQUOTA:
|
1247 |
|
|
dqopt->flags &= ~DQUOT_USR_ENABLED;
|
1248 |
|
|
break;
|
1249 |
|
|
case GRPQUOTA:
|
1250 |
|
|
dqopt->flags &= ~DQUOT_GRP_ENABLED;
|
1251 |
|
|
break;
|
1252 |
|
|
}
|
1253 |
|
|
}
|
1254 |
|
|
|
1255 |
|
|
/* Function in inode.c - remove pointers to dquots in icache */
|
1256 |
|
|
extern void remove_dquot_ref(struct super_block *, int);
|
1257 |
|
|
|
1258 |
|
|
/*
|
1259 |
|
|
* Turn quota off on a device. type == -1 ==> quotaoff for all types (umount)
|
1260 |
|
|
*/
|
1261 |
|
|
int vfs_quota_off(struct super_block *sb, int type)
|
1262 |
|
|
{
|
1263 |
|
|
int cnt;
|
1264 |
|
|
struct quota_info *dqopt = sb_dqopt(sb);
|
1265 |
|
|
|
1266 |
|
|
lock_kernel();
|
1267 |
|
|
if (!sb)
|
1268 |
|
|
goto out;
|
1269 |
|
|
|
1270 |
|
|
/* We need to serialize quota_off() for device */
|
1271 |
|
|
down(&dqopt->dqoff_sem);
|
1272 |
|
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
1273 |
|
|
if (type != -1 && cnt != type)
|
1274 |
|
|
continue;
|
1275 |
|
|
if (!is_enabled(dqopt, cnt))
|
1276 |
|
|
continue;
|
1277 |
|
|
reset_enable_flags(dqopt, cnt);
|
1278 |
|
|
|
1279 |
|
|
/* Note: these are blocking operations */
|
1280 |
|
|
remove_dquot_ref(sb, cnt);
|
1281 |
|
|
invalidate_dquots(sb, cnt);
|
1282 |
|
|
if (info_dirty(&dqopt->info[cnt]))
|
1283 |
|
|
dqopt->ops[cnt]->write_file_info(sb, cnt);
|
1284 |
|
|
if (dqopt->ops[cnt]->free_file_info)
|
1285 |
|
|
dqopt->ops[cnt]->free_file_info(sb, cnt);
|
1286 |
|
|
put_quota_format(dqopt->info[cnt].dqi_format);
|
1287 |
|
|
|
1288 |
|
|
fput(dqopt->files[cnt]);
|
1289 |
|
|
dqopt->files[cnt] = (struct file *)NULL;
|
1290 |
|
|
dqopt->info[cnt].dqi_flags = 0;
|
1291 |
|
|
dqopt->info[cnt].dqi_igrace = 0;
|
1292 |
|
|
dqopt->info[cnt].dqi_bgrace = 0;
|
1293 |
|
|
dqopt->ops[cnt] = NULL;
|
1294 |
|
|
}
|
1295 |
|
|
up(&dqopt->dqoff_sem);
|
1296 |
|
|
out:
|
1297 |
|
|
unlock_kernel();
|
1298 |
|
|
return 0;
|
1299 |
|
|
}
|
1300 |
|
|
|
1301 |
|
|
int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path)
|
1302 |
|
|
{
|
1303 |
|
|
struct file *f = NULL;
|
1304 |
|
|
struct inode *inode;
|
1305 |
|
|
struct quota_info *dqopt = sb_dqopt(sb);
|
1306 |
|
|
struct quota_format_type *fmt = find_quota_format(format_id);
|
1307 |
|
|
int error;
|
1308 |
|
|
|
1309 |
|
|
if (!fmt)
|
1310 |
|
|
return -ESRCH;
|
1311 |
|
|
if (is_enabled(dqopt, type)) {
|
1312 |
|
|
error = -EBUSY;
|
1313 |
|
|
goto out_fmt;
|
1314 |
|
|
}
|
1315 |
|
|
|
1316 |
|
|
down(&dqopt->dqoff_sem);
|
1317 |
|
|
|
1318 |
|
|
f = filp_open(path, O_RDWR, 0600);
|
1319 |
|
|
|
1320 |
|
|
error = PTR_ERR(f);
|
1321 |
|
|
if (IS_ERR(f))
|
1322 |
|
|
goto out_lock;
|
1323 |
|
|
dqopt->files[type] = f;
|
1324 |
|
|
error = -EIO;
|
1325 |
|
|
if (!f->f_op || !f->f_op->read || !f->f_op->write)
|
1326 |
|
|
goto out_f;
|
1327 |
|
|
inode = f->f_dentry->d_inode;
|
1328 |
|
|
error = -EACCES;
|
1329 |
|
|
if (!S_ISREG(inode->i_mode))
|
1330 |
|
|
goto out_f;
|
1331 |
|
|
error = -EINVAL;
|
1332 |
|
|
if (!fmt->qf_ops->check_quota_file(sb, type))
|
1333 |
|
|
goto out_f;
|
1334 |
|
|
/* We don't want quota and atime on quota files */
|
1335 |
|
|
dquot_drop(inode);
|
1336 |
|
|
inode->i_flags |= S_NOQUOTA | S_NOATIME;
|
1337 |
|
|
|
1338 |
|
|
dqopt->ops[type] = fmt->qf_ops;
|
1339 |
|
|
dqopt->info[type].dqi_format = fmt;
|
1340 |
|
|
if ((error = dqopt->ops[type]->read_file_info(sb, type)) < 0)
|
1341 |
|
|
goto out_f;
|
1342 |
|
|
set_enable_flags(dqopt, type);
|
1343 |
|
|
|
1344 |
|
|
add_dquot_ref(sb, type);
|
1345 |
|
|
|
1346 |
|
|
up(&dqopt->dqoff_sem);
|
1347 |
|
|
return 0;
|
1348 |
|
|
|
1349 |
|
|
out_f:
|
1350 |
|
|
if (f)
|
1351 |
|
|
filp_close(f, NULL);
|
1352 |
|
|
dqopt->files[type] = NULL;
|
1353 |
|
|
out_lock:
|
1354 |
|
|
up(&dqopt->dqoff_sem);
|
1355 |
|
|
out_fmt:
|
1356 |
|
|
put_quota_format(fmt);
|
1357 |
|
|
|
1358 |
|
|
return error;
|
1359 |
|
|
}
|
1360 |
|
|
|
1361 |
|
|
/* Generic routine for getting common part of quota structure */
|
1362 |
|
|
static void do_get_dqblk(struct dquot *dquot, struct if_dqblk *di)
|
1363 |
|
|
{
|
1364 |
|
|
struct mem_dqblk *dm = &dquot->dq_dqb;
|
1365 |
|
|
|
1366 |
|
|
di->dqb_bhardlimit = dm->dqb_bhardlimit;
|
1367 |
|
|
di->dqb_bsoftlimit = dm->dqb_bsoftlimit;
|
1368 |
|
|
di->dqb_curspace = dm->dqb_curspace;
|
1369 |
|
|
di->dqb_ihardlimit = dm->dqb_ihardlimit;
|
1370 |
|
|
di->dqb_isoftlimit = dm->dqb_isoftlimit;
|
1371 |
|
|
di->dqb_curinodes = dm->dqb_curinodes;
|
1372 |
|
|
di->dqb_btime = dm->dqb_btime;
|
1373 |
|
|
di->dqb_itime = dm->dqb_itime;
|
1374 |
|
|
di->dqb_valid = QIF_ALL;
|
1375 |
|
|
}
|
1376 |
|
|
|
1377 |
|
|
int vfs_get_dqblk(struct super_block *sb, int type, qid_t id, struct if_dqblk *di)
|
1378 |
|
|
{
|
1379 |
|
|
struct dquot *dquot = dqget(sb, id, type);
|
1380 |
|
|
|
1381 |
|
|
if (!dquot)
|
1382 |
|
|
return -EINVAL;
|
1383 |
|
|
do_get_dqblk(dquot, di);
|
1384 |
|
|
dqput(dquot);
|
1385 |
|
|
return 0;
|
1386 |
|
|
}
|
1387 |
|
|
|
1388 |
|
|
/* Generic routine for setting common part of quota structure */
|
1389 |
|
|
static void do_set_dqblk(struct dquot *dquot, struct if_dqblk *di)
|
1390 |
|
|
{
|
1391 |
|
|
struct mem_dqblk *dm = &dquot->dq_dqb;
|
1392 |
|
|
int check_blim = 0, check_ilim = 0;
|
1393 |
|
|
|
1394 |
|
|
if (di->dqb_valid & QIF_SPACE) {
|
1395 |
|
|
dm->dqb_curspace = di->dqb_curspace;
|
1396 |
|
|
check_blim = 1;
|
1397 |
|
|
}
|
1398 |
|
|
if (di->dqb_valid & QIF_BLIMITS) {
|
1399 |
|
|
dm->dqb_bsoftlimit = di->dqb_bsoftlimit;
|
1400 |
|
|
dm->dqb_bhardlimit = di->dqb_bhardlimit;
|
1401 |
|
|
check_blim = 1;
|
1402 |
|
|
}
|
1403 |
|
|
if (di->dqb_valid & QIF_INODES) {
|
1404 |
|
|
dm->dqb_curinodes = di->dqb_curinodes;
|
1405 |
|
|
check_ilim = 1;
|
1406 |
|
|
}
|
1407 |
|
|
if (di->dqb_valid & QIF_ILIMITS) {
|
1408 |
|
|
dm->dqb_isoftlimit = di->dqb_isoftlimit;
|
1409 |
|
|
dm->dqb_ihardlimit = di->dqb_ihardlimit;
|
1410 |
|
|
check_ilim = 1;
|
1411 |
|
|
}
|
1412 |
|
|
if (di->dqb_valid & QIF_BTIME)
|
1413 |
|
|
dm->dqb_btime = di->dqb_btime;
|
1414 |
|
|
if (di->dqb_valid & QIF_ITIME)
|
1415 |
|
|
dm->dqb_itime = di->dqb_itime;
|
1416 |
|
|
|
1417 |
|
|
if (check_blim) {
|
1418 |
|
|
if (!dm->dqb_bsoftlimit || toqb(dm->dqb_curspace) < dm->dqb_bsoftlimit) {
|
1419 |
|
|
dm->dqb_btime = 0;
|
1420 |
|
|
dquot->dq_flags &= ~DQ_BLKS;
|
1421 |
|
|
}
|
1422 |
|
|
else if (!(di->dqb_valid & QIF_BTIME)) /* Set grace only if user hasn't provided his own... */
|
1423 |
|
|
dm->dqb_btime = CURRENT_TIME + sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_bgrace;
|
1424 |
|
|
}
|
1425 |
|
|
if (check_ilim) {
|
1426 |
|
|
if (!dm->dqb_isoftlimit || dm->dqb_curinodes < dm->dqb_isoftlimit) {
|
1427 |
|
|
dm->dqb_itime = 0;
|
1428 |
|
|
dquot->dq_flags &= ~DQ_INODES;
|
1429 |
|
|
}
|
1430 |
|
|
else if (!(di->dqb_valid & QIF_ITIME)) /* Set grace only if user hasn't provided his own... */
|
1431 |
|
|
dm->dqb_itime = CURRENT_TIME + sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_igrace;
|
1432 |
|
|
}
|
1433 |
|
|
if (dm->dqb_bhardlimit || dm->dqb_bsoftlimit || dm->dqb_ihardlimit || dm->dqb_isoftlimit)
|
1434 |
|
|
dquot->dq_flags &= ~DQ_FAKE;
|
1435 |
|
|
else
|
1436 |
|
|
dquot->dq_flags |= DQ_FAKE;
|
1437 |
|
|
dquot->dq_flags |= DQ_MOD;
|
1438 |
|
|
}
|
1439 |
|
|
|
1440 |
|
|
int vfs_set_dqblk(struct super_block *sb, int type, qid_t id, struct if_dqblk *di)
|
1441 |
|
|
{
|
1442 |
|
|
struct dquot *dquot = dqget(sb, id, type);
|
1443 |
|
|
|
1444 |
|
|
if (!dquot)
|
1445 |
|
|
return -EINVAL;
|
1446 |
|
|
do_set_dqblk(dquot, di);
|
1447 |
|
|
dqput(dquot);
|
1448 |
|
|
return 0;
|
1449 |
|
|
}
|
1450 |
|
|
|
1451 |
|
|
/* Generic routine for getting common part of quota file information */
|
1452 |
|
|
int vfs_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii)
|
1453 |
|
|
{
|
1454 |
|
|
struct mem_dqinfo *mi = sb_dqopt(sb)->info + type;
|
1455 |
|
|
|
1456 |
|
|
ii->dqi_bgrace = mi->dqi_bgrace;
|
1457 |
|
|
ii->dqi_igrace = mi->dqi_igrace;
|
1458 |
|
|
ii->dqi_flags = mi->dqi_flags & DQF_MASK;
|
1459 |
|
|
ii->dqi_valid = IIF_ALL;
|
1460 |
|
|
return 0;
|
1461 |
|
|
}
|
1462 |
|
|
|
1463 |
|
|
/* Generic routine for setting common part of quota file information */
|
1464 |
|
|
int vfs_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii)
|
1465 |
|
|
{
|
1466 |
|
|
struct mem_dqinfo *mi = sb_dqopt(sb)->info + type;
|
1467 |
|
|
|
1468 |
|
|
if (ii->dqi_valid & IIF_BGRACE)
|
1469 |
|
|
mi->dqi_bgrace = ii->dqi_bgrace;
|
1470 |
|
|
if (ii->dqi_valid & IIF_IGRACE)
|
1471 |
|
|
mi->dqi_igrace = ii->dqi_igrace;
|
1472 |
|
|
if (ii->dqi_valid & IIF_FLAGS)
|
1473 |
|
|
mi->dqi_flags = (mi->dqi_flags & ~DQF_MASK) | (ii->dqi_flags & DQF_MASK);
|
1474 |
|
|
mark_info_dirty(mi);
|
1475 |
|
|
return 0;
|
1476 |
|
|
}
|
1477 |
|
|
|
1478 |
|
|
struct quotactl_ops vfs_quotactl_ops = {
|
1479 |
|
|
quota_on: vfs_quota_on,
|
1480 |
|
|
quota_off: vfs_quota_off,
|
1481 |
|
|
quota_sync: vfs_quota_sync,
|
1482 |
|
|
get_info: vfs_get_dqinfo,
|
1483 |
|
|
set_info: vfs_set_dqinfo,
|
1484 |
|
|
get_dqblk: vfs_get_dqblk,
|
1485 |
|
|
set_dqblk: vfs_set_dqblk
|
1486 |
|
|
};
|
1487 |
|
|
|
1488 |
|
|
static ctl_table fs_dqstats_table[] = {
|
1489 |
|
|
{FS_DQ_LOOKUPS, "lookups", &dqstats.lookups, sizeof(int), 0444, NULL, &proc_dointvec},
|
1490 |
|
|
{FS_DQ_DROPS, "drops", &dqstats.drops, sizeof(int), 0444, NULL, &proc_dointvec},
|
1491 |
|
|
{FS_DQ_READS, "reads", &dqstats.reads, sizeof(int), 0444, NULL, &proc_dointvec},
|
1492 |
|
|
{FS_DQ_WRITES, "writes", &dqstats.writes, sizeof(int), 0444, NULL, &proc_dointvec},
|
1493 |
|
|
{FS_DQ_CACHE_HITS, "cache_hits", &dqstats.cache_hits, sizeof(int), 0444, NULL, &proc_dointvec},
|
1494 |
|
|
{FS_DQ_ALLOCATED, "allocated_dquots", &dqstats.allocated_dquots, sizeof(int), 0444, NULL, &proc_dointvec},
|
1495 |
|
|
{FS_DQ_FREE, "free_dquots", &dqstats.free_dquots, sizeof(int), 0444, NULL, &proc_dointvec},
|
1496 |
|
|
{FS_DQ_SYNCS, "syncs", &dqstats.syncs, sizeof(int), 0444, NULL, &proc_dointvec},
|
1497 |
|
|
{},
|
1498 |
|
|
};
|
1499 |
|
|
|
1500 |
|
|
static ctl_table fs_table[] = {
|
1501 |
|
|
{FS_DQSTATS, "quota", NULL, 0, 0555, fs_dqstats_table},
|
1502 |
|
|
{},
|
1503 |
|
|
};
|
1504 |
|
|
|
1505 |
|
|
static ctl_table sys_table[] = {
|
1506 |
|
|
{CTL_FS, "fs", NULL, 0, 0555, fs_table},
|
1507 |
|
|
{},
|
1508 |
|
|
};
|
1509 |
|
|
|
1510 |
|
|
static int __init dquot_init(void)
|
1511 |
|
|
{
|
1512 |
|
|
int i;
|
1513 |
|
|
|
1514 |
|
|
register_sysctl_table(sys_table, 0);
|
1515 |
|
|
for (i = 0; i < NR_DQHASH; i++)
|
1516 |
|
|
INIT_LIST_HEAD(dquot_hash + i);
|
1517 |
|
|
printk(KERN_NOTICE "VFS: Disk quotas v%s\n", __DQUOT_VERSION__);
|
1518 |
|
|
|
1519 |
|
|
return 0;
|
1520 |
|
|
}
|
1521 |
|
|
__initcall(dquot_init);
|
1522 |
|
|
|
1523 |
|
|
EXPORT_SYMBOL(register_quota_format);
|
1524 |
|
|
EXPORT_SYMBOL(unregister_quota_format);
|
1525 |
|
|
EXPORT_SYMBOL(dqstats);
|
1526 |
|
|
EXPORT_SYMBOL(init_dquot_operations);
|