-static int yaffs_readpage_unlock(struct file *f, struct page *pg)
-{
- int ret = yaffs_readpage_nolock(f, pg);
- UnlockPage(pg);
- return ret;
-}
-
-static int yaffs_readpage(struct file *f, struct page *pg)
-{
- int ret;
-
- T(YAFFS_TRACE_OS, (TSTR("yaffs_readpage\n")));
- ret=yaffs_readpage_unlock(f, pg);
- T(YAFFS_TRACE_OS, (TSTR("yaffs_readpage done\n")));
- return ret;
-}
-
-/* writepage inspired by/stolen from smbfs */
-
-static int yaffs_writepage(struct page *page, struct writeback_control *wbc)
-{
- yaffs_dev_t *dev;
- struct address_space *mapping = page->mapping;
- struct inode *inode;
- unsigned long end_index;
- char *buffer;
- yaffs_obj_t *obj;
- int n_written = 0;
- unsigned n_bytes;
- loff_t i_size;
-
- if (!mapping)
- BUG();
- inode = mapping->host;
- if (!inode)
- BUG();
- i_size = i_size_read(inode);
-
- end_index = i_size >> PAGE_CACHE_SHIFT;
-
- if(page->index < end_index)
- n_bytes = PAGE_CACHE_SIZE;
- else {
- n_bytes = i_size & (PAGE_CACHE_SIZE -1);
-
- if (page->index > end_index || !n_bytes) {
- T(YAFFS_TRACE_OS,
- (TSTR("yaffs_writepage at %08x, inode size = %08x!!!\n"),
- (unsigned)(page->index << PAGE_CACHE_SHIFT),
- (unsigned)inode->i_size));
- T(YAFFS_TRACE_OS,
- (TSTR(" -> don't care!!\n")));
-
- zero_user_segment(page,0,PAGE_CACHE_SIZE);
- set_page_writeback(page);
- unlock_page(page);
- end_page_writeback(page);
- return 0;
- }
- }
-
- if(n_bytes != PAGE_CACHE_SIZE)
- zero_user_segment(page,n_bytes,PAGE_CACHE_SIZE);
-
- get_page(page);
-
- buffer = kmap(page);
-
- obj = yaffs_inode_to_obj(inode);
- dev = obj->my_dev;
- yaffs_gross_lock(dev);
-
- T(YAFFS_TRACE_OS,
- (TSTR("yaffs_writepage at %08x, size %08x\n"),
- (unsigned)(page->index << PAGE_CACHE_SHIFT), n_bytes));
- T(YAFFS_TRACE_OS,
- (TSTR("writepag0: obj = %05x, ino = %05x\n"),
- (int)obj->variant.file_variant.file_size, (int)inode->i_size));
-
- n_written = yaffs_wr_file(obj, buffer,
- page->index << PAGE_CACHE_SHIFT, n_bytes, 0);
-
- yaffs_touch_super(dev);
-
- T(YAFFS_TRACE_OS,
- (TSTR("writepag1: obj = %05x, ino = %05x\n"),
- (int)obj->variant.file_variant.file_size, (int)inode->i_size));
-
- yaffs_gross_unlock(dev);
-
- kunmap(page);
- set_page_writeback(page);
- unlock_page(page);
- end_page_writeback(page);
- put_page(page);
-
- return (n_written == n_bytes) ? 0 : -ENOSPC;
-}
-
-
-static int yaffs_write_begin(struct file *filp, struct address_space *mapping,
- loff_t pos, unsigned len, unsigned flags,
- struct page **pagep, void **fsdata)
-{
- struct page *pg = NULL;
- pgoff_t index = pos >> PAGE_CACHE_SHIFT;
-
- int ret = 0;
- int space_held = 0;
-
- /* Get a page */
- pg = grab_cache_page_write_begin(mapping, index, flags);
-
- *pagep = pg;
- if (!pg) {
- ret = -ENOMEM;
- goto out;
- }
- T(YAFFS_TRACE_OS,
- (TSTR("start yaffs_write_begin index %d(%x) uptodate %d\n"),
- (int)index,(int)index,Page_Uptodate(pg) ? 1 : 0));
-
- /* Get fs space */
- space_held = yaffs_hold_space(filp);
-
- if (!space_held) {
- ret = -ENOSPC;
- goto out;
- }
-
- /* Update page if required */
-
- if (!Page_Uptodate(pg))
- ret = yaffs_readpage_nolock(filp, pg);
-
- if (ret)
- goto out;
-
- /* Happy path return */
- T(YAFFS_TRACE_OS, (TSTR("end yaffs_write_begin - ok\n")));
-
- return 0;
-
-out:
- T(YAFFS_TRACE_OS,
- (TSTR("end yaffs_write_begin fail returning %d\n"), ret));
- if (space_held)
- yaffs_release_space(filp);
- if (pg) {
- unlock_page(pg);
- page_cache_release(pg);
- }
- return ret;
-}
-
-
-static int yaffs_write_end(struct file *filp, struct address_space *mapping,
- loff_t pos, unsigned len, unsigned copied,
- struct page *pg, void *fsdadata)
-{
- int ret = 0;
- void *addr, *kva;
- uint32_t offset_into_page = pos & (PAGE_CACHE_SIZE - 1);
-
- kva = kmap(pg);
- addr = kva + offset_into_page;
-
- T(YAFFS_TRACE_OS,
- ("yaffs_write_end addr %p pos %x n_bytes %d\n",
- addr,(unsigned)pos, copied));
-
- ret = yaffs_file_write(filp, addr, copied, &pos);
-
- if (ret != copied) {
- T(YAFFS_TRACE_OS,
- (TSTR("yaffs_write_end not same size ret %d copied %d\n"),
- ret, copied));
- SetPageError(pg);
- } else {
- /* Nothing */
- }
-
- kunmap(pg);
-
- yaffs_release_space(filp);
- unlock_page(pg);
- page_cache_release(pg);
- return ret;
-}
-
-
-static void yaffs_fill_inode_from_obj(struct inode *inode, yaffs_obj_t *obj)
-{
- if (inode && obj) {
-
-
- /* Check mode against the variant type and attempt to repair if broken. */
- __u32 mode = obj->yst_mode;
- switch (obj->variant_type) {
- case YAFFS_OBJECT_TYPE_FILE:
- if (!S_ISREG(mode)) {
- obj->yst_mode &= ~S_IFMT;
- obj->yst_mode |= S_IFREG;
- }
-
- break;
- case YAFFS_OBJECT_TYPE_SYMLINK:
- if (!S_ISLNK(mode)) {
- obj->yst_mode &= ~S_IFMT;
- obj->yst_mode |= S_IFLNK;
- }
-
- break;
- case YAFFS_OBJECT_TYPE_DIRECTORY:
- if (!S_ISDIR(mode)) {
- obj->yst_mode &= ~S_IFMT;
- obj->yst_mode |= S_IFDIR;
- }
-
- break;
- case YAFFS_OBJECT_TYPE_UNKNOWN:
- case YAFFS_OBJECT_TYPE_HARDLINK:
- case YAFFS_OBJECT_TYPE_SPECIAL:
- default:
- /* TODO? */
- break;
- }
-
- inode->i_flags |= S_NOATIME;
-
- inode->i_ino = obj->obj_id;
- inode->i_mode = obj->yst_mode;
- inode->i_uid = obj->yst_uid;
- inode->i_gid = obj->yst_gid;
-
- inode->i_rdev = old_decode_dev(obj->yst_rdev);
-
- inode->i_atime.tv_sec = (time_t) (obj->yst_atime);
- inode->i_atime.tv_nsec = 0;
- inode->i_mtime.tv_sec = (time_t) obj->yst_mtime;
- inode->i_mtime.tv_nsec = 0;
- inode->i_ctime.tv_sec = (time_t) obj->yst_ctime;
- inode->i_ctime.tv_nsec = 0;
- inode->i_size = yaffs_get_obj_length(obj);
- inode->i_blocks = (inode->i_size + 511) >> 9;
-
- inode->i_nlink = yaffs_get_obj_link_count(obj);
-
- T(YAFFS_TRACE_OS,
- (TSTR("yaffs_fill_inode mode %x uid %d gid %d size %d count %d\n"),
- inode->i_mode, inode->i_uid, inode->i_gid,
- (int)inode->i_size, atomic_read(&inode->i_count)));
-
- switch (obj->yst_mode & S_IFMT) {
- default: /* fifo, device or socket */
- init_special_inode(inode, obj->yst_mode,
- old_decode_dev(obj->yst_rdev));
- break;
- case S_IFREG: /* file */
- inode->i_op = &yaffs_file_inode_operations;
- inode->i_fop = &yaffs_file_operations;
- inode->i_mapping->a_ops =
- &yaffs_file_address_operations;
- break;
- case S_IFDIR: /* directory */
- inode->i_op = &yaffs_dir_inode_operations;
- inode->i_fop = &yaffs_dir_operations;
- break;
- case S_IFLNK: /* symlink */
- inode->i_op = &yaffs_symlink_inode_operations;
- break;
- }
-
- yaffs_inode_to_obj_lv(inode) = obj;
-
- obj->my_inode = inode;
-
- } else {
- T(YAFFS_TRACE_OS,
- (TSTR("yaffs_fill_inode invalid parameters\n")));
- }
+static const struct inode_operations yaffs_dir_inode_operations = {
+ .create = yaffs_create,
+ .lookup = yaffs_lookup,
+ .link = yaffs_link,
+ .unlink = yaffs_unlink,
+ .symlink = yaffs_symlink,
+ .mkdir = yaffs_mkdir,
+ .rmdir = yaffs_unlink,
+ .mknod = yaffs_mknod,
+ .rename = yaffs_rename,
+ .setattr = yaffs_setattr,
+ .setxattr = yaffs_setxattr,
+ .getxattr = yaffs_getxattr,
+ .listxattr = yaffs_listxattr,
+ .removexattr = yaffs_removexattr,
+};
+/*-----------------------------------------------------------------*/
+/* Directory search context allows us to unlock access to yaffs during
+ * filldir without causing problems with the directory being modified.
+ * This is similar to the tried and tested mechanism used in yaffs direct.
+ *
+ * A search context iterates along a doubly linked list of siblings in the
+ * directory. If the iterating object is deleted then this would corrupt
+ * the list iteration, likely causing a crash. The search context avoids
+ * this by using the remove_obj_fn to move the search context to the
+ * next object before the object is deleted.
+ *
+ * Many readdirs (and thus seach conexts) may be alive simulateously so
+ * each struct yaffs_dev has a list of these.
+ *
+ * A seach context lives for the duration of a readdir.
+ *
+ * All these functions must be called while yaffs is locked.
+ */