/*

 *  linux/fs/file_table.c

 *

 *  Copyright (C) 1991, 1992  Linus Torvalds

 */



#include <linux/config.h>

#include <linux/fs.h>

#include <linux/string.h>

#include <linux/mm.h>



/*

 * first_file points to a doubly linked list of all file structures in

 *            the system.

 * nr_files   holds the length of this list.

 */

struct file * first_file = NULL;

int nr_files = 0;

int max_files = NR_FILE;



/*

 * Insert a new file structure at the head of the list of available ones.

 */

static inline void insert_file_free(struct file *file)

{

	file->f_count = 0;

	file->f_next = first_file;

	file->f_prev = first_file->f_prev;

	file->f_next->f_prev = file;

	file->f_prev->f_next = file;

	first_file = file;

}



/*

 * Remove a file structure from the list of available ones.

 */

static inline void remove_file_free(struct file *file)

{

	if (first_file == file)

		first_file = first_file->f_next;

	file->f_next->f_prev = file->f_prev;

	file->f_prev->f_next = file->f_next;

	file->f_next = file->f_prev = NULL;

}



/*

 * Insert a file structure at the end of the list of available ones.

 */

static inline void put_last_free(struct file *file)

{

	file->f_prev = first_file->f_prev;

	file->f_prev->f_next = file;

	file->f_next = first_file;

	file->f_next->f_prev = file;

}



/*

 * Allocate a new memory page for file structures and

 * insert the new structures into the global list.

 * Returns 0, if there is no more memory, 1 otherwise.

 */

static int grow_files(void)

{

	struct file * file;

	int i;



	/*

	 * We don't have to clear the page because we only look into

	 * f_count, f_prev and f_next and they get initialized in

	 * insert_file_free.  The rest of the file structure is cleared

	 * by get_empty_filp before it is returned.

	 */

	file = (struct file *) __get_free_page(GFP_KERNEL);



	if (!file)

		return 0;



	nr_files += i = PAGE_SIZE/sizeof(struct file);



	if (!first_file)

		file->f_count = 0,

		file->f_next = file->f_prev = first_file = file++,

		i--;



	for (; i ; i--)

		insert_file_free(file++);



	return 1;

}



unsigned long file_table_init(unsigned long start, unsigned long end)

{

	return start;

}



/*

 * Find an unused file structure and return a pointer to it.

 * Returns NULL, if there are no more free file structures or

 * we run out of memory.

 */

struct file * get_empty_filp(void)

{

	int i;

	int max = max_files;

	struct file * f;



	/*

	 * Reserve a few files for the super-user..

	 */

	if (current->euid)

		max -= 10;



	/* if the return is taken, we are in deep trouble */

	if (!first_file && !grow_files())

		return NULL;



	do {

		for (f = first_file, i=0; i < nr_files; i++, f = f->f_next)

			if (!f->f_count) {

				remove_file_free(f);

				memset(f,0,sizeof(*f));

				put_last_free(f);

				f->f_count = 1;

				f->f_version = ++event;

				return f;

			}

	} while (nr_files < max && grow_files());



	return NULL;

}



#ifdef CONFIG_QUOTA



void add_dquot_ref(kdev_t dev, short type)

{

	struct file *filp;

	int cnt;



	for (filp = first_file, cnt = 0; cnt < nr_files; cnt++, filp = filp->f_next) {

		if (!filp->f_count || !filp->f_inode || filp->f_inode->i_dev != dev)

			continue;

		if (filp->f_mode & FMODE_WRITE && filp->f_inode->i_sb->dq_op) {

			filp->f_inode->i_sb->dq_op->initialize(filp->f_inode, type);

			filp->f_inode->i_flags |= S_WRITE;

		}

	}

}



void reset_dquot_ptrs(kdev_t dev, short type)

{

	struct file *filp;

	int cnt;



	for (filp = first_file, cnt = 0; cnt < nr_files; cnt++, filp = filp->f_next) {

		if (!filp->f_count || !filp->f_inode || filp->f_inode->i_dev != dev)

			continue;

		if (IS_WRITABLE(filp->f_inode)) {

			filp->f_inode->i_dquot[type] = NODQUOT;

			filp->f_inode->i_flags &= ~S_WRITE;

		}

	}

}



#endif