| /* |
| * linux/fs/inode.c |
| * |
| * Copyright (C) 1991, 1992 Linus Torvalds |
| */ |
| |
| #include <linux/stat.h> |
| #include <linux/sched.h> |
| #include <linux/kernel.h> |
| #include <linux/mm.h> |
| #include <linux/string.h> |
| |
| #include <asm/system.h> |
| |
| static struct inode * hash_table[NR_IHASH]; |
| static struct inode * first_inode; |
| static struct wait_queue * inode_wait = NULL; |
| static int nr_inodes = 0, nr_free_inodes = 0; |
| |
| static inline int const hashfn(dev_t dev, int i) |
| { |
| return (dev ^ i) % NR_IHASH; |
| } |
| |
| static inline struct inode ** const hash(dev_t dev, int i) |
| { |
| return hash_table + hashfn(dev, i); |
| } |
| |
| static void insert_inode_free(struct inode *inode) |
| { |
| inode->i_next = first_inode; |
| inode->i_prev = first_inode->i_prev; |
| inode->i_next->i_prev = inode; |
| inode->i_prev->i_next = inode; |
| first_inode = inode; |
| } |
| |
| static void remove_inode_free(struct inode *inode) |
| { |
| if (first_inode == inode) |
| first_inode = first_inode->i_next; |
| if (inode->i_next) |
| inode->i_next->i_prev = inode->i_prev; |
| if (inode->i_prev) |
| inode->i_prev->i_next = inode->i_next; |
| inode->i_next = inode->i_prev = NULL; |
| } |
| |
| void insert_inode_hash(struct inode *inode) |
| { |
| struct inode **h; |
| h = hash(inode->i_dev, inode->i_ino); |
| |
| inode->i_hash_next = *h; |
| inode->i_hash_prev = NULL; |
| if (inode->i_hash_next) |
| inode->i_hash_next->i_hash_prev = inode; |
| *h = inode; |
| } |
| |
| static void remove_inode_hash(struct inode *inode) |
| { |
| struct inode **h; |
| h = hash(inode->i_dev, inode->i_ino); |
| |
| if (*h == inode) |
| *h = inode->i_hash_next; |
| if (inode->i_hash_next) |
| inode->i_hash_next->i_hash_prev = inode->i_hash_prev; |
| if (inode->i_hash_prev) |
| inode->i_hash_prev->i_hash_next = inode->i_hash_next; |
| inode->i_hash_prev = inode->i_hash_next = NULL; |
| } |
| |
| static void put_last_free(struct inode *inode) |
| { |
| remove_inode_free(inode); |
| inode->i_prev = first_inode->i_prev; |
| inode->i_prev->i_next = inode; |
| inode->i_next = first_inode; |
| inode->i_next->i_prev = inode; |
| } |
| |
| void grow_inodes(void) |
| { |
| unsigned long page; |
| struct inode * inode; |
| int i; |
| |
| page = get_free_page(GFP_KERNEL); |
| if (!page) |
| return; |
| inode = (struct inode *) page; |
| for (i=0; i < (PAGE_SIZE / sizeof(struct inode)); i++, inode++) |
| { |
| if (!first_inode) |
| { |
| inode->i_next = inode; |
| inode->i_prev = inode; |
| first_inode = inode; |
| } |
| else |
| insert_inode_free(inode); |
| } |
| nr_inodes += i; |
| nr_free_inodes += i; |
| } |
| |
| unsigned long inode_init(unsigned long start, unsigned long end) |
| { |
| memset(hash_table, 0, sizeof(hash_table)); |
| first_inode = NULL; |
| return start; |
| } |
| |
| static void __wait_on_inode(struct inode *); |
| |
| static inline void wait_on_inode(struct inode * inode) |
| { |
| if (inode->i_lock) |
| __wait_on_inode(inode); |
| } |
| |
| static inline void lock_inode(struct inode * inode) |
| { |
| wait_on_inode(inode); |
| inode->i_lock = 1; |
| } |
| |
| static inline void unlock_inode(struct inode * inode) |
| { |
| inode->i_lock = 0; |
| wake_up(&inode->i_wait); |
| } |
| |
| /* |
| * Note that we don't want to disturb any wait-queues when we discard |
| * an inode. |
| * |
| * Argghh. Got bitten by a gcc problem with inlining: no way to tell |
| * the compiler that the inline asm function 'memset' changes 'inode'. |
| * I've been searching for the bug for days, and was getting desperate. |
| * Finally looked at the assembler output... Grrr. |
| * |
| * The solution is the weird use of 'volatile'. Ho humm. Have to report |
| * it to the gcc lists, and hope we can do this more cleanly some day.. |
| */ |
| void clear_inode(struct inode * inode) |
| { |
| struct wait_queue * wait; |
| |
| wait_on_inode(inode); |
| wait = ((volatile struct inode *) inode)->i_wait; |
| remove_inode_hash(inode); |
| remove_inode_free(inode); |
| if (inode->i_count) |
| nr_free_inodes++; |
| memset(inode,0,sizeof(*inode)); |
| ((volatile struct inode *) inode)->i_wait = wait; |
| insert_inode_free(inode); |
| } |
| |
| int fs_may_mount(dev_t dev) |
| { |
| struct inode * inode; |
| int i; |
| |
| for (inode = first_inode, i=0; i<nr_inodes; i++, inode=inode->i_next) { |
| if (inode->i_dev != dev) |
| continue; |
| if (inode->i_count || inode->i_dirt || inode->i_lock) |
| return 0; |
| clear_inode(inode); |
| } |
| return 1; |
| } |
| |
| int fs_may_umount(dev_t dev, struct inode * mount_root) |
| { |
| struct inode * inode; |
| int i; |
| |
| for (inode = first_inode, i=0; i<nr_inodes; i++, inode=inode->i_next) { |
| if (inode->i_dev==dev && inode->i_count) |
| if (inode == mount_root && inode->i_count == 1) |
| continue; |
| else |
| return 0; |
| } |
| return 1; |
| } |
| |
| int fs_may_remount_ro(dev_t dev) |
| { |
| struct file * file; |
| int i; |
| |
| /* Check that no files are currently opened for writing. */ |
| for (file = first_file, i=0; i<nr_files; i++, file=file->f_next) { |
| if (!file->f_count || !file->f_inode || |
| file->f_inode->i_dev != dev) |
| continue; |
| if (S_ISREG(file->f_inode->i_mode) && (file->f_mode & 2)) |
| return 0; |
| } |
| return 1; |
| } |
| |
| static void write_inode(struct inode * inode) |
| { |
| if (!inode->i_dirt) |
| return; |
| wait_on_inode(inode); |
| if (!inode->i_dirt) |
| return; |
| if (!inode->i_sb || !inode->i_sb->s_op || !inode->i_sb->s_op->write_inode) { |
| inode->i_dirt = 0; |
| return; |
| } |
| inode->i_lock = 1; |
| inode->i_sb->s_op->write_inode(inode); |
| unlock_inode(inode); |
| } |
| |
| static void read_inode(struct inode * inode) |
| { |
| lock_inode(inode); |
| if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->read_inode) |
| inode->i_sb->s_op->read_inode(inode); |
| unlock_inode(inode); |
| } |
| |
| /* |
| * notify_change is called for inode-changing operations such as |
| * chown, chmod, utime, and truncate. It is guaranteed (unlike |
| * write_inode) to be called from the context of the user requesting |
| * the change. It is not called for ordinary access-time updates. |
| * NFS uses this to get the authentication correct. -- jrs |
| */ |
| |
| int notify_change(int flags, struct inode * inode) |
| { |
| if (inode->i_sb && inode->i_sb->s_op && |
| inode->i_sb->s_op->notify_change) |
| return inode->i_sb->s_op->notify_change(flags, inode); |
| return 0; |
| } |
| |
| /* |
| * bmap is needed for demand-loading and paging: if this function |
| * doesn't exist for a filesystem, then those things are impossible: |
| * executables cannot be run from the filesystem etc... |
| * |
| * This isn't as bad as it sounds: the read-routines might still work, |
| * so the filesystem would be otherwise ok (for example, you might have |
| * a DOS filesystem, which doesn't lend itself to bmap very well, but |
| * you could still transfer files to/from the filesystem) |
| */ |
| int bmap(struct inode * inode, int block) |
| { |
| if (inode->i_op && inode->i_op->bmap) |
| return inode->i_op->bmap(inode,block); |
| return 0; |
| } |
| |
| void invalidate_inodes(dev_t dev) |
| { |
| int i; |
| struct inode * inode; |
| |
| inode = first_inode; |
| for(i = 0; i < nr_inodes*2; i++, inode = inode->i_next) { |
| wait_on_inode(inode); |
| if (inode->i_dev == dev) { |
| if (inode->i_count) { |
| printk("VFS: inode busy on removed device %d/%d\n", |
| MAJOR(dev), MINOR(dev)); |
| continue; |
| } |
| clear_inode(inode); |
| } |
| } |
| } |
| |
| void sync_inodes(dev_t dev) |
| { |
| int i; |
| struct inode * inode; |
| |
| inode = first_inode; |
| for(i = 0; i < nr_inodes*2; i++, inode = inode->i_next) { |
| if (dev && inode->i_dev != dev) |
| continue; |
| wait_on_inode(inode); |
| if (inode->i_dirt) |
| write_inode(inode); |
| } |
| } |
| |
| void iput(struct inode * inode) |
| { |
| if (!inode) |
| return; |
| wait_on_inode(inode); |
| if (!inode->i_count) { |
| printk("VFS: iput: trying to free free inode\n"); |
| printk("VFS: device %d/%d, inode %d, mode=0%07o\n", |
| MAJOR(inode->i_rdev), MINOR(inode->i_rdev), |
| inode->i_ino, inode->i_mode); |
| return; |
| } |
| if (inode->i_pipe) { |
| wake_up(&PIPE_READ_WAIT(*inode)); |
| wake_up(&PIPE_WRITE_WAIT(*inode)); |
| } |
| repeat: |
| if (inode->i_count>1) { |
| inode->i_count--; |
| return; |
| } |
| wake_up(&inode_wait); |
| if (inode->i_pipe) { |
| unsigned long page = (unsigned long) PIPE_BASE(*inode); |
| PIPE_BASE(*inode) = NULL; |
| free_page(page); |
| } |
| if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->put_inode) { |
| inode->i_sb->s_op->put_inode(inode); |
| if (!inode->i_nlink) |
| return; |
| } |
| if (inode->i_dirt) { |
| write_inode(inode); /* we can sleep - so do again */ |
| wait_on_inode(inode); |
| goto repeat; |
| } |
| inode->i_count--; |
| nr_free_inodes++; |
| return; |
| } |
| |
| struct inode * get_empty_inode(void) |
| { |
| struct inode * inode, * best; |
| int i; |
| |
| if (nr_inodes < NR_INODE && nr_free_inodes < (nr_inodes >> 2)) |
| grow_inodes(); |
| repeat: |
| inode = first_inode; |
| best = NULL; |
| for (i = 0; i<nr_inodes; inode = inode->i_next, i++) { |
| if (!inode->i_count) { |
| if (!best) |
| best = inode; |
| if (!inode->i_dirt && !inode->i_lock) { |
| best = inode; |
| break; |
| } |
| } |
| } |
| if (!best || best->i_dirt || best->i_lock) |
| if (nr_inodes < NR_INODE) { |
| grow_inodes(); |
| goto repeat; |
| } |
| inode = best; |
| if (!inode) { |
| printk("VFS: No free inodes - contact Linus\n"); |
| sleep_on(&inode_wait); |
| goto repeat; |
| } |
| if (inode->i_lock) { |
| wait_on_inode(inode); |
| goto repeat; |
| } |
| if (inode->i_dirt) { |
| write_inode(inode); |
| goto repeat; |
| } |
| if (inode->i_count) |
| goto repeat; |
| clear_inode(inode); |
| inode->i_count = 1; |
| inode->i_nlink = 1; |
| nr_free_inodes--; |
| if (nr_free_inodes < 0) |
| { |
| printk ("VFS: get_empty_inode: bad free inode count.\n"); |
| nr_free_inodes = 0; |
| } |
| return inode; |
| } |
| |
| struct inode * get_pipe_inode(void) |
| { |
| struct inode * inode; |
| |
| if (!(inode = get_empty_inode())) |
| return NULL; |
| if (!(PIPE_BASE(*inode) = (char *) get_free_page(GFP_USER))) { |
| inode->i_count = 0; |
| return NULL; |
| } |
| inode->i_count = 2; /* sum of readers/writers */ |
| PIPE_READ_WAIT(*inode) = PIPE_WRITE_WAIT(*inode) = NULL; |
| PIPE_HEAD(*inode) = PIPE_TAIL(*inode) = 0; |
| PIPE_RD_OPENERS(*inode) = PIPE_WR_OPENERS(*inode) = 0; |
| PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 1; |
| inode->i_pipe = 1; |
| inode->i_mode |= S_IFIFO; |
| inode->i_uid = current->euid; |
| inode->i_gid = current->egid; |
| inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; |
| return inode; |
| } |
| |
| struct inode * iget(struct super_block * sb,int nr) |
| { |
| struct inode * inode, * empty; |
| |
| if (!sb) |
| panic("VFS: iget with sb==NULL"); |
| repeat: |
| empty = get_empty_inode(); |
| inode = *(hash(sb->s_dev,nr)); |
| while (inode) { |
| if (inode->i_dev != sb->s_dev || inode->i_ino != nr) { |
| inode = inode->i_hash_next; |
| continue; |
| } |
| wait_on_inode(inode); |
| if (inode->i_dev != sb->s_dev || inode->i_ino != nr) |
| goto repeat; |
| if (!inode->i_count) |
| nr_free_inodes--; |
| inode->i_count++; |
| if (inode->i_mount) { |
| int i; |
| |
| for (i = 0 ; i<NR_SUPER ; i++) |
| if (super_block[i].s_covered==inode) |
| break; |
| if (i >= NR_SUPER) { |
| printk("VFS: Mounted inode hasn't got sb\n"); |
| if (empty) |
| iput(empty); |
| return inode; |
| } |
| iput(inode); |
| if (!(inode = super_block[i].s_mounted)) |
| printk("VFS: Mounted device %d/%d has no rootinode\n", |
| MAJOR(inode->i_dev), MINOR(inode->i_dev)); |
| else { |
| if (!inode->i_count) |
| nr_free_inodes--; |
| inode->i_count++; |
| wait_on_inode(inode); |
| } |
| } |
| if (empty) |
| iput(empty); |
| return inode; |
| } |
| if (!empty) |
| return (NULL); |
| inode = empty; |
| inode->i_sb = sb; |
| inode->i_dev = sb->s_dev; |
| inode->i_ino = nr; |
| inode->i_flags = sb->s_flags; |
| put_last_free(inode); |
| insert_inode_hash(inode); |
| read_inode(inode); |
| return inode; |
| } |
| |
| /* |
| * The "new" scheduling primitives (new as of 0.97 or so) allow this to |
| * be done without disabling interrupts (other than in the actual queue |
| * updating things: only a couple of 386 instructions). This should be |
| * much better for interrupt latency. |
| */ |
| static void __wait_on_inode(struct inode * inode) |
| { |
| struct wait_queue wait = { current, NULL }; |
| |
| add_wait_queue(&inode->i_wait, &wait); |
| repeat: |
| current->state = TASK_UNINTERRUPTIBLE; |
| if (inode->i_lock) { |
| schedule(); |
| goto repeat; |
| } |
| remove_wait_queue(&inode->i_wait, &wait); |
| current->state = TASK_RUNNING; |
| } |