*** old/0.95c+/linux/fs/Makefile	Mon Apr  6 18:45:50 1992
--- linux/fs/Makefile	Wed Apr 15 21:33:38 1992
***************
*** 24,30 ****
  
  OBJS=	open.o read_write.o inode.o file_table.o buffer.o super.o \
  	block_dev.o char_dev.o stat.o exec.o pipe.o namei.o \
! 	fcntl.o ioctl.o select.o
  
  fs.o: $(OBJS)
  	$(LD) -r -o fs.o $(OBJS)
--- 24,30 ----
  
  OBJS=	open.o read_write.o inode.o file_table.o buffer.o super.o \
  	block_dev.o char_dev.o stat.o exec.o pipe.o namei.o \
! 	fcntl.o ioctl.o select.o quota.o
  
  fs.o: $(OBJS)
  	$(LD) -r -o fs.o $(OBJS)
*** old/0.95c+/linux/fs/buffer.c	Mon Apr  6 18:41:40 1992
--- linux/fs/buffer.c	Fri Apr 24 17:31:08 1992
***************
*** 65,70 ****
--- 65,71 ----
  int sys_sync(void)
  {
  	sync_inodes();		/* write out inodes into buffers */
+ 	sync_quota();
  	sync_buffers(0);
  	return 0;
  }
*** old/0.95c+/linux/fs/open.c	Mon Apr  6 18:41:40 1992
--- linux/fs/open.c	Sat Apr 25 14:55:05 1992
***************
*** 127,138 ****
  {
  	struct inode * inode;
  
  	if (!(inode=namei(filename)))
  		return -ENOENT;
! 	if (!suser()) {
! 		iput(inode);
! 		return -EACCES;
  	}
  	inode->i_uid=uid;
  	inode->i_gid=gid;
  	inode->i_dirt=1;
--- 127,157 ----
  {
  	struct inode * inode;
  
+ 	if (!suser())
+ 	        return -EACCES;
+ 
  	if (!(inode=namei(filename)))
  		return -ENOENT;
! 
! #ifdef USE_QUOTA
! 	if (uid) {
! 	  if ((add_inode(uid,inode->i_dev)) == -1) {
! 	    iput(inode);
! 	    return -EDQUOT;
! 	  }
! 	    
! 	  if (inode->i_size)
! 	    if ((add_block(uid,(inode->i_size/1024)+1,inode->i_dev)) == -1) {
! 	      iput(inode);
! 	      return -EDQUOT;
! 	    }
  	}
+ 
+ 	if (inode->i_uid)
+ 	  remove_quota(inode->i_uid, inode->i_size?((inode->i_size/1024)+1):0,
+ 		       inode->i_dev);
+ 
+ #endif
  	inode->i_uid=uid;
  	inode->i_gid=gid;
  	inode->i_dirt=1;
*** old/0.95c+/linux/fs/super.c	Mon Apr  6 18:41:41 1992
--- linux/fs/super.c	Mon Apr 27 10:06:14 1992
***************
*** 160,165 ****
--- 160,168 ----
  		return -ENOENT;
  	if (!sb->s_covered->i_mount)
  		printk("Mounted inode has i_mount=0\n");
+ #ifdef USE_QUOTA
+ 	quota_umount(dev);
+ #endif
  	for (inode = inode_table+0 ; inode < inode_table+NR_INODE ; inode++)
  		if (inode->i_dev==dev && inode->i_count)
  			if (inode == sb->s_mounted && inode->i_count == 1)
*** old/0.95c+/linux/fs/minix/bitmap.c	Thu Mar  5 18:36:44 1992
--- linux/fs/minix/bitmap.c	Fri Apr 24 19:57:28 1992
***************
*** 10,15 ****
--- 10,16 ----
  #include <linux/sched.h>
  #include <linux/minix_fs.h>
  #include <linux/kernel.h>
+ #include <errno.h>
  
  #define clear_block(addr) \
  __asm__("cld\n\t" \
***************
*** 83,88 ****
--- 84,94 ----
  
  	if (!(sb = get_super(dev)))
  		panic("trying to get new block from nonexistant device");
+ #ifdef USE_QUOTA
+ 	if (current->euid)
+ 	  if ((add_block(current->euid,1,dev)) == -1)
+             return 0;
+ #endif
  	j = 8192;
  	for (i=0 ; i<8 ; i++)
  		if (bh=sb->s_zmap[i])
*** old/0.95c+/linux/fs/minix/truncate.c	Sat Mar 14 17:42:50 1992
--- linux/fs/minix/truncate.c	Sat Apr 25 11:28:28 1992
***************
*** 21,26 ****
--- 21,30 ----
  
  	if (!block)
  		return 1;
+ #ifdef USE_QUOTA
+ 	if (current->euid)
+ 	  remove_block(current->euid,1,dev);
+ #endif
  	block_busy = 0;
  	if (bh=bread(dev,block)) {
  		p = (unsigned short *) bh->b_data;
***************
*** 48,53 ****
--- 52,61 ----
  
  	if (!block)
  		return 1;
+ #ifdef USE_QUOTA
+ 	if (current->euid)
+ 	  remove_block(current->euid,1,dev);
+ #endif
  	block_busy = 0;
  	if (bh=bread(dev,block)) {
  		p = (unsigned short *) bh->b_data;
***************
*** 97,102 ****
--- 105,114 ----
  		schedule();
  		goto repeat;
  	}
+ #ifdef USE_QUOTA
+ 	if (inode->i_uid && inode->i_size)
+ 	  remove_block(inode->i_uid,(inode->i_size/1024)+1,inode->i_dev);
+ #endif
  	inode->i_size = 0;
  	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
  }
*** old/0.95c+/linux/fs/minix/namei.c	Mon Apr  6 18:41:42 1992
--- linux/fs/minix/namei.c	Sat Apr 25 16:23:35 1992
***************
*** 244,249 ****
--- 244,254 ----
  	*result = NULL;
  	if (!dir)
  		return -ENOENT;
+ #ifdef USE_QUOTA
+ 	if (current->euid)
+ 	  if ((add_inode(current->euid, inode->i_dev)) == -1)
+ 	    return -EDQUOT;
+ #endif
  	inode = minix_new_inode(dir->i_dev);
  	if (!inode) {
  		iput(dir);
***************
*** 311,316 ****
--- 316,327 ----
  	struct inode * inode;
  	struct buffer_head * bh, *dir_block;
  	struct minix_dir_entry * de;
+ 
+ #ifdef USE_QUOTA
+ 	if (inode->i_uid)
+ 	  if ((add_inode(inode->i_uid, inode->i_dev)) == -1)
+ 	    return -EDQUOT;
+ #endif
  	
  	bh = minix_find_entry(dir,name,len,&de);
  	if (bh) {
***************
*** 458,463 ****
--- 469,478 ----
  	dir->i_ctime = dir->i_mtime = CURRENT_TIME;
  	dir->i_dirt=1;
  	retval = 0;
+ #ifdef USE_QUOTA
+ 	if (inode->i_uid)
+ 	  remove_inode(inode->i_uid,inode->i_dev);
+ #endif
  end_rmdir:
  	iput(dir);
  	iput(inode);
***************
*** 497,502 ****
--- 512,523 ----
  	inode->i_dirt = 1;
  	inode->i_ctime = CURRENT_TIME;
  	retval = 0;
+ 
+ #ifdef USE_QUOTA
+ /* No need to remove the file blocks here. This is done in the truncate code */
+ 	if (inode->i_uid && !inode->i_nlink)
+ 	  remove_inode(inode->i_uid,inode->i_dev);
+ #endif
  end_unlink:
  	brelse(bh);
  	iput(inode);
*** old/0.95c+/linux/init/main.c	Mon Apr  6 21:28:53 1992
--- linux/init/main.c	Wed Apr 22 20:38:20 1992
***************
*** 164,169 ****
--- 164,172 ----
  	buffer_init(buffer_memory_end);
  	hd_init();
  	floppy_init();
+ #ifdef USE_QUOTA
+ 	quota_init();
+ #endif
  	sti();
  	move_to_user_mode();
  	if (!fork()) {		/* we count on this going ok */
*** old/0.95c+/linux/include/sys/types.h	Fri Feb 28 21:34:59 1992
--- linux/include/sys/types.h	Wed Apr 22 17:20:43 1992
***************
*** 32,37 ****
--- 32,38 ----
  typedef long off_t;
  typedef unsigned char u_char;
  typedef unsigned short ushort;
+ typedef unsigned short u_short;
  typedef char *caddr_t;
  
  typedef unsigned char cc_t;
*** old/0.95c+/linux/include/unistd.h	Mon Apr  6 18:41:52 1992
--- linux/include/unistd.h	Sat Apr 25 17:19:34 1992
***************
*** 155,160 ****
--- 155,164 ----
  #define __NR_swapon	87
  #define __NR_reboot	88
  #define __NR_readdir	89
+ #ifdef USE_QUOTA
+ #define __NR_quota      90
+ #define __NR_setquota   91
+ #endif
  
  /* XXX - _foo needs to be __foo, while __NR_bar could be _NR_bar. */
  #define _syscall0(type,name) \
***************
*** 299,302 ****
--- 303,310 ----
  int select(int width, fd_set * readfds, fd_set * writefds,
  	fd_set * exceptfds, struct timeval * timeout);
  int swapon(const char * specialfile);
+ #ifdef QUOTA
+ int setquota(const char *, const char *);
+ int quota(int, const char *, uid_t, const char *);
+ #endif
  #endif
*** old/0.95c+/linux/include/errno.h	Tue Feb 18 20:36:19 1992
--- linux/include/errno.h	Sat Apr 25 15:55:54 1992
***************
*** 57,62 ****
--- 57,63 ----
  #define ENOSYS		38
  #define ENOTEMPTY	39
  #define ELOOP		40
+ #define EDQUOT          41
  
  /* Should never be seen by user programs */
  #define ERESTARTSYS	512
*** old/0.95c+/linux/include/linux/sys.h	Mon Apr  6 18:41:54 1992
--- linux/include/linux/sys.h	Sat Apr 25 17:17:17 1992
***************
*** 92,97 ****
--- 92,101 ----
  extern int sys_swapon();
  extern int sys_reboot();
  extern int sys_readdir();
+ #ifdef USE_QUOTA
+ extern int sys_quota();
+ extern int sys_setquota();
+ #endif
  
  fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read,
  sys_write, sys_open, sys_close, sys_waitpid, sys_creat, sys_link,
***************
*** 108,114 ****
  sys_setreuid,sys_setregid, sys_sigsuspend, sys_sigpending, sys_sethostname,
  sys_setrlimit, sys_getrlimit, sys_getrusage, sys_gettimeofday, 
  sys_settimeofday, sys_getgroups, sys_setgroups, sys_select, sys_symlink,
! sys_lstat, sys_readlink, sys_uselib, sys_swapon, sys_reboot, sys_readdir };
  
  /* So we don't have to do any more manual updating.... */
  int NR_syscalls = sizeof(sys_call_table)/sizeof(fn_ptr);
--- 112,122 ----
  sys_setreuid,sys_setregid, sys_sigsuspend, sys_sigpending, sys_sethostname,
  sys_setrlimit, sys_getrlimit, sys_getrusage, sys_gettimeofday, 
  sys_settimeofday, sys_getgroups, sys_setgroups, sys_select, sys_symlink,
! sys_lstat, sys_readlink, sys_uselib, sys_swapon, sys_reboot, sys_readdir
! #ifdef USE_QUOTA
! , sys_quota, sys_setquota 
! #endif
! };
  
  /* So we don't have to do any more manual updating.... */
  int NR_syscalls = sizeof(sys_call_table)/sizeof(fn_ptr);
*** old/0.95c+/linux/include/linux/fs.h	Mon Apr  6 18:41:55 1992
--- linux/include/linux/fs.h	Sat Apr 25 17:28:59 1992
***************
*** 230,233 ****
--- 230,244 ----
  extern int char_write(struct inode *, struct file *, char *, int);
  extern int block_write(struct inode *, struct file *, char *, int);
  
+ #ifdef USE_QUOTA
+ extern int add_inode(uid_t user, dev_t dev);
+ extern int add_block(uid_t user, int cnt, dev_t dev);
+ extern void quota_umount(dev_t dev);
+ extern void sync_quota();
+ extern void quota_init();
+ extern void remove_quota(uid_t user, int cnt, dev_t dev);
+ extern void remove_block(uid_t user, int cnt, dev_t dev);
+ extern void remove_inode(uid_t user, dev_t dev);
+ #endif
+ 
  #endif
*** /dev/null	Fri Jan 17 14:51:36 1992
--- linux/fs/quota.c	Mon Apr 27 11:33:02 1992
***************
*** 0 ****
--- 1,717 ----
+  /* quota - simple implementation of a quota mechanism for linux 0.95c
+ 
+    Edvard Tuinder  <v892231@si.hhs.nl>
+ 
+    $Id: quota.c,v 1.4 1992/04/25 16:38:41 ettin Exp ettin $
+ 
+  */
+ 
+ #ifdef USE_QUOTA
+ #include <linux/sched.h>
+ #include <linux/fs.h>
+ #include <sys/types.h>
+ #include <linux/quota.h>
+ #include <asm/segment.h>
+ #include <fcntl.h>
+ #include <errno.h>
+ 
+ #define NR(dev)        MAJOR(dev), MINOR(dev)
+ #define MIN(a,b)       (((a)<(b))?(a):(b)) 
+ #define LAST_USED(dev,uid) ((dev) == last_dev_done && (uid == last_uid_done))
+ #define LAST_DEV(dev)      ((dev) == last_dev_done)
+ #define LAST_UID(dev,uid)  ((dev) == last_q_d_used && (uid == last_uid_done))
+ #define NEW_USER(dev,uid)  (quotas[dev].user[uid].dirty == 2)
+ 
+ 
+ void remove_quota(uid_t, int, dev_t);
+ void remove_inode(uid_t, dev_t);
+ void remove_block(uid_t, int, dev_t);
+ int add_inode(uid_t, dev_t);
+ int add_block(uid_t, int, dev_t);
+ void quota_umount(dev_t dev);
+ void sync_quota();
+ 
+ /* local functions */
+ static inline void copy_devname(char *, char *);
+ static inline void quota_write(u_short, u_short);
+ static int unset_quota(const char *);
+ static int set_quota_user(int, const char *, uid_t, struct copy_quota_block *);
+ static int set_quota(const char *, const char *);
+ static int get_quota(const char *, uid_t, struct copy_quota_block *);
+ static short find_user(uid_t user, u_short);
+ static int store_block(struct buffer_head *, int, dev_t, short);
+ static int write_block(struct buffer_head *, int, u_short, u_short);
+ static short find_dev(dev_t dev);
+ static short find_free_dev(void);
+ 
+ static struct quota_block quotas[NR_SUPER];
+ static u_char no_quotas_set=0;
+ static u_char sync_counter=0;
+ 
+ /* these are for speedup purposes */
+ static inline void find_info(dev_t, uid_t, short *, short *);
+ static uid_t last_uid_done;
+ static dev_t last_dev_done;
+ static u_short last_q_d_used;
+ static u_short last_q_u_used;
+ 
+ static inline void copy_devname(char *from, char *to)
+ {
+   short i;
+ 
+   verify_area(to, Q_MAX_DEV_NAME);
+   for (i=0; i < Q_MAX_DEV_NAME && *from;i++) {
+     put_fs_byte(*from,to);
+     from++;
+     to++;
+   }
+ }
+ 
+ static short find_dev(dev_t dev)
+ {
+   u_short l;
+ 
+   if (LAST_DEV(dev))
+     return last_q_d_used;
+ 
+   for (l=0;l<NR_SUPER;) {
+     if (quotas[l].dev == dev)
+       return l;
+     else
+       l++;
+   }
+   return -1;
+ }
+ 
+ static short find_user(uid_t user, u_short q_d)
+ {
+   u_short l;
+ 
+   if (!user)			/* no quota for root */
+     return -1;
+ 
+ /* very rarely used, but anyway */
+   if (LAST_UID(q_d,user))
+     return last_q_u_used;
+ 
+   for (l=0;l<Q_RESIDENT;) {
+     if (quotas[q_d].user[l].q_user == user)
+       return l;
+     else
+       l++;
+   }
+ 
+   return -1;
+ }
+ 
+ static inline void find_info(dev_t dev, uid_t user, short *q_d, short *q_u)
+ {
+   if (!LAST_USED(dev,user)) {
+     if ((*q_d = find_dev(dev)) == -1)
+       return;
+     if ((*q_u = find_user(user, *q_d)) == -1)
+       return;
+   } else {
+     *q_u = last_q_u_used;
+     *q_d = last_q_d_used;
+   }
+ 
+ /* keep info on last user */
+   last_uid_done = user;
+   last_dev_done = dev;
+   last_q_d_used = *q_d;
+   last_q_u_used = *q_u;
+ }
+ 
+ 
+ void remove_quota(uid_t user, int nr_blks, dev_t dev)
+ {
+   remove_inode(user, dev);
+   if (nr_blks)
+     remove_block(user, nr_blks, dev);
+ }
+ 
+ static inline void quota_init_dev(short q_d)
+ {
+   short l;
+ 
+   for (l=0;l<Q_RESIDENT;l++)
+     quotas[q_d].user[l].q_user = 0;
+ }
+ 
+ static short find_free_dev(void)
+ {
+   u_short l;
+ 
+   for (l=0;l<NR_SUPER;) {
+     if (quotas[l].dev == 0) {
+       quota_init_dev(l);
+       return l;
+     }
+     else 
+       l++;
+   }
+ 
+   return -1;
+ }
+ 
+ static short find_free_user(short q_d)
+ {
+   u_short l;
+ 
+   for (l=0;l<Q_RESIDENT;) {
+     if (quotas[q_d].user[l].q_user == 0) {
+       return l;
+     }
+     else
+       l++;
+   }
+ 
+   return -1;
+ }
+ 
+ 
+ int add_inode(uid_t user, dev_t dev)
+ {
+   short q_u, q_d;
+   
+   if (!no_quotas_set)
+     return 0;
+ 
+   find_info(dev, user, &q_d, &q_u);
+   if (q_d == -1)
+     return 0;
+   if (q_u = -1) {
+ #ifdef STRICT_QUOTA
+     if (user < Q_NO_QUOTA)
+       return 0;
+     else {
+       printk("FILE LIMIT REACHED. FILESYSTEM (%s) FULL\n",quotas[q_d].dev_name);
+       return -1;
+     }
+ #else
+     return 0;
+ #endif
+   }
+ 
+   if (!quotas[q_d].set_on)
+     return 0;
+ 
+   if (quotas[q_d].user[q_u].ino_used >= quotas[q_d].user[q_u].ino_hard ||
+       (((quotas[q_d].user[q_u].ino_time+MAX_WAIT_TIME) <= CURRENT_TIME) &&
+ 	(quotas[q_d].user[q_u].ino_time) && 
+ 	(quotas[q_d].user[q_u].ino_used > quotas[q_d].user[q_u].ino_soft)) ||
+       (quotas[q_d].user[q_u].ino_used+1 >= quotas[q_d].user[q_u].ino_hard)) {
+     printk("FILE LIMIT REACHED. FILESYSTEM (%s) FULL\n",quotas[q_d].dev_name);
+     return -1;
+   }
+   quotas[q_d].user[q_u].dirty = 1;
+   quotas[q_d].user[q_u].ino_used ++;
+   if (quotas[q_d].user[q_u].ino_used > quotas[q_d].user[q_u].ino_soft) {
+     if (!quotas[q_d].user[q_u].ino_time)
+       printk("WARNING: FILE QOUTA EXCEEDED ON %s\n", quotas[q_d].dev_name);
+     quotas[q_d].user[q_u].ino_time = CURRENT_TIME;
+   }
+   return 0;
+ }
+ 
+ void remove_inode(uid_t user, dev_t dev)
+ {
+   short q_u, q_d;
+ 
+   if (!no_quotas_set)
+     return;
+ 
+   find_info(dev, user, &q_d, &q_u);
+   if (q_d == -1)
+       return;
+   if (q_u == -1)
+       return;
+ 
+   if (!quotas[q_d].set_on)
+     return;
+ 
+   quotas[q_d].user[q_u].dirty = 1;
+   if (quotas[q_d].user[q_u].ino_used)
+     quotas[q_d].user[q_u].ino_used--;
+   if (quotas[q_d].user[q_u].ino_time && (quotas[q_d].user[q_u].blk_used < quotas[q_d].user[q_u].ino_soft))
+     quotas[q_d].user[q_u].ino_time = 0L;
+ }
+ 
+ static inline void quota_write(u_short q_d, u_short q_u)
+ {
+   static struct buffer_head *bh;
+   int nr;
+   int ret;
+   off_t f_pos=0;
+ 
+   if (nr = quotas[q_d].inode->i_op->bmap(quotas[q_d].inode,(f_pos)>>BLOCK_SIZE_BITS))
+     if (!(bh = bread(quotas[q_d].dev,nr)))
+ 	return;
+ 
+   nr = f_pos & (BLOCK_SIZE-1);
+   if (quotas[q_d].user[q_u].dirty == 1) {
+     while (f_pos < BLOCK_SIZE && *(bh->b_data+f_pos)) {
+       ret = write_block(bh, f_pos, q_d, q_u);
+       f_pos += Q_BLOCK_SIZE;
+       if (ret) {
+ 	bh->b_dirt=1;		/* mark dirty */
+ 	brelse(bh);
+ 	return;
+       }
+     }
+   } else {
+     while (f_pos < BLOCK_SIZE && *(bh->b_data+f_pos))
+       f_pos += Q_BLOCK_SIZE;
+     (void) write_block(bh, f_pos, q_d, q_u);
+     bh->b_dirt=1;
+ /* Next line is rather dirty... */
+     quotas[q_d].inode->i_size += Q_BLOCK_SIZE;
+ 
+ /* Update inode info. Need to adjust all time fields */
+     quotas[q_d].inode->i_atime = quotas[q_d].inode->i_mtime = 
+       quotas[q_d].inode->i_ctime = CURRENT_TIME;
+     quotas[q_d].inode->i_count++; /* not realy release the inode */
+     iput(quotas[q_d].inode);
+ 
+   }
+ 
+   brelse(bh);
+   
+   return;
+ }
+ 
+ int add_block(uid_t user, int nr_blk, dev_t dev)
+ {
+   short q_u, q_d;
+ 
+   if (!no_quotas_set)
+     return 0;
+ 
+   find_info(dev, user, &q_d, &q_u);
+   if (q_d == -1)
+     return 0;
+   if (q_u = -1) {
+ #ifdef STRICT_QUOTA
+     if (user < Q_NO_QUOTA)
+       return 0;
+     else {
+       printk("BLOCK LIMIT REACHED. FILESYSTEM (%s) FULL\n",quotas[q_d].dev_name);
+       return -1;
+     }
+ #else
+     return 0;
+ #endif
+   }
+   
+   if (!quotas[q_d].set_on)
+     return 0;
+ 
+   if (quotas[q_d].user[q_u].blk_used >= quotas[q_d].user[q_u].blk_hard ||
+       (((quotas[q_d].user[q_u].blk_time+MAX_WAIT_TIME) <= CURRENT_TIME)
+        && (quotas[q_d].user[q_u].blk_time) &&
+        (quotas[q_d].user[q_u].blk_used > quotas[q_d].user[q_u].blk_soft)) ||
+       (quotas[q_d].user[q_u].blk_used+nr_blk >= quotas[q_d].user[q_u].blk_hard)) {
+     printk("BLOCK LIMIT REACHED. FILESYSTEM (%s) FULL\n",quotas[q_d].dev_name);
+     return -1;
+   }
+   quotas[q_d].user[q_u].dirty = 1;
+   quotas[q_d].user[q_u].blk_used += nr_blk;
+   if (quotas[q_d].user[q_u].blk_used > quotas[q_d].user[q_u].blk_soft) {
+     if (!quotas[q_d].user[q_u].blk_time)
+       printk("WARNING: BLOCK QUOTA EXCEEDED on %s\n",quotas[q_d].dev_name);
+     quotas[q_d].user[q_u].blk_time = CURRENT_TIME;
+   }
+ 
+ #ifdef QUOTA_VERY_VERBOSE
+   printk("%s: used %d limit %d\n",quotas[q_d].dev_name,quotas[q_d].user[q_u].blk_used,quotas[q_d].user[q_u].blk_hard);
+ #endif
+ 
+   return 0;
+ }
+ 
+ void remove_block(uid_t user, int nr_blk, dev_t dev)
+ {
+   short q_u, q_d;
+   
+   if (!no_quotas_set)
+     return;
+ 
+   find_info(dev, user, &q_d, &q_u);
+   if (q_d == -1)
+       return;
+   if (q_u == -1)
+       return;
+ 
+   if (!quotas[q_d].set_on)
+     return;
+ 
+   if (((long)quotas[q_d].user[q_u].blk_used - nr_blk) >= 0)
+     quotas[q_d].user[q_u].blk_used -= nr_blk;
+   else
+     quotas[q_d].user[q_u].blk_used = 0;
+   if (quotas[q_d].user[q_u].blk_time && 
+       (quotas[q_d].user[q_u].blk_used < quotas[q_d].user[q_u].blk_soft))
+     quotas[q_d].user[q_u].blk_time = 0L;
+ 
+ #ifdef QUOTA_VERY_VERBOSE
+   printk("%s: used %d limit %d\n",quotas[q_d].dev_name,quotas[q_d].user[q_u].blk_used,quotas[q_d].user[q_u].blk_hard);
+ #endif
+ }
+ 
+ void quota_umount(dev_t dev)
+ {
+   short q_d, q_u;
+ 
+   find_info(dev, (uid_t)0, &q_d, &q_u);
+   if (q_d == -1)
+     return;
+ 
+   sync_quota();
+   for (q_u = 0;q_u<Q_RESIDENT;q_u++)
+     quotas[q_d].user[q_u].q_user = 0;
+   quotas[q_d].set_on = 0;
+   if (quotas[q_d].inode->i_count != 1)
+     iput(quotas[q_d].inode);	/* release inode.  */
+   no_quotas_set--;		/* one less */
+ }
+   
+ static int store_block(struct buffer_head *bh, int read, dev_t dev,
+ 			     short q_d)
+ {
+   struct disk_quota_block *qb;
+   short q_u;
+ 
+   qb = (struct disk_quota_block *)((bh->b_data)+read);
+ 
+   if ((q_u = find_user(qb->user, q_d)) != -1) {
+     /* User already has a quota on this file-system. Not fatal, just inform */
+     printk("Trying to add user %d again to quota-structure for device %d/%d\n",
+ 	   qb->user, MAJOR(dev), MINOR(dev));
+     return 0;
+   }
+   if ((q_u = find_free_user(q_d)) == -1)
+     return EINVAL;
+ 
+   quotas[q_d].user[q_u].blk_hard = qb->blk_hardlimit;
+   quotas[q_d].user[q_u].blk_soft = (qb->blk_softlimit>qb->blk_hardlimit?qb->blk_hardlimit:qb->blk_softlimit);
+   quotas[q_d].user[q_u].blk_used = qb->curblocks;
+   quotas[q_d].user[q_u].ino_hard = qb->ino_hardlimit;
+   quotas[q_d].user[q_u].ino_soft = (qb->ino_softlimit>qb->ino_hardlimit?qb->ino_hardlimit:qb->ino_softlimit);
+   quotas[q_d].user[q_u].ino_used = qb->curinodes;
+   quotas[q_d].user[q_u].blk_time = qb->blk_etime;
+   quotas[q_d].user[q_u].ino_time = qb->ino_etime;
+   quotas[q_d].user[q_u].q_user = qb->user;
+ 
+ #ifdef QUOTA_VERBOSE
+   printk("quota: adding user %d on %s\n",qb->user,quotas[q_d].dev_name);
+ #endif
+ 
+   return 0;
+ }
+ 
+ int sys_quota(int cmd, const char *dev_root, uid_t uid, const char *addr)
+ {
+   if (!cmd)
+     return -EINVAL;
+ 
+   switch (cmd) {
+   case Q_QUOTAON:
+     return set_quota(dev_root, addr);
+     break;
+   case Q_GETQUOTA:
+     return get_quota(dev_root, uid, (struct copy_quota_block *)addr);
+     break;
+   case Q_SETQUOTA:
+   case Q_SETQLIM:
+     return set_quota_user(cmd, dev_root, uid, (struct copy_quota_block *)addr);
+     break;
+   case Q_QUOTAOFF:
+     return unset_quota(dev_root);
+     break;
+   case Q_SYNC:
+     sync_quota();
+     return 0;
+     break;
+   default:
+     return -EINVAL;
+     break;
+   }
+   return -ENOSYS;
+ }
+ 
+ int sys_setquota(const char *dev_root, const char *qu_name)
+ {
+   return set_quota(dev_root, qu_name);
+ }
+ 
+ static int set_quota(const char *dev_root, const char *qu_name)
+ {
+   struct inode *inode;
+   struct inode *base;
+   struct buffer_head *bh = NULL;
+   u_char stored=0, eoc=0;
+   off_t f_pos=0;
+   int left=0, nr, ret;
+   dev_t dev;
+   short q_d;
+ 
+   if (current->uid || current->euid)
+     return -EPERM;
+ 
+   dev = base->i_dev;
+   if (!(base = namei(dev_root)))
+     return -EINVAL;
+   if (!(inode =_namei(qu_name,base,1))) {
+     iput(base);
+     return -ENOENT;
+   }
+ 
+ /* Take care not to iput a mounted root inode. That is really bad ... */
+   if (base->i_ino != 1)
+     iput(base);
+   else if (base->i_dev == ROOT_DEV)
+     iput(base);
+ 
+   if ((q_d = find_dev(inode->i_dev)) == -1)
+     if ((q_d = find_free_dev()) == -1) {
+       iput(inode);
+       return EINVAL;
+     }
+ 
+   quotas[q_d].dev = inode->i_dev;
+ 
+   for (nr =0;!eoc && nr < Q_MAX_DEV_NAME;nr++) {
+     quotas[q_d].dev_name[nr] = get_fs_byte(dev_root+nr);
+     if (quotas[q_d].dev_name[nr] == '\0')
+       eoc = 1;
+   }
+ 
+   if (nr < Q_MAX_DEV_NAME)
+     quotas[q_d].dev_name[nr] = '\0';
+ 
+   left = inode->i_size;
+   while (left > 0) {
+     if (nr = inode->i_op->bmap(inode,(f_pos)>>BLOCK_SIZE_BITS))
+       if (!(bh = bread(inode->i_dev,nr))) {
+ 	if (stored)
+ 	  quotas[q_d].inode = inode;
+ 	else
+ 	  iput(inode);
+ 	return -1;
+       }
+     nr = f_pos & (BLOCK_SIZE-1);
+     while (f_pos < BLOCK_SIZE && *(bh->b_data+f_pos)) {
+       left -= Q_BLOCK_SIZE;
+       ret = store_block(bh, f_pos, inode->i_dev, q_d);
+       f_pos += Q_BLOCK_SIZE;
+       if (ret) {
+ 	brelse(bh);
+ 	if (stored) 
+ 	  quotas[q_d].inode = inode;
+ 	else
+ 	  iput(inode);		/* release inode */
+ 	return -ret;
+       }
+       stored=1;
+     }
+     brelse(bh);
+   }
+ 
+   no_quotas_set++;
+   quotas[q_d].inode = inode;
+   quotas[q_d].set_on = 1;
+   return 0;
+ }
+ 
+ void sync_quota()
+ {
+   u_short d,u;
+ 
+   if (!no_quotas_set)
+     return;
+ 
+   sync_counter++;
+ 
+   if ((int)sync_counter != Q_SYNC_CNT)
+     return;
+ 
+   for (d=0;d<NR_SUPER;) {
+     if (quotas[d].set_on) {
+       for (u=0;u<Q_RESIDENT;) {
+ 	if (quotas[d].user[u].dirty != 0 && quotas[d].user[u].q_user)
+ 	  quota_write(d,u);
+ 	u++;
+       }
+ /* Update inode info. Need to adjust all time fields */
+       quotas[d].inode->i_atime = quotas[d].inode->i_mtime = 
+ 	quotas[d].inode->i_ctime = CURRENT_TIME;
+       quotas[d].inode->i_count++; /* not realy release the inode */
+       iput(quotas[d].inode);
+     }
+     d++;
+   }
+   sync_counter=0;
+ }
+ 
+ static int write_block(struct buffer_head *bh, int read, u_short q_d,
+ 			     u_short q_u)
+ {
+   struct disk_quota_block *qb;
+ 
+   qb = (struct disk_quota_block *)((bh->b_data)+read);
+   if (qb->user != quotas[q_d].user[q_u].q_user && !NEW_USER(q_d,q_u))
+     return 0;
+ 
+   qb->ino_softlimit = quotas[q_d].user[q_u].ino_soft;
+   qb->ino_hardlimit = quotas[q_d].user[q_u].ino_hard;
+   qb->blk_softlimit = quotas[q_d].user[q_u].blk_soft;
+   qb->blk_hardlimit = quotas[q_d].user[q_u].blk_hard;
+   qb->ino_etime = quotas[q_d].user[q_u].ino_time;
+   qb->blk_etime = quotas[q_d].user[q_u].blk_time;
+   qb->curblocks = quotas[q_d].user[q_u].blk_used;
+   qb->curinodes = quotas[q_d].user[q_u].ino_used;
+   quotas[q_d].user[q_u].dirty = 0; /* remove dirty flag! */
+   return 1;
+ }
+ 
+ void quota_init()
+ {
+   u_short u, d;
+ 
+   for (d =0;d<NR_SUPER;d++) {
+     for (u=0;u<Q_RESIDENT;u++) { 
+       quotas[d].user[u].q_user =0;
+       quotas[d].user[u].dirty = 0;
+     }
+     quotas[d].dev = 0;
+     quotas[d].set_on = 0;
+   }
+  last_uid_done= last_dev_done= last_q_d_used= last_q_u_used=0;
+ }    
+ 
+ static int get_quota(const char *dev_root, uid_t user,
+ 		     struct copy_quota_block *q_blk)
+ {
+   static struct inode *base;
+   short q_d, q_u;
+   dev_t dev;
+ 
+   if (current->euid && current->euid != user)
+     return -EPERM;
+ 
+   if (!(base = namei(dev_root)))
+     return -EINVAL;
+ 
+ 
+   dev = base->i_dev;
+   if (base->i_ino != 1)
+     iput(base);
+   else if (base->i_dev == ROOT_DEV)
+     iput(base);
+ 
+   find_info(dev, user, &q_d, &q_u);
+   if (q_d == -1)
+     return -EPERM;
+   if (q_u == -1)
+     return -EPERM;
+ 
+   verify_area(q_blk, sizeof *q_blk);
+   put_fs_long(quotas[q_d].user[q_u].blk_used,
+ 	      (unsigned long *)&q_blk->blk_used);
+   put_fs_long(quotas[q_d].user[q_u].ino_used,
+ 	      (unsigned long *)&q_blk->ino_used);
+   put_fs_long(quotas[q_d].user[q_u].ino_soft,
+ 	      (unsigned long *)&q_blk->ino_soft);
+   put_fs_long(quotas[q_d].user[q_u].ino_hard,
+ 	      (unsigned long *)&q_blk->ino_hard);
+   put_fs_long(quotas[q_d].user[q_u].blk_soft,
+ 	      (unsigned long *)&q_blk->blk_soft);
+   put_fs_long(quotas[q_d].user[q_u].blk_hard,
+ 	      (unsigned long *)&q_blk->blk_hard);
+   copy_devname(quotas[q_d].dev_name, (char *)&q_blk->dev_name);
+   return 0;
+ }
+ 
+ static int set_quota_user(int cmd, const char *dev_root, uid_t user, 
+ 			  struct copy_quota_block * addr)
+ {
+   static struct inode *base;
+   u_char user_new = 0;
+   short q_d, q_u;
+   dev_t dev;
+ 
+   if (current->euid)
+     return -EPERM;
+ 
+   if (!(base = namei(dev_root)))
+     return -EINVAL;
+ 
+   dev = base->i_dev;
+   if (base->i_ino != 1)
+     iput(base);
+   else if (base->i_dev == ROOT_DEV)
+     iput(base);
+ 
+   find_info(dev, user, &q_d, &q_u);
+   if (q_d == -1)
+     return -EPERM;
+   if (q_u == -1) {
+     if ((q_u = find_free_user(q_d)) == -1)
+       return -EPERM;
+     else
+       user_new = 1;
+   }
+ 
+   quotas[q_d].user[q_u].blk_hard = get_fs_long((unsigned long *)addr);
+   quotas[q_d].user[q_u].blk_soft = get_fs_long(((unsigned long *)addr)+1);
+   quotas[q_d].user[q_u].ino_hard = get_fs_long(((unsigned long *)addr)+3);
+   quotas[q_d].user[q_u].ino_soft = get_fs_long(((unsigned long *)addr)+4);
+   if (cmd == Q_SETQUOTA) {
+     quotas[q_d].user[q_u].blk_used = get_fs_long(((unsigned long *)addr)+2);
+     quotas[q_d].user[q_u].ino_used = get_fs_long(((unsigned long *)addr)+5);
+   }
+   if (user_new) {
+ /* set_quota gave new user. Write to quota file straigth away */    
+     quotas[q_d].user[q_u].q_user = user;
+     quotas[q_d].user[q_u].dirty = 2;
+     quota_write(q_d, q_u);
+   } else
+     quotas[q_d].user[q_u].dirty = 1;
+   return 0;
+ }
+ 
+ static int unset_quota(const char *dev_root)
+ {
+   static struct inode *base;
+   short q_d;
+   dev_t dev;
+ 
+   if (current->euid)
+     return -EPERM;
+ 
+   if (!(base = namei(dev_root)))
+     return -EINVAL;
+ 
+   dev = base->i_dev;
+   if (base->i_ino != 1)
+     iput(base);
+   else if (base->i_dev == ROOT_DEV)
+     iput(base);
+ 
+   if (!LAST_DEV(dev)) {
+     if ((q_d = find_dev(dev)) == -1)
+       return -EPERM;
+   } else
+     q_d = last_q_d_used;
+ 
+   if (!quotas[q_d].set_on)
+     return -EINVAL;
+ 
+   quotas[q_d].set_on = 0;
+   if (no_quotas_set > 0)
+     no_quotas_set--;
+   return 0;
+ }
+ 
+ #endif /* USE_QUOTA */
