| /* |
| * JFFS -- Journaling Flash File System, Linux implementation. |
| * |
| * Copyright (C) 2000 Axis Communications AB. |
| * |
| * Created by Simon Kagstrom <simonk@axis.com>. |
| * |
| * $Id: jffs_proc.c,v 1.5 2001/06/02 14:34:55 dwmw2 Exp $ |
| * |
| * This is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * Overview: |
| * This file defines JFFS partition entries in the proc file system. |
| * |
| * TODO: |
| * Create some more proc files for different kinds of info, i.e. statistics |
| * about written and read bytes, number of calls to different routines, |
| * reports about failures. |
| */ |
| |
| #include <linux/errno.h> |
| #include <linux/fs.h> |
| #include <linux/jffs.h> |
| #include <linux/slab.h> |
| #include <linux/proc_fs.h> |
| #include <linux/time.h> |
| #include <linux/types.h> |
| #include "jffs_fm.h" |
| #include "jffs_proc.h" |
| |
| /* |
| * Structure for a JFFS partition in the system |
| */ |
| struct jffs_partition_dir { |
| struct jffs_control *c; |
| struct proc_dir_entry *part_root; |
| struct proc_dir_entry *part_info; |
| struct proc_dir_entry *part_layout; |
| struct jffs_partition_dir *next; |
| }; |
| |
| /* |
| * Structure for top-level entry in '/proc/fs' directory |
| */ |
| struct proc_dir_entry *jffs_proc_root; |
| |
| /* |
| * Linked list of 'jffs_partition_dirs' to help us track |
| * the mounted JFFS partitions in the system |
| */ |
| static struct jffs_partition_dir *jffs_part_dirs; |
| |
| /* |
| * Read functions for entries |
| */ |
| static int jffs_proc_info_read(char *page, char **start, off_t off, |
| int count, int *eof, void *data); |
| static int jffs_proc_layout_read (char *page, char **start, off_t off, |
| int count, int *eof, void *data); |
| |
| |
| /* |
| * Register a JFFS partition directory (called upon mount) |
| */ |
| int jffs_register_jffs_proc_dir(int mtd, struct jffs_control *c) |
| { |
| struct jffs_partition_dir *part_dir; |
| struct proc_dir_entry *part_info = NULL; |
| struct proc_dir_entry *part_layout = NULL; |
| struct proc_dir_entry *part_root = NULL; |
| char name[10]; |
| |
| sprintf(name, "%d", mtd); |
| /* Allocate structure for local JFFS partition table */ |
| part_dir = (struct jffs_partition_dir *) |
| kmalloc(sizeof (struct jffs_partition_dir), GFP_KERNEL); |
| if (!part_dir) |
| goto out; |
| |
| /* Create entry for this partition */ |
| part_root = proc_mkdir(name, jffs_proc_root); |
| if (!part_root) |
| goto out1; |
| |
| /* Create entry for 'info' file */ |
| part_info = create_proc_entry ("info", 0, part_root); |
| if (!part_info) |
| goto out2; |
| part_info->read_proc = jffs_proc_info_read; |
| part_info->data = (void *) c; |
| |
| /* Create entry for 'layout' file */ |
| part_layout = create_proc_entry ("layout", 0, part_root); |
| if (!part_layout) |
| goto out3; |
| part_layout->read_proc = jffs_proc_layout_read; |
| part_layout->data = (void *) c; |
| |
| /* Fill in structure for table and insert in the list */ |
| part_dir->c = c; |
| part_dir->part_root = part_root; |
| part_dir->part_info = part_info; |
| part_dir->part_layout = part_layout; |
| part_dir->next = jffs_part_dirs; |
| jffs_part_dirs = part_dir; |
| |
| /* Return happy */ |
| return 0; |
| |
| out3: |
| remove_proc_entry("info", part_root); |
| out2: |
| remove_proc_entry(name, jffs_proc_root); |
| out1: |
| kfree(part_dir); |
| out: |
| return -ENOMEM; |
| } |
| |
| |
| /* |
| * Unregister a JFFS partition directory (called at umount) |
| */ |
| int jffs_unregister_jffs_proc_dir(struct jffs_control *c) |
| { |
| struct jffs_partition_dir *part_dir = jffs_part_dirs; |
| struct jffs_partition_dir *prev_part_dir = NULL; |
| |
| while (part_dir) { |
| if (part_dir->c == c) { |
| /* Remove entries for partition */ |
| remove_proc_entry (part_dir->part_info->name, |
| part_dir->part_root); |
| remove_proc_entry (part_dir->part_layout->name, |
| part_dir->part_root); |
| remove_proc_entry (part_dir->part_root->name, |
| jffs_proc_root); |
| |
| /* Remove entry from list */ |
| if (prev_part_dir) |
| prev_part_dir->next = part_dir->next; |
| else |
| jffs_part_dirs = part_dir->next; |
| |
| /* |
| * Check to see if this is the last one |
| * and remove the entry from '/proc/fs' |
| * if it is. |
| */ |
| if (jffs_part_dirs == part_dir->next) |
| remove_proc_entry ("jffs", proc_root_fs); |
| |
| /* Free memory for entry */ |
| kfree(part_dir); |
| |
| /* Return happy */ |
| return 0; |
| } |
| |
| /* Move to next entry */ |
| prev_part_dir = part_dir; |
| part_dir = part_dir->next; |
| } |
| |
| /* Return unhappy */ |
| return -1; |
| } |
| |
| |
| /* |
| * Read a JFFS partition's `info' file |
| */ |
| static int jffs_proc_info_read (char *page, char **start, off_t off, |
| int count, int *eof, void *data) |
| { |
| struct jffs_control *c = (struct jffs_control *) data; |
| int len = 0; |
| |
| /* Get information on the parition */ |
| len += sprintf (page, |
| "partition size: %08lX (%u)\n" |
| "sector size: %08lX (%u)\n" |
| "used size: %08lX (%u)\n" |
| "dirty size: %08lX (%u)\n" |
| "free size: %08lX (%u)\n\n", |
| (unsigned long) c->fmc->flash_size, c->fmc->flash_size, |
| (unsigned long) c->fmc->sector_size, c->fmc->sector_size, |
| (unsigned long) c->fmc->used_size, c->fmc->used_size, |
| (unsigned long) c->fmc->dirty_size, c->fmc->dirty_size, |
| (unsigned long) (c->fmc->flash_size - |
| (c->fmc->used_size + c->fmc->dirty_size)), |
| c->fmc->flash_size - (c->fmc->used_size + c->fmc->dirty_size)); |
| |
| /* We're done */ |
| *eof = 1; |
| |
| /* Return length */ |
| return len; |
| } |
| |
| |
| /* |
| * Read a JFFS partition's `layout' file |
| */ |
| static int jffs_proc_layout_read (char *page, char **start, off_t off, |
| int count, int *eof, void *data) |
| { |
| struct jffs_control *c = (struct jffs_control *) data; |
| struct jffs_fm *fm = NULL; |
| struct jffs_fm *last_fm = NULL; |
| int len = 0; |
| |
| /* Get the first item in the list */ |
| fm = c->fmc->head; |
| |
| /* Print free space */ |
| if (fm && fm->offset) { |
| len += sprintf (page, "00000000 %08lX free\n", |
| (unsigned long) fm->offset); |
| } |
| |
| /* Loop through all of the flash control structures */ |
| while (fm && (len < (off + count))) { |
| if (fm->nodes) { |
| len += sprintf (page + len, |
| "%08lX %08lX ino=%08lX, ver=%08lX\n", |
| (unsigned long) fm->offset, |
| (unsigned long) fm->size, |
| (unsigned long) fm->nodes->node->ino, |
| (unsigned long) fm->nodes->node->version); |
| } |
| else { |
| len += sprintf (page + len, |
| "%08lX %08lX dirty\n", |
| (unsigned long) fm->offset, |
| (unsigned long) fm->size); |
| } |
| last_fm = fm; |
| fm = fm->next; |
| } |
| |
| /* Print free space */ |
| if ((len < (off + count)) && last_fm |
| && (last_fm->offset < c->fmc->flash_size)) { |
| len += sprintf (page + len, |
| "%08lX %08lX free\n", |
| (unsigned long) last_fm->offset + |
| last_fm->size, |
| (unsigned long) (c->fmc->flash_size - |
| (last_fm->offset + last_fm->size))); |
| } |
| |
| /* We're done */ |
| *eof = 1; |
| |
| /* Return length */ |
| return len; |
| } |