• Jump To … +
    bitmap.c bitmap.h dir.c file.c inode.c itree.c namei.c xiafs.h
  • dir.c

  • ¶

    Many of the functions in this file are used in namei.c.

    /*
     * Porting work to modern kernels copyright (C) Jeremy Bingham, 2013.
     * Based on work by Linus Torvalds, Q. Frank Xia, and others as noted.
     *
     * This port of Q. Frank Xia's xiafs was done using the existing minix
     * filesystem code, but based on the original xiafs code in pre-2.1.21 or so
     * kernels.
     */
    
    /*
     *  linux/fs/xiafs/dir.c
     *
     *  Copyright (C) 1991, 1992 Linus Torvalds
     *
     *  xiafs directory handling functions
     *
     *  Updated to filesystem version 3 by Daniel Aragones
     */
    
    #include "xiafs.h"
    #include <linux/buffer_head.h>
    #include <linux/highmem.h>
    #include <linux/swap.h>
    
    typedef struct xiafs_direct xiafs_dirent;
    
    static int xiafs_readdir(struct file *, struct dir_context *);
  • ¶

    Round a number up to the nearest number divisible by 4.

    #define RNDUP4(x)	((3+(u_long)(x)) & ~3)
  • ¶

    Sets the function pointers for directory specific file operations. Only one of the xiafs_dir_operations is a filesystem specific function: xiafs_readdir. The others use generic functions.

    const struct file_operations xiafs_dir_operations = {
    	.llseek		= generic_file_llseek,
    	.read		= generic_read_dir,
    	.iterate	= xiafs_readdir,
    	.fsync		= generic_file_fsync,
    };
  • ¶

    Let the page in question go. Be free, little page.

    static inline void dir_put_page(struct page *page)
    {
    	kunmap(page);
    	page_cache_release(page);
    }
  • ¶

    The last byte is either equal to the page cache size (which is architecture dependent, but is 4KB on x86), or if this is the last page the directory is using for its dentry list, the inode's size ANDed with the page cache size - 1. In a completely made up example, if the inode's size is 8888 bytes, and page_nr was 2 (meaning that the page being requested is the last one the directory is using, since three 4KB pages are needed to hold 8888 bytes), by ANDing 8888 with (PAGE_CACHE_SIZE - 1) we get 696 back as the last used byte on that page.

    /*
     * Return the offset into page `page_nr' of the last valid
     * byte in that page, plus one.
     */
    static unsigned
    xiafs_last_byte(struct inode *inode, unsigned long page_nr)
    {
    	unsigned last_byte = PAGE_CACHE_SIZE;
    
    	if (page_nr == (inode->i_size >> PAGE_CACHE_SHIFT))
    		last_byte = inode->i_size & (PAGE_CACHE_SIZE - 1);
    	return last_byte;
    }
  • ¶

    How many pages is this directory using for its dentry list.

    static inline unsigned long dir_pages(struct inode *inode)
    {
    	return (inode->i_size+PAGE_CACHE_SIZE-1)>>PAGE_CACHE_SHIFT;
    }
  • ¶

    Commit the changed directory page.

    static int dir_commit_chunk(struct page *page, loff_t pos, unsigned len)
    {
    	struct address_space *mapping = page->mapping;
    	struct inode *dir = mapping->host;
    	int err = 0;
  • ¶

    Commit the change made to this page of memory.

    	block_write_end(NULL, mapping, pos, len, len, page, NULL);
  • ¶

    Make the directory bigger, if need be.

    	if (pos+len > dir->i_size) {
    		i_size_write(dir, pos+len);
    		mark_inode_dirty(dir);
    	}
  • ¶

    If this filesystem has been mounted dirsync, write the changes out immediately; otherwise unlock the page. It will get written out soon enough.

    	if (IS_DIRSYNC(dir))
    		err = write_one_page(page, 1);
    	else
    		unlock_page(page);
    	return err;
    }
  • ¶

    Get page n of the directory's list of entries.

    static struct page * dir_get_page(struct inode *dir, unsigned long n)
    {
    	struct address_space *mapping = dir->i_mapping;
  • ¶

    Read in the page.

    	struct page *page = read_mapping_page(mapping, n, NULL);
    	if (!IS_ERR(page)) {
  • ¶

    If the read succeeded, get the address for the page from kmap. If the kmap call failed, return an IO error.

    		kmap(page);
    		if (!PageUptodate(page))
    			goto fail;
    	}
    	return page;
    
    fail:
    	dir_put_page(page);
    	return ERR_PTR(-EIO);
    }
  • ¶

    Gets the next dentry in the directory. The xiafs directory entries have variable lengths, so the next entry will be d_rec_len bytes after the current entry.

    static inline void *xiafs_next_entry(void *de)
    {
    	/* make this less gimpy */
    	xiafs_dirent *d = (xiafs_dirent *)de; /* not the most efficient way */
    	return (void*)((char*)de + d->d_rec_len);
    }
  • ¶

    Where the heavy lifting of reading a directory's contents happens. It's important enough, and specific enough, that xiafs_readdir is the only function pointer in the xiafs_dir_operations struct to use a xiafs-specific function.

    static int xiafs_readdir(struct file * file, struct dir_context *ctx)
    {
    	unsigned long pos = ctx->pos;
    	struct inode *inode = file_inode(file);
  • ¶

    The number of pages this directory is using to store its list of entries

    	unsigned long npages = dir_pages(inode);
  • ¶

    "chunk_size" is very important with the Minix file system, since all directory entries have the same length. Xiafs has variable length dentries, but since the minimum length for a dentry is 12 this value still has some use.

    	unsigned chunk_size = _XIAFS_DIR_SIZE; /* 1st entry is always 12, it seems. */
    	char *name;
    	unsigned char namelen;
    	__u32 inumber;
    	unsigned offset;
    	unsigned long n;
  • ¶

    Set our position in the dentry list.

    	ctx->pos = pos = ALIGN(pos, chunk_size); /* (pos + chunk_size-1) & ~(chunk_size-1); */
  • ¶

    Don't go off the edge.

    	if (pos >= inode->i_size)
    		return 0;
  • ¶

    Calculate the starting offset from the beginning of the directory's data.

    	offset = pos & ~PAGE_CACHE_MASK;
  • ¶

    Calculate which page we start on.

    	n = pos >> PAGE_CACHE_SHIFT;
  • ¶

    Start walking through the pages of memory.

    	for ( ; n < npages; n++, offset = 0) {
    		char *p, *kaddr, *limit;
  • ¶

    Get the page.

    		struct page *page = dir_get_page(inode, n);
    
    		if (IS_ERR(page))
    			continue;
  • ¶

    The address of the page's data.

    		kaddr = (char *)page_address(page);
  • ¶

    If all is going well, p will be the first dentry on the page.

    		p = kaddr+offset;
  • ¶

    Subtracting chunk_size from xiafs_last_byte keeps it from marching off into space.

    		limit = kaddr + xiafs_last_byte(inode, n) - chunk_size;
  • ¶

    While p is less than the limit, get the next directory entry.

    		for ( ; p <= limit; p = xiafs_next_entry(p)) {
  • ¶

    Cast p to be a xiafs dentry.

    			xiafs_dirent *de = (xiafs_dirent *)p;
  • ¶

    A xiafs directory entry's length should never be less than 12. If it's 0, something's wrong with the filesystem.

    			if (de->d_rec_len == 0){
    				printk("XIAFS: Zero-length directory entry at (%s %d)\n", WHERE_ERR);
    				dir_put_page(page);
    				return -EIO;
    			}
  • ¶

    Otherwise we found something.

    			name = de->d_name;
    			inumber = de->d_ino;
    			namelen = de->d_name_len;
  • ¶

    If this entry has an inode number, add the entry to the list. If it doesn't work, stop trying to read the directory.

    			if (inumber) {
    				if (!dir_emit(ctx, name, namelen, inumber, DT_UNKNOWN)){
    					dir_put_page(page);
    					return 0;
    				}
    			}
    			/* Minix has here:
    			 * ctx->pos += chunk_size;
    			 * xiafs has variable length directories, though, so we
    			 * want to increase the position by the length of the
    			 * directory entry rather than a fixed chunk size. */
  • ¶

    Advance the position to the next entry.

    			ctx->pos += de->d_rec_len;
    		}
    		dir_put_page(page);
    	}
    
    	return 0;
    }
  • ¶

    Check if two file names match. First, however, it checks to see if the first name is shorter than the other and only compares the names if it is not.

    static inline int namecompare(int len, int maxlen,
    	const char * name, const char * buffer)
    {
    	if (len < maxlen && buffer[len])
    		return 0;
    	return !memcmp(name, buffer, len);
    }
  • ¶

    The comment here explains exactly what this function does.

    /*
     *	xiafs_find_entry()
     *
     * finds an entry in the specified directory with the wanted name. It
     * returns the cache buffer in which the entry was found, and the entry
     * itself (as a parameter - res_dir). It does NOT read the inode of the
     * entry - you'll have to do that yourself if you want to.
     */
    xiafs_dirent *xiafs_find_entry(struct dentry *dentry, struct page **res_page, struct xiafs_direct **old_de)
    {
  • ¶

    Set up the name, length of the name, and inode of the parent directory.

    	const char * name = dentry->d_name.name;
    	int namelen = dentry->d_name.len;
    	struct inode * dir = dentry->d_parent->d_inode;
    	unsigned long n;
  • ¶

    Number of pages the directory is using for its dentry list.

    	unsigned long npages = dir_pages(dir);
    	struct page *page = NULL;
    	char *p;
    
    	char *namx;
    	__u32 inumber;
    	*res_page = NULL;
  • ¶

    Walk through the pages of memory.

    	for (n = 0; n < npages; n++) {
    		char *kaddr, *limit;
    		unsigned short reclen;
    		xiafs_dirent *de_pre;
    
    		page = dir_get_page(dir, n);
  • ¶

    If fetching page n gave an error, try the next one.

    		if (IS_ERR(page))
    			continue;
  • ¶

    Set the limit, the starting address, and the previous dentry.

    		kaddr = (char*)page_address(page);
    		limit = kaddr + xiafs_last_byte(dir, n) - 12;
    		de_pre = (xiafs_dirent *)kaddr;
  • ¶

    Loop over each entry on this page.

    		for (p = kaddr; p <= limit; p = xiafs_next_entry(p)) {
  • ¶

    Cast p to be a xiafs dentry.

    			xiafs_dirent *de = (xiafs_dirent *)p;
  • ¶

    If the dentry's length is 0, something is wrong with the filesystem. Print an error and bail.

    			if (de->d_rec_len == 0){
    				printk("XIAFS: Zero-length directory entry at (%s %d)\n", WHERE_ERR);
    				dir_put_page(page);
    				return ERR_PTR(-EIO);
    			}
    			namx = de->d_name;
    			inumber = de->d_ino;
  • ¶

    If this dentry has no inode, continue on.

    			if (!inumber)
    				continue;
    			reclen = de->d_rec_len;
  • ¶

    If old_de is not a NULL pointer, set it to the address of the previous dentry. If the found entry is to be deleted, the previous dentry needs to have is d_rec_len expanded to encompass the deleted entry.

    			if(old_de)
    				*old_de = de_pre;
  • ¶

    If the names match, we found what we were looking for.

    			if (namecompare(namelen, _XIAFS_NAME_LEN, name, namx))
    				goto found;
  • ¶

    If they didn't match in this iteration, however, set the current dentry to be the next dentry's previous dentry.

    			de_pre = de;
    		}
    		dir_put_page(page);
    	}
  • ¶

    If it reaches this point, the desired entry was not found.

    	return NULL;
    
    found:
  • ¶

    If this point is reached, the entry was found. Set *res_page so the caller has access to the dentry's page in memory and return the dentry pointer.

    	*res_page = page;
    	return (xiafs_dirent *)p;
    }
  • ¶

    Add a link to an inode in a directory with the given dentry by inserting it into the directory's list of dentries.

    int xiafs_add_link(struct dentry *dentry, struct inode *inode)
    {
  • ¶

    The directory to add the link to.

    	struct inode *dir = dentry->d_parent->d_inode;
  • ¶

    The desired name and name length.

    	const char * name = dentry->d_name.name;
    	int namelen = dentry->d_name.len;
    	struct page *page = NULL;
  • ¶

    The number of pages of memory this directory is using.

    	unsigned long npages = dir_pages(dir);
    	unsigned long n;
    	char *kaddr, *p;
    	xiafs_dirent *de, *de_pre;
    	loff_t pos;
    	int err;
    	int i;
    	int rec_size;
    	char *namx = NULL;
    	__u32 inumber;
    
    	/*
    	 * We take care of directory expansion in the same loop
    	 * This code plays outside i_size, so it locks the page
    	 * to protect that region.
    	 */
  • ¶

    Go through the pages looking for a good place to add the link.

    	for (n = 0; n <= npages; n++) {
    		char *limit, *dir_end;
  • ¶

    Get the page here. A lot of the code in this function is similar to the readdir and find_entry functions.

    		page = dir_get_page(dir, n);
    		err = PTR_ERR(page);
    		if (IS_ERR(page))
    			goto out;
    		lock_page(page);
  • ¶

    Set the beginning address.

    		kaddr = (char*)page_address(page);
  • ¶

    Set the end of the directory and the limit on this page's size.

    		dir_end = kaddr + xiafs_last_byte(dir, n);
    		limit = kaddr + PAGE_CACHE_SIZE;
  • ¶

    Store the first entry as the previous entry for the first round of the for loop.

    		de_pre = (xiafs_dirent *)kaddr;
  • ¶

    Go through this page's dentries.

    		for (p = kaddr; p < limit; p = xiafs_next_entry(p)) {
  • ¶

    As before, cast p to a xiafs directory entry.

    			de = (xiafs_dirent *)p;
  • ¶

    If the dentry's record length is 0 and we're not at the end of the directory, bail with an error because something's wrong with the filesystem.

    			if (de->d_rec_len == 0 && p != dir_end){
    				printk("XIAFS: Zero-length directory entry at (%s %d)\n", WHERE_ERR);
    				dir_put_page(page);
    				return -EIO;
    			}
    			rec_size = de->d_rec_len;
  • ¶

    If we can fit the record for this link in the space of another dentry, great. This can happen if a link was deleted earlier and had its length added to the previous dentry, or if we're on the last entry in the list, in which case the dentry's record length exends to the end of the 1024 block. This block size mismatch between xiafs expecting 1024 byte blocks and the kernel using (architecture dependent, but on x86 4KB) pages of a potentially different size causes some interesting issues.

    Anyway, if we can fit the dentry inside another dentry, set the pointer to the previous entry to the current entry, set the current dentry pointer to just after the now previous entry, set the current dentry record length equal to the previous entry's former length minus its current length and its inode number to 0, and shrink the previous entry's record length to its new length.

    			if (de->d_ino && RNDUP4(de->d_name_len)+RNDUP4(namelen)+16 <= de->d_rec_len){
    				/* We have an entry we can get another one 
    				 * inside of. */
    				i = RNDUP4(de->d_name_len)+8;
    				de_pre = de;
    				de = (xiafs_dirent *)(i+(u_char *)de_pre);
    				de->d_ino = 0;
    				de->d_rec_len = de_pre->d_rec_len-i;
    				de_pre->d_rec_len=i;
    			}
    			namx = de->d_name;
    			inumber = de->d_ino;
  • ¶

    If p == dir_end, we've smacked up against the end of the directory. Add another 1k bytes to the directory.

    			if (p == dir_end) {
    				/* We hit i_size */
    				de->d_ino = 0;
  • ¶

    In practice, this does not appear to be an issue.

    				/* NOTE: need to test what happens when dirsize
    				 * is equal to the page size, or when we go over
    				 * the initial XIAFS_ZSIZE. */
    				rec_size = de->d_rec_len = XIAFS_ZSIZE(xiafs_sb(dir->i_sb));
    				/* We're at the end of the directory, so we
    				 * need to make the new d_rec_len equal to
    				 * XIAFS_ZSIZE */
    				goto got_it;
    			}
  • ¶

    If it fits, we have what we needed.

    			if (!inumber && RNDUP4(namelen)+ 8 <= de->d_rec_len)
    				goto got_it;
    			err = -EEXIST;
  • ¶

    Make sure this entry doesn't have the same name as the one we want to add.

    			if (namecompare(namelen, _XIAFS_NAME_LEN, name, namx))
    				goto out_unlock;
    			de_pre = de;
    		}
    		unlock_page(page);
    		dir_put_page(page);
    	}
  • ¶

    If it gets here, something went wrong.

    	BUG();
    	return -EINVAL;
    
    got_it:
  • ¶

    If it's here everything went great. Fill in the dentry's name and inode number, commit the chunk of memory, and update the directory's change and modification time.

    	pos = page_offset(page) + p - (char *)page_address(page);
    	err = xiafs_prepare_chunk(page, pos, rec_size);
    	if (err)
    		goto out_unlock;
    	memcpy (namx, name, namelen);
    	/* memset (namx + namelen, 0, de->d_rec_len - namelen - 7); */
    	de->d_name[namelen] = 0;
    	de->d_name_len=namelen;
    	de->d_ino = inode->i_ino;
    	err = dir_commit_chunk(page, pos, rec_size);
    	dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
    	mark_inode_dirty(dir);
  • ¶

    A goto can go directly here without setting the link in case something went wrong. It can also be a perfectly reasonable and expected thing to happen, though, which is why it's still in the normal flow of execution.

    out_put:
    	dir_put_page(page);
  • ¶

    As above.

    out:
    	return err;
  • ¶

    This is a little confusing, but if something goes wrong and the page needs to be unlocked, the code takes a goto to get here and unlock the page. It's after everything else to avoid unlocking the page when it was either successful in adding the link, or failed before the page was fetched. Either way, having another goto sending it back to out_put feels very... odd.

    out_unlock:
    	unlock_page(page);
    	goto out_put;
    }
  • ¶

    Standing in opposition to the previous function, this deletes entries from a directory.

    int xiafs_delete_entry(struct xiafs_direct *de, struct xiafs_direct *de_pre, struct page *page)
    {
  • ¶

    Get the dentry's inode.

    	struct address_space *mapping = page->mapping;
    	struct inode *inode = (struct inode*)mapping->host;
  • ¶

    Get the page's address and the dentry's position on the page.

    	char *kaddr = page_address(page);
    	loff_t pos = page_offset(page) + (char*)de - kaddr;
    	loff_t tmp_pos;
    	unsigned len = de->d_rec_len;
    	int err;
  • ¶

    Lock the page to keep someone from changing it from under us.

    	lock_page(page);
  • ¶

    If the entry and the previous entry are the same, life is a lot simpler. Just clear the dentry's inode number.

    	if (de == de_pre){
    		de->d_ino = 0;
    	} else {
  • ¶

    Otherwise we have to extend the previous entry (de_pre) to cover the space used by the dentry to be deleted.

    	/* Join the previous entry with this one. */
  • ¶

    Move de_pre up the chain, if necessary.

    		while (de_pre->d_rec_len + (u_char *)de_pre < (u_char *)de){
  • ¶

    Bail on a bad directory entry, if one is found.

    			if (de_pre->d_rec_len < 12){
    				printk("XIA-FS: bad directory entry (%s %d)\n", WHERE_ERR);
    				return -1;
    			}
    			de_pre=(struct xiafs_direct *)(de_pre->d_rec_len + (u_char *)de_pre);
    		}
  • ¶

    If de_pre extends past the directory entry we're deleting, something is very wrong.

    		if (de_pre->d_rec_len + (u_char *)de_pre > (u_char *)de){
    			printk("XIA-FS: bad directory entry (%s %d)\n", WHERE_ERR);
    			return -1;
    			}
  • ¶

    As this comment says, don't join extend de_pre to cover the deleted dentry if it would make de_pre's d_rec_len be greater than XIAFS_ZSIZE. If that is the case, just set the deleted entry's inode number to 0. It may be able to be used later.

    		/* d_rec_len can only be XIAFS_ZSIZE at most. Don't join them
    		 * together if they'd go over */
    		tmp_pos = page_offset(page) + (char *)de_pre - kaddr;
    		if (((tmp_pos & (XIAFS_ZSIZE(xiafs_sb(inode->i_sb)) - 1)) + de_pre->d_rec_len + de->d_rec_len) <= XIAFS_ZSIZE(xiafs_sb(inode->i_sb))){
    			de_pre->d_rec_len += de->d_rec_len;
    			len = de_pre->d_rec_len;
    			pos = tmp_pos;
    		} else {
    			/* If it would go over, just set d_ino to 0 */
    			de->d_ino = 0;
    		}
    	}
  • ¶

    Prepare and commit the change.

    	err = xiafs_prepare_chunk(page, pos, len);
    
    	if (err == 0) {
    		err = dir_commit_chunk(page, pos, len);
    	} else {
    		unlock_page(page);
    	}
    	dir_put_page(page);
  • ¶

    Update the entry's inode change and modification times and mark it dirty.

    	inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC;
    	mark_inode_dirty(inode);
    	return err;
    }
  • ¶

    Make an empty directory.

    int xiafs_make_empty(struct inode *inode, struct inode *dir)
    {
  • ¶

    Get the page we need.

    	struct address_space *mapping = inode->i_mapping;
    	struct page *page = grab_cache_page(mapping, 0);
    	char *kaddr;
    	int err;
  • ¶

    A brand new directory always gets XIAFS_ZSIZE bytes, which as mentioned before always ends up being 1024 bytes.

    	unsigned int zsize = XIAFS_ZSIZE(xiafs_sb(dir->i_sb));
    	xiafs_dirent *de;
  • ¶

    Of course, if we don't have a page we have to bail.

    	if (!page)
    		return -ENOMEM;
  • ¶

    Prepare the page, bombing out if it doesn't work.

    	err = xiafs_prepare_chunk(page, 0, zsize);
    	if (err) {
    		unlock_page(page);
    		goto fail;
    	}
  • ¶

    Get the address for the page.

    	kaddr = kmap_atomic(page);
  • ¶

    Zero out the page's data.

    	memset(kaddr, 0, PAGE_CACHE_SIZE);
  • ¶

    Cast that address to a directory entry.

    	de = (xiafs_dirent *)kaddr;
  • ¶

    Make the "." and ".." entries that every directory has.

    	de->d_ino = inode->i_ino;
    	strcpy(de->d_name, ".");
    	de->d_name_len = 1;
    	de->d_rec_len = 12;
    	de = xiafs_next_entry(de);
  • ¶

    "..", of course, refers to the parent directory and shares the same inode.

    	de->d_ino = dir->i_ino;
    	strcpy(de->d_name, "..");
    	de->d_name_len = 2;
  • ¶

    ".."'s record length extends to the end of the 1k block, except for the space used by ".".

    	de->d_rec_len = zsize - 12;
    	kunmap_atomic(kaddr);
  • ¶

    Commit and return.

    	err = dir_commit_chunk(page, 0, zsize);
    fail:
    	page_cache_release(page);
    	return err;
    }
  • ¶

    As the comment here indicates, check to see that the specified directory is empty.

    /*
     * routine to check that the specified directory is empty (for rmdir)
     */
    int xiafs_empty_dir(struct inode * inode)
    {
    	struct page *page = NULL;
    	unsigned long i, npages = dir_pages(inode);
    	char *name;
    	__u32 inumber;
  • ¶

    This is the same process for walking through the directory's pages that's in many of the other functions in this file.

    	for (i = 0; i < npages; i++) {
    		char *p, *kaddr, *limit;
    
    		page = dir_get_page(inode, i);
    		if (IS_ERR(page))
    			continue;
    
    		kaddr = (char *)page_address(page);
    		limit = kaddr + xiafs_last_byte(inode, i) - _XIAFS_DIR_SIZE;
  • ¶

    Loop over each dentry in the page

    		for (p = kaddr; p <= limit; p = xiafs_next_entry(p)) {
    			xiafs_dirent *de = (xiafs_dirent *)p;
  • ¶

    Even before checking the inode number, check to see if the entry is so messed up that d_rec_len is 0.

    			if (de->d_rec_len == 0){
    				printk("XIAFS: Zero-length directory entry at (%s %d)\n", WHERE_ERR);
    				dir_put_page(page);
    				return -EIO;
    			}
    			name = de->d_name;
    			inumber = de->d_ino;
  • ¶

    If the inode number is 0, that means that for our purposes the entry doesn't exist. There are some situations where an entry has been deleted, but the previous entry couldn't be extended to cover that entry's space in the dentry list. If the inode number isn't zero, though, it needs to be checked for being anything other than "." and ".."

    			if (inumber != 0) {
    				/* check for . and .. */
  • ¶

    Obviously if the first character in the name isn't ".", it isn't "." or "..".

    				if (name[0] != '.')
    					goto not_empty;
  • ¶

    This would be a weird condition to satisfy, if the entry's name is "." but the inode number of the entry wasn't the directory's inode. Still, this condition gets checked.

    				if (!name[1]) {
    					if (inumber != inode->i_ino)
    						goto not_empty;
  • ¶

    If it's not ".." it's not empty.

    				} else if (name[1] != '.')
    					goto not_empty;
  • ¶

    If there's a third character of any sort, it's obviously neither "." nor "..".

    				else if (name[2])
    					goto not_empty;
    			}
    		}
    		dir_put_page(page);
    	}
  • ¶

    The directory turned out to be empty.

    	return 1;
  • ¶

    If the directory wasn't empty, it jumps here with a goto, puts the page back, and returns false.

    not_empty:
    	dir_put_page(page);
    	return 0;
    }
  • ¶

    This is only called by xiafs_rename in namei.c. As the name implies, it sets the link for the entry and inode.

    /* Releases the page */
    void xiafs_set_link(struct xiafs_direct *de, struct page *page,
    	struct inode *inode)
    {
  • ¶

    Get the directory the entry's being added to.

    	struct address_space *mapping = page->mapping;
    	struct inode *dir = mapping->host;
  • ¶

    Get the position on the page.

    	loff_t pos = page_offset(page) +
    			(char *)de-(char*)page_address(page);
    	int err;
  • ¶

    Lock the page and prepare the chunk for writing.

    	lock_page(page);
    
    	err = xiafs_prepare_chunk(page, pos, de->d_rec_len);
    	if (err == 0) {
  • ¶

    If all went well, set the inode number and commit the chunk.

    		de->d_ino = inode->i_ino;
    		err = dir_commit_chunk(page, pos, de->d_rec_len);
    	} else {
    		unlock_page(page);
    	}
  • ¶

    Release the page, set modified and change times on the host directory, and mark the directory as dirty.

    	dir_put_page(page);
    	dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
    	mark_inode_dirty(dir);
    }
  • ¶

    Get the parent directory of this inode.

    struct xiafs_direct * xiafs_dotdot (struct inode *dir, struct page **p)
    {
    	struct page *page = dir_get_page(dir, 0);
    	struct xiafs_direct *de = NULL;
    
    	if (!IS_ERR(page)) {
  • ¶

    ".." is always the second entry in the list.

    		de = xiafs_next_entry(page_address(page));
  • ¶

    And set the double pointer **p for this node's page.

    		*p = page;
    	}
    	return de;
    }
  • ¶

    Wrapper function around xiafs_find_entry that returns the inode number for the desired directory entry.

    ino_t xiafs_inode_by_name(struct dentry *dentry)
    {
    	struct page *page;
    	struct xiafs_direct *de = xiafs_find_entry(dentry, &page, NULL);
    	ino_t res = 0;
    
    	if (de) {
    		res = de->d_ino;
    		dir_put_page(page);
    	}
    	return res;
    }