+ struct inode *inode = dentry->d_inode;
+ int error = 0;
+ yaffs_Device *dev;
+
+ T(YAFFS_TRACE_OS,
+ (TSTR("yaffs_setattr of object %d\n"),
+ yaffs_InodeToObject(inode)->objectId));
+
+ /* Fail if a requested resize >= 2GB */
+ if (attr->ia_valid & ATTR_SIZE &&
+ (attr->ia_size >> 31))
+ error = -EINVAL;
+
+ if (error == 0)
+ error = inode_change_ok(inode, attr);
+ if (error == 0) {
+ int result;
+ if (!error){
+ error = yaffs_vfs_setattr(inode, attr);
+ T(YAFFS_TRACE_OS,(TSTR("inode_setattr called\n")));
+ if (attr->ia_valid & ATTR_SIZE){
+ yaffs_vfs_setsize(inode,attr->ia_size);
+ inode->i_blocks = (inode->i_size + 511) >> 9;
+ }
+ }
+ dev = yaffs_InodeToObject(inode)->myDev;
+ if (attr->ia_valid & ATTR_SIZE){
+ T(YAFFS_TRACE_OS,(TSTR("resize to %d(%x)\n"),
+ (int)(attr->ia_size),(int)(attr->ia_size)));
+ }
+ yaffs_GrossLock(dev);
+ result = yaffs_SetAttributes(yaffs_InodeToObject(inode), attr);
+ if(result == YAFFS_OK) {
+ error = 0;
+ } else {
+ error = -EPERM;
+ }
+ yaffs_GrossUnlock(dev);
+
+ }
+
+ T(YAFFS_TRACE_OS,
+ (TSTR("yaffs_setattr done returning %d\n"),error));
+
+ return error;
+}
+
+#ifdef CONFIG_YAFFS_XATTR
+int yaffs_setxattr(struct dentry *dentry, const char *name,
+ const void *value, size_t size, int flags)
+{
+ struct inode *inode = dentry->d_inode;
+ int error = 0;
+ yaffs_Device *dev;
+ yaffs_Object *obj = yaffs_InodeToObject(inode);
+
+ T(YAFFS_TRACE_OS,
+ (TSTR("yaffs_setxattr of object %d\n"),
+ obj->objectId));
+
+
+ if (error == 0) {
+ int result;
+ dev = obj->myDev;
+ yaffs_GrossLock(dev);
+ result = yaffs_SetXAttribute(obj, name, value, size, flags);
+ if(result == YAFFS_OK)
+ error = 0;
+ else if(result < 0)
+ error = result;
+ yaffs_GrossUnlock(dev);
+
+ }
+ T(YAFFS_TRACE_OS,
+ (TSTR("yaffs_setxattr done returning %d\n"),error));
+
+ return error;
+}
+
+
+ssize_t yaffs_getxattr(struct dentry *dentry, const char *name, void *buff,
+ size_t size)
+{
+ struct inode *inode = dentry->d_inode;
+ int error = 0;
+ yaffs_Device *dev;
+ yaffs_Object *obj = yaffs_InodeToObject(inode);
+
+ T(YAFFS_TRACE_OS,
+ (TSTR("yaffs_getxattr \"%s\" from object %d\n"),
+ name, obj->objectId));
+
+ if (error == 0) {
+ dev = obj->myDev;
+ yaffs_GrossLock(dev);
+ error = yaffs_GetXAttribute(obj, name, buff, size);
+ yaffs_GrossUnlock(dev);
+
+ }
+ T(YAFFS_TRACE_OS,
+ (TSTR("yaffs_getxattr done returning %d\n"),error));
+
+ return error;
+}
+
+int yaffs_removexattr(struct dentry *dentry, const char *name)
+{
+ struct inode *inode = dentry->d_inode;
+ int error = 0;
+ yaffs_Device *dev;
+ yaffs_Object *obj = yaffs_InodeToObject(inode);
+
+ T(YAFFS_TRACE_OS,
+ (TSTR("yaffs_removexattr of object %d\n"),
+ obj->objectId));
+
+
+ if (error == 0) {
+ int result;
+ dev = obj->myDev;
+ yaffs_GrossLock(dev);
+ result = yaffs_RemoveXAttribute(obj, name);
+ if(result == YAFFS_OK)
+ error = 0;
+ else if(result < 0)
+ error = result;
+ yaffs_GrossUnlock(dev);
+
+ }
+ T(YAFFS_TRACE_OS,
+ (TSTR("yaffs_removexattr done returning %d\n"),error));
+
+ return error;
+}
+
+ssize_t yaffs_listxattr(struct dentry *dentry, char *buff, size_t size)
+{
+ struct inode *inode = dentry->d_inode;
+ int error = 0;
+ yaffs_Device *dev;
+ yaffs_Object *obj = yaffs_InodeToObject(inode);
+
+ T(YAFFS_TRACE_OS,
+ (TSTR("yaffs_listxattr of object %d\n"),
+ obj->objectId));
+
+
+ if (error == 0) {
+ dev = obj->myDev;
+ yaffs_GrossLock(dev);
+ error = yaffs_ListXAttributes(obj, buff, size);
+ yaffs_GrossUnlock(dev);
+
+ }
+ T(YAFFS_TRACE_OS,
+ (TSTR("yaffs_listxattr done returning %d\n"),error));
+
+ return error;
+}
+
+#endif
+
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17))
+static int yaffs_statfs(struct dentry *dentry, struct kstatfs *buf)
+{
+ yaffs_Device *dev = yaffs_DentryToObject(dentry)->myDev;
+ struct super_block *sb = dentry->d_sb;
+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0))
+static int yaffs_statfs(struct super_block *sb, struct kstatfs *buf)
+{
+ yaffs_Device *dev = yaffs_SuperToDevice(sb);
+#else
+static int yaffs_statfs(struct super_block *sb, struct statfs *buf)
+{
+ yaffs_Device *dev = yaffs_SuperToDevice(sb);
+#endif
+
+ T(YAFFS_TRACE_OS, (TSTR("yaffs_statfs\n")));
+
+ yaffs_GrossLock(dev);
+
+ buf->f_type = YAFFS_MAGIC;
+ buf->f_bsize = sb->s_blocksize;
+ buf->f_namelen = 255;
+
+ if (dev->nDataBytesPerChunk & (dev->nDataBytesPerChunk - 1)) {
+ /* Do this if chunk size is not a power of 2 */
+
+ uint64_t bytesInDev;
+ uint64_t bytesFree;
+
+ bytesInDev = ((uint64_t)((dev->param.endBlock - dev->param.startBlock + 1))) *
+ ((uint64_t)(dev->param.nChunksPerBlock * dev->nDataBytesPerChunk));
+
+ do_div(bytesInDev, sb->s_blocksize); /* bytesInDev becomes the number of blocks */
+ buf->f_blocks = bytesInDev;
+
+ bytesFree = ((uint64_t)(yaffs_GetNumberOfFreeChunks(dev))) *
+ ((uint64_t)(dev->nDataBytesPerChunk));
+
+ do_div(bytesFree, sb->s_blocksize);
+
+ buf->f_bfree = bytesFree;
+
+ } else if (sb->s_blocksize > dev->nDataBytesPerChunk) {
+
+ buf->f_blocks =
+ (dev->param.endBlock - dev->param.startBlock + 1) *
+ dev->param.nChunksPerBlock /
+ (sb->s_blocksize / dev->nDataBytesPerChunk);
+ buf->f_bfree =
+ yaffs_GetNumberOfFreeChunks(dev) /
+ (sb->s_blocksize / dev->nDataBytesPerChunk);
+ } else {
+ buf->f_blocks =
+ (dev->param.endBlock - dev->param.startBlock + 1) *
+ dev->param.nChunksPerBlock *
+ (dev->nDataBytesPerChunk / sb->s_blocksize);
+
+ buf->f_bfree =
+ yaffs_GetNumberOfFreeChunks(dev) *
+ (dev->nDataBytesPerChunk / sb->s_blocksize);
+ }
+
+ buf->f_files = 0;
+ buf->f_ffree = 0;
+ buf->f_bavail = buf->f_bfree;
+
+ yaffs_GrossUnlock(dev);
+ return 0;
+}
+
+
+
+static void yaffs_FlushInodes(struct super_block *sb)
+{
+ struct inode *iptr;
+ yaffs_Object *obj;
+
+ list_for_each_entry(iptr,&sb->s_inodes, i_sb_list){
+ obj = yaffs_InodeToObject(iptr);
+ if(obj){
+ T(YAFFS_TRACE_OS, (TSTR("flushing obj %d\n"),
+ obj->objectId));
+ yaffs_FlushFile(obj,1,0);
+ }
+ }
+}
+
+
+static void yaffs_FlushSuperBlock(struct super_block *sb, int do_checkpoint)
+{
+ yaffs_Device *dev = yaffs_SuperToDevice(sb);
+ if(!dev)
+ return;
+
+ yaffs_FlushInodes(sb);
+ yaffs_UpdateDirtyDirectories(dev);
+ yaffs_FlushEntireDeviceCache(dev);
+ if(do_checkpoint)
+ yaffs_CheckpointSave(dev);
+}
+
+
+static unsigned yaffs_bg_gc_urgency(yaffs_Device *dev)
+{
+ unsigned erasedChunks = dev->nErasedBlocks * dev->param.nChunksPerBlock;
+ struct yaffs_LinuxContext *context = yaffs_DeviceToLC(dev);
+ unsigned scatteredFree = 0; /* Free chunks not in an erased block */
+
+ if(erasedChunks < dev->nFreeChunks)
+ scatteredFree = (dev->nFreeChunks - erasedChunks);
+
+ if(!context->bgRunning)
+ return 0;
+ else if(scatteredFree < (dev->param.nChunksPerBlock * 2))
+ return 0;
+ else if(erasedChunks > dev->nFreeChunks/2)
+ return 0;
+ else if(erasedChunks > dev->nFreeChunks/4)
+ return 1;
+ else
+ return 2;
+}
+
+static int yaffs_do_sync_fs(struct super_block *sb,
+ int request_checkpoint)
+{
+
+ yaffs_Device *dev = yaffs_SuperToDevice(sb);
+ unsigned int oneshot_checkpoint = (yaffs_auto_checkpoint & 4);
+ unsigned gc_urgent = yaffs_bg_gc_urgency(dev);
+ int do_checkpoint;
+
+ T(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC | YAFFS_TRACE_BACKGROUND,
+ (TSTR("yaffs_do_sync_fs: gc-urgency %d %s %s%s\n"),
+ gc_urgent,
+ sb->s_dirt ? "dirty" : "clean",
+ request_checkpoint ? "checkpoint requested" : "no checkpoint",
+ oneshot_checkpoint ? " one-shot" : "" ));
+
+ yaffs_GrossLock(dev);
+ do_checkpoint = ((request_checkpoint && !gc_urgent) ||
+ oneshot_checkpoint) &&
+ !dev->isCheckpointed;
+
+ if (sb->s_dirt || do_checkpoint) {
+ yaffs_FlushSuperBlock(sb, !dev->isCheckpointed && do_checkpoint);
+ sb->s_dirt = 0;
+ if(oneshot_checkpoint)
+ yaffs_auto_checkpoint &= ~4;
+ }
+ yaffs_GrossUnlock(dev);
+
+ return 0;
+}
+
+/*
+ * yaffs background thread functions .
+ * yaffs_BackgroundThread() the thread function
+ * yaffs_BackgroundStart() launches the background thread.
+ * yaffs_BackgroundStop() cleans up the background thread.
+ *
+ * NB:
+ * The thread should only run after the yaffs is initialised
+ * The thread should be stopped before yaffs is unmounted.
+ * The thread should not do any writing while the fs is in read only.
+ */
+
+#ifdef YAFFS_COMPILE_BACKGROUND
+
+void yaffs_background_waker(unsigned long data)
+{
+ wake_up_process((struct task_struct *)data);
+}
+
+static int yaffs_BackgroundThread(void *data)
+{
+ yaffs_Device *dev = (yaffs_Device *)data;
+ struct yaffs_LinuxContext *context = yaffs_DeviceToLC(dev);
+ unsigned long now = jiffies;
+ unsigned long next_dir_update = now;
+ unsigned long next_gc = now;
+ unsigned long expires;
+ unsigned int urgency;
+
+ int gcResult;
+ struct timer_list timer;
+
+ T(YAFFS_TRACE_BACKGROUND,
+ (TSTR("yaffs_background starting for dev %p\n"),
+ (void *)dev));
+
+ set_freezable();
+
+ while(context->bgRunning){
+ T(YAFFS_TRACE_BACKGROUND,
+ (TSTR("yaffs_background\n")));
+
+ if(kthread_should_stop())
+ break;
+
+ if(try_to_freeze())
+ continue;
+
+ yaffs_GrossLock(dev);
+
+ now = jiffies;
+
+ if(time_after(now, next_dir_update) && yaffs_bg_enable){
+ yaffs_UpdateDirtyDirectories(dev);
+ next_dir_update = now + HZ;
+ }
+
+ if(time_after(now,next_gc) && yaffs_bg_enable){
+ if(!dev->isCheckpointed){
+ urgency = yaffs_bg_gc_urgency(dev);
+ gcResult = yaffs_BackgroundGarbageCollect(dev, urgency);
+ if(urgency > 1)
+ next_gc = now + HZ/20+1;
+ else if(urgency > 0)
+ next_gc = now + HZ/10+1;
+ else
+ next_gc = now + HZ * 2;
+ } else /*
+ * gc not running so set to next_dir_update
+ * to cut down on wake ups
+ */
+ next_gc = next_dir_update;
+ }
+ yaffs_GrossUnlock(dev);
+#if 1
+ expires = next_dir_update;
+ if (time_before(next_gc,expires))
+ expires = next_gc;
+ if(time_before(expires,now))
+ expires = now + HZ;
+
+ init_timer_on_stack(&timer);
+ timer.expires = expires+1;
+ timer.data = (unsigned long) current;
+ timer.function = yaffs_background_waker;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ add_timer(&timer);
+ schedule();
+ del_timer_sync(&timer);
+#else
+ msleep(10);
+#endif
+ }
+
+ return 0;
+}
+
+static int yaffs_BackgroundStart(yaffs_Device *dev)
+{
+ int retval = 0;
+ struct yaffs_LinuxContext *context = yaffs_DeviceToLC(dev);
+
+ if(dev->readOnly)
+ return -1;
+
+ context->bgRunning = 1;
+
+ context->bgThread = kthread_run(yaffs_BackgroundThread,
+ (void *)dev,"yaffs-bg-%d",context->mount_id);
+
+ if(IS_ERR(context->bgThread)){
+ retval = PTR_ERR(context->bgThread);
+ context->bgThread = NULL;
+ context->bgRunning = 0;
+ }
+ return retval;
+}
+
+static void yaffs_BackgroundStop(yaffs_Device *dev)
+{
+ struct yaffs_LinuxContext *ctxt = yaffs_DeviceToLC(dev);
+
+ ctxt->bgRunning = 0;
+
+ if( ctxt->bgThread){
+ kthread_stop(ctxt->bgThread);
+ ctxt->bgThread = NULL;
+ }
+}
+#else
+static int yaffs_BackgroundThread(void *data)
+{
+ return 0;
+}
+
+static int yaffs_BackgroundStart(yaffs_Device *dev)
+{
+ return 0;
+}
+
+static void yaffs_BackgroundStop(yaffs_Device *dev)
+{
+}
+#endif
+
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17))
+static void yaffs_write_super(struct super_block *sb)
+#else
+static int yaffs_write_super(struct super_block *sb)
+#endif
+{
+ unsigned request_checkpoint = (yaffs_auto_checkpoint >= 2);