Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * JFFS -- Journaling Flash File System, Linux implementation. | |
3 | * | |
4 | * Copyright (C) 2000 Axis Communications AB. | |
5 | * | |
6 | * Created by Simon Kagstrom <simonk@axis.com>. | |
7 | * | |
8 | * $Id: jffs_proc.c,v 1.5 2001/06/02 14:34:55 dwmw2 Exp $ | |
9 | * | |
10 | * This is free software; you can redistribute it and/or modify it | |
11 | * under the terms of the GNU General Public License as published by | |
12 | * the Free Software Foundation; either version 2 of the License, or | |
13 | * (at your option) any later version. | |
14 | * | |
15 | * Overview: | |
16 | * This file defines JFFS partition entries in the proc file system. | |
17 | * | |
18 | * TODO: | |
19 | * Create some more proc files for different kinds of info, i.e. statistics | |
20 | * about written and read bytes, number of calls to different routines, | |
21 | * reports about failures. | |
22 | */ | |
23 | ||
24 | #include <linux/errno.h> | |
25 | #include <linux/fs.h> | |
26 | #include <linux/jffs.h> | |
27 | #include <linux/slab.h> | |
28 | #include <linux/proc_fs.h> | |
29 | #include <linux/time.h> | |
30 | #include <linux/types.h> | |
31 | #include "jffs_fm.h" | |
32 | #include "jffs_proc.h" | |
33 | ||
34 | /* | |
35 | * Structure for a JFFS partition in the system | |
36 | */ | |
37 | struct jffs_partition_dir { | |
38 | struct jffs_control *c; | |
39 | struct proc_dir_entry *part_root; | |
40 | struct proc_dir_entry *part_info; | |
41 | struct proc_dir_entry *part_layout; | |
42 | struct jffs_partition_dir *next; | |
43 | }; | |
44 | ||
45 | /* | |
46 | * Structure for top-level entry in '/proc/fs' directory | |
47 | */ | |
48 | struct proc_dir_entry *jffs_proc_root; | |
49 | ||
50 | /* | |
51 | * Linked list of 'jffs_partition_dirs' to help us track | |
52 | * the mounted JFFS partitions in the system | |
53 | */ | |
54 | static struct jffs_partition_dir *jffs_part_dirs; | |
55 | ||
56 | /* | |
57 | * Read functions for entries | |
58 | */ | |
59 | static int jffs_proc_info_read(char *page, char **start, off_t off, | |
60 | int count, int *eof, void *data); | |
61 | static int jffs_proc_layout_read (char *page, char **start, off_t off, | |
62 | int count, int *eof, void *data); | |
63 | ||
64 | ||
65 | /* | |
66 | * Register a JFFS partition directory (called upon mount) | |
67 | */ | |
68 | int jffs_register_jffs_proc_dir(int mtd, struct jffs_control *c) | |
69 | { | |
70 | struct jffs_partition_dir *part_dir; | |
71 | struct proc_dir_entry *part_info = NULL; | |
72 | struct proc_dir_entry *part_layout = NULL; | |
73 | struct proc_dir_entry *part_root = NULL; | |
74 | char name[10]; | |
75 | ||
76 | sprintf(name, "%d", mtd); | |
77 | /* Allocate structure for local JFFS partition table */ | |
78 | part_dir = (struct jffs_partition_dir *) | |
79 | kmalloc(sizeof (struct jffs_partition_dir), GFP_KERNEL); | |
80 | if (!part_dir) | |
81 | goto out; | |
82 | ||
83 | /* Create entry for this partition */ | |
84 | part_root = proc_mkdir(name, jffs_proc_root); | |
85 | if (!part_root) | |
86 | goto out1; | |
87 | ||
88 | /* Create entry for 'info' file */ | |
89 | part_info = create_proc_entry ("info", 0, part_root); | |
90 | if (!part_info) | |
91 | goto out2; | |
92 | part_info->read_proc = jffs_proc_info_read; | |
93 | part_info->data = (void *) c; | |
94 | ||
95 | /* Create entry for 'layout' file */ | |
96 | part_layout = create_proc_entry ("layout", 0, part_root); | |
97 | if (!part_layout) | |
98 | goto out3; | |
99 | part_layout->read_proc = jffs_proc_layout_read; | |
100 | part_layout->data = (void *) c; | |
101 | ||
102 | /* Fill in structure for table and insert in the list */ | |
103 | part_dir->c = c; | |
104 | part_dir->part_root = part_root; | |
105 | part_dir->part_info = part_info; | |
106 | part_dir->part_layout = part_layout; | |
107 | part_dir->next = jffs_part_dirs; | |
108 | jffs_part_dirs = part_dir; | |
109 | ||
110 | /* Return happy */ | |
111 | return 0; | |
112 | ||
113 | out3: | |
114 | remove_proc_entry("info", part_root); | |
115 | out2: | |
116 | remove_proc_entry(name, jffs_proc_root); | |
117 | out1: | |
118 | kfree(part_dir); | |
119 | out: | |
120 | return -ENOMEM; | |
121 | } | |
122 | ||
123 | ||
124 | /* | |
125 | * Unregister a JFFS partition directory (called at umount) | |
126 | */ | |
127 | int jffs_unregister_jffs_proc_dir(struct jffs_control *c) | |
128 | { | |
129 | struct jffs_partition_dir *part_dir = jffs_part_dirs; | |
130 | struct jffs_partition_dir *prev_part_dir = NULL; | |
131 | ||
132 | while (part_dir) { | |
133 | if (part_dir->c == c) { | |
134 | /* Remove entries for partition */ | |
135 | remove_proc_entry (part_dir->part_info->name, | |
136 | part_dir->part_root); | |
137 | remove_proc_entry (part_dir->part_layout->name, | |
138 | part_dir->part_root); | |
139 | remove_proc_entry (part_dir->part_root->name, | |
140 | jffs_proc_root); | |
141 | ||
142 | /* Remove entry from list */ | |
143 | if (prev_part_dir) | |
144 | prev_part_dir->next = part_dir->next; | |
145 | else | |
146 | jffs_part_dirs = part_dir->next; | |
147 | ||
148 | /* | |
149 | * Check to see if this is the last one | |
150 | * and remove the entry from '/proc/fs' | |
151 | * if it is. | |
152 | */ | |
153 | if (jffs_part_dirs == part_dir->next) | |
154 | remove_proc_entry ("jffs", proc_root_fs); | |
155 | ||
156 | /* Free memory for entry */ | |
157 | kfree(part_dir); | |
158 | ||
159 | /* Return happy */ | |
160 | return 0; | |
161 | } | |
162 | ||
163 | /* Move to next entry */ | |
164 | prev_part_dir = part_dir; | |
165 | part_dir = part_dir->next; | |
166 | } | |
167 | ||
168 | /* Return unhappy */ | |
169 | return -1; | |
170 | } | |
171 | ||
172 | ||
173 | /* | |
174 | * Read a JFFS partition's `info' file | |
175 | */ | |
176 | static int jffs_proc_info_read (char *page, char **start, off_t off, | |
177 | int count, int *eof, void *data) | |
178 | { | |
179 | struct jffs_control *c = (struct jffs_control *) data; | |
180 | int len = 0; | |
181 | ||
182 | /* Get information on the parition */ | |
183 | len += sprintf (page, | |
184 | "partition size: %08lX (%u)\n" | |
185 | "sector size: %08lX (%u)\n" | |
186 | "used size: %08lX (%u)\n" | |
187 | "dirty size: %08lX (%u)\n" | |
188 | "free size: %08lX (%u)\n\n", | |
189 | (unsigned long) c->fmc->flash_size, c->fmc->flash_size, | |
190 | (unsigned long) c->fmc->sector_size, c->fmc->sector_size, | |
191 | (unsigned long) c->fmc->used_size, c->fmc->used_size, | |
192 | (unsigned long) c->fmc->dirty_size, c->fmc->dirty_size, | |
193 | (unsigned long) (c->fmc->flash_size - | |
194 | (c->fmc->used_size + c->fmc->dirty_size)), | |
195 | c->fmc->flash_size - (c->fmc->used_size + c->fmc->dirty_size)); | |
196 | ||
197 | /* We're done */ | |
198 | *eof = 1; | |
199 | ||
200 | /* Return length */ | |
201 | return len; | |
202 | } | |
203 | ||
204 | ||
205 | /* | |
206 | * Read a JFFS partition's `layout' file | |
207 | */ | |
208 | static int jffs_proc_layout_read (char *page, char **start, off_t off, | |
209 | int count, int *eof, void *data) | |
210 | { | |
211 | struct jffs_control *c = (struct jffs_control *) data; | |
212 | struct jffs_fm *fm = NULL; | |
213 | struct jffs_fm *last_fm = NULL; | |
214 | int len = 0; | |
215 | ||
216 | /* Get the first item in the list */ | |
217 | fm = c->fmc->head; | |
218 | ||
219 | /* Print free space */ | |
220 | if (fm && fm->offset) { | |
221 | len += sprintf (page, "00000000 %08lX free\n", | |
222 | (unsigned long) fm->offset); | |
223 | } | |
224 | ||
225 | /* Loop through all of the flash control structures */ | |
226 | while (fm && (len < (off + count))) { | |
227 | if (fm->nodes) { | |
228 | len += sprintf (page + len, | |
229 | "%08lX %08lX ino=%08lX, ver=%08lX\n", | |
230 | (unsigned long) fm->offset, | |
231 | (unsigned long) fm->size, | |
232 | (unsigned long) fm->nodes->node->ino, | |
233 | (unsigned long) fm->nodes->node->version); | |
234 | } | |
235 | else { | |
236 | len += sprintf (page + len, | |
237 | "%08lX %08lX dirty\n", | |
238 | (unsigned long) fm->offset, | |
239 | (unsigned long) fm->size); | |
240 | } | |
241 | last_fm = fm; | |
242 | fm = fm->next; | |
243 | } | |
244 | ||
245 | /* Print free space */ | |
246 | if ((len < (off + count)) && last_fm | |
247 | && (last_fm->offset < c->fmc->flash_size)) { | |
248 | len += sprintf (page + len, | |
249 | "%08lX %08lX free\n", | |
250 | (unsigned long) last_fm->offset + | |
251 | last_fm->size, | |
252 | (unsigned long) (c->fmc->flash_size - | |
253 | (last_fm->offset + last_fm->size))); | |
254 | } | |
255 | ||
256 | /* We're done */ | |
257 | *eof = 1; | |
258 | ||
259 | /* Return length */ | |
260 | return len; | |
261 | } |