1433d6423SLionel Sambuc /* This file deals with inode management.
2433d6423SLionel Sambuc *
3433d6423SLionel Sambuc * The entry points into this file are:
4433d6423SLionel Sambuc * init_inode initialize the inode table, return the root inode
5433d6423SLionel Sambuc * find_inode find an inode based on its inode number
6433d6423SLionel Sambuc * get_inode increase the reference count of an inode
7433d6423SLionel Sambuc * put_inode decrease the reference count of an inode
8433d6423SLionel Sambuc * link_inode link an inode as a directory entry to another inode
9433d6423SLionel Sambuc * unlink_inode unlink an inode from its parent directory
10433d6423SLionel Sambuc * get_free_inode return a free inode object
11433d6423SLionel Sambuc * have_free_inode check whether there is a free inode available
12433d6423SLionel Sambuc * have_used_inode check whether any inode is still in use
13433d6423SLionel Sambuc * do_putnode perform the PUTNODE file system call
14433d6423SLionel Sambuc *
15433d6423SLionel Sambuc * Created:
16433d6423SLionel Sambuc * April 2009 (D.C. van Moolenbroek)
17433d6423SLionel Sambuc */
18433d6423SLionel Sambuc
19433d6423SLionel Sambuc #include "inc.h"
20433d6423SLionel Sambuc
21433d6423SLionel Sambuc static struct inode inodes[NUM_INODES];
22433d6423SLionel Sambuc
23433d6423SLionel Sambuc static TAILQ_HEAD(free_head, inode) free_list;
24433d6423SLionel Sambuc
25433d6423SLionel Sambuc /*===========================================================================*
26433d6423SLionel Sambuc * init_inode *
27433d6423SLionel Sambuc *===========================================================================*/
init_inode(void)28433d6423SLionel Sambuc struct inode *init_inode(void)
29433d6423SLionel Sambuc {
30433d6423SLionel Sambuc /* Initialize inode table. Return the root inode.
31433d6423SLionel Sambuc */
32433d6423SLionel Sambuc struct inode *ino;
33*7c48de6cSDavid van Moolenbroek unsigned int i;
34433d6423SLionel Sambuc
35433d6423SLionel Sambuc TAILQ_INIT(&free_list);
36433d6423SLionel Sambuc
37433d6423SLionel Sambuc dprintf(("%s: %d inodes, %u bytes each, equals %u bytes\n",
38433d6423SLionel Sambuc sffs_name, NUM_INODES, sizeof(struct inode), sizeof(inodes)));
39433d6423SLionel Sambuc
40433d6423SLionel Sambuc /* Mark all inodes except the root inode as free. */
41*7c48de6cSDavid van Moolenbroek for (i = 1; i < NUM_INODES; i++) {
42*7c48de6cSDavid van Moolenbroek ino = &inodes[i];
43433d6423SLionel Sambuc ino->i_parent = NULL;
44433d6423SLionel Sambuc LIST_INIT(&ino->i_child);
45*7c48de6cSDavid van Moolenbroek ino->i_num = i + 1;
46433d6423SLionel Sambuc ino->i_gen = (unsigned short)-1; /* aesthetics */
47433d6423SLionel Sambuc ino->i_ref = 0;
48433d6423SLionel Sambuc ino->i_flags = 0;
49433d6423SLionel Sambuc TAILQ_INSERT_TAIL(&free_list, ino, i_free);
50433d6423SLionel Sambuc }
51433d6423SLionel Sambuc
52433d6423SLionel Sambuc /* Initialize and return the root inode. */
53433d6423SLionel Sambuc ino = &inodes[0];
54433d6423SLionel Sambuc ino->i_parent = ino; /* root inode is its own parent */
55433d6423SLionel Sambuc LIST_INIT(&ino->i_child);
56433d6423SLionel Sambuc ino->i_num = ROOT_INODE_NR;
57433d6423SLionel Sambuc ino->i_gen = 0; /* unused by root node */
58433d6423SLionel Sambuc ino->i_ref = 1; /* root inode is hereby in use */
59433d6423SLionel Sambuc ino->i_flags = I_DIR; /* root inode is a directory */
60433d6423SLionel Sambuc ino->i_name[0] = 0; /* root inode has empty name */
61433d6423SLionel Sambuc
62433d6423SLionel Sambuc return ino;
63433d6423SLionel Sambuc }
64433d6423SLionel Sambuc
65433d6423SLionel Sambuc /*===========================================================================*
66433d6423SLionel Sambuc * find_inode *
67433d6423SLionel Sambuc *===========================================================================*/
find_inode(ino_t ino_nr)68433d6423SLionel Sambuc struct inode *find_inode(ino_t ino_nr)
69433d6423SLionel Sambuc {
70433d6423SLionel Sambuc /* Get an inode based on its inode number. Do not increase its reference count.
71433d6423SLionel Sambuc */
72433d6423SLionel Sambuc struct inode *ino;
73*7c48de6cSDavid van Moolenbroek int i;
74433d6423SLionel Sambuc
75433d6423SLionel Sambuc /* Inode 0 (= index -1) is not a valid inode number. */
76*7c48de6cSDavid van Moolenbroek i = INODE_INDEX(ino_nr);
77*7c48de6cSDavid van Moolenbroek if (i < 0) {
78433d6423SLionel Sambuc printf("%s: VFS passed invalid inode number!\n", sffs_name);
79433d6423SLionel Sambuc
80433d6423SLionel Sambuc return NULL;
81433d6423SLionel Sambuc }
82433d6423SLionel Sambuc
83*7c48de6cSDavid van Moolenbroek assert(i < NUM_INODES);
84433d6423SLionel Sambuc
85*7c48de6cSDavid van Moolenbroek ino = &inodes[i];
86433d6423SLionel Sambuc
87433d6423SLionel Sambuc /* Make sure the generation number matches. */
88433d6423SLionel Sambuc if (INODE_GEN(ino_nr) != ino->i_gen) {
89433d6423SLionel Sambuc printf("%s: VFS passed outdated inode number!\n", sffs_name);
90433d6423SLionel Sambuc
91433d6423SLionel Sambuc return NULL;
92433d6423SLionel Sambuc }
93433d6423SLionel Sambuc
94433d6423SLionel Sambuc /* The VFS/FS protocol only uses referenced inodes. */
95433d6423SLionel Sambuc if (ino->i_ref == 0)
96433d6423SLionel Sambuc printf("%s: VFS passed unused inode!\n", sffs_name);
97433d6423SLionel Sambuc
98433d6423SLionel Sambuc return ino;
99433d6423SLionel Sambuc }
100433d6423SLionel Sambuc
101433d6423SLionel Sambuc /*===========================================================================*
102433d6423SLionel Sambuc * get_inode *
103433d6423SLionel Sambuc *===========================================================================*/
get_inode(struct inode * ino)104433d6423SLionel Sambuc void get_inode(struct inode *ino)
105433d6423SLionel Sambuc {
106433d6423SLionel Sambuc /* Increase the given inode's reference count. If both reference and link
107433d6423SLionel Sambuc * count were zero before, remove the inode from the free list.
108433d6423SLionel Sambuc */
109433d6423SLionel Sambuc
110433d6423SLionel Sambuc dprintf(("%s: get_inode(%p) ['%s']\n", sffs_name, ino, ino->i_name));
111433d6423SLionel Sambuc
112433d6423SLionel Sambuc /* (INUSE, CACHED) -> INUSE */
113433d6423SLionel Sambuc
114433d6423SLionel Sambuc /* If this is the first reference, remove the node from the free list. */
115433d6423SLionel Sambuc if (ino->i_ref == 0 && !HAS_CHILDREN(ino))
116433d6423SLionel Sambuc TAILQ_REMOVE(&free_list, ino, i_free);
117433d6423SLionel Sambuc
118433d6423SLionel Sambuc ino->i_ref++;
119433d6423SLionel Sambuc
120433d6423SLionel Sambuc if (ino->i_ref == 0)
121433d6423SLionel Sambuc panic("inode reference count wrapped");
122433d6423SLionel Sambuc }
123433d6423SLionel Sambuc
124433d6423SLionel Sambuc /*===========================================================================*
125433d6423SLionel Sambuc * put_inode *
126433d6423SLionel Sambuc *===========================================================================*/
put_inode(struct inode * ino)127433d6423SLionel Sambuc void put_inode(struct inode *ino)
128433d6423SLionel Sambuc {
129433d6423SLionel Sambuc /* Decrease an inode's reference count. If this count has reached zero, close
130433d6423SLionel Sambuc * the inode's file handle, if any. If both reference and link count have
131433d6423SLionel Sambuc * reached zero, mark the inode as cached or free.
132433d6423SLionel Sambuc */
133433d6423SLionel Sambuc
134433d6423SLionel Sambuc dprintf(("%s: put_inode(%p) ['%s']\n", sffs_name, ino, ino->i_name));
135433d6423SLionel Sambuc
136433d6423SLionel Sambuc assert(ino != NULL);
137433d6423SLionel Sambuc assert(ino->i_ref > 0);
138433d6423SLionel Sambuc
139433d6423SLionel Sambuc ino->i_ref--;
140433d6423SLionel Sambuc
141433d6423SLionel Sambuc /* If there are still references to this inode, we're done here. */
142433d6423SLionel Sambuc if (ino->i_ref > 0)
143433d6423SLionel Sambuc return;
144433d6423SLionel Sambuc
145433d6423SLionel Sambuc /* Close any file handle associated with this inode. */
146433d6423SLionel Sambuc put_handle(ino);
147433d6423SLionel Sambuc
148433d6423SLionel Sambuc /* Only add the inode to the free list if there are also no links to it. */
149433d6423SLionel Sambuc if (HAS_CHILDREN(ino))
150433d6423SLionel Sambuc return;
151433d6423SLionel Sambuc
152433d6423SLionel Sambuc /* INUSE -> CACHED, DELETED -> FREE */
153433d6423SLionel Sambuc
154433d6423SLionel Sambuc /* Add the inode to the head or tail of the free list, depending on whether
155433d6423SLionel Sambuc * it is also deleted (and therefore can never be reused as is).
156433d6423SLionel Sambuc */
157433d6423SLionel Sambuc if (ino->i_parent == NULL)
158433d6423SLionel Sambuc TAILQ_INSERT_HEAD(&free_list, ino, i_free);
159433d6423SLionel Sambuc else
160433d6423SLionel Sambuc TAILQ_INSERT_TAIL(&free_list, ino, i_free);
161433d6423SLionel Sambuc }
162433d6423SLionel Sambuc
163433d6423SLionel Sambuc /*===========================================================================*
164433d6423SLionel Sambuc * link_inode *
165433d6423SLionel Sambuc *===========================================================================*/
link_inode(struct inode * parent,struct inode * ino)166433d6423SLionel Sambuc void link_inode(struct inode *parent, struct inode *ino)
167433d6423SLionel Sambuc {
168433d6423SLionel Sambuc /* Link an inode to a parent. If both reference and link count were zero
169433d6423SLionel Sambuc * before, remove the inode from the free list. This function should only be
170433d6423SLionel Sambuc * called from add_dentry().
171433d6423SLionel Sambuc */
172433d6423SLionel Sambuc
173433d6423SLionel Sambuc /* This can never happen, right? */
174433d6423SLionel Sambuc if (parent->i_ref == 0 && !HAS_CHILDREN(parent))
175433d6423SLionel Sambuc TAILQ_REMOVE(&free_list, parent, i_free);
176433d6423SLionel Sambuc
177433d6423SLionel Sambuc LIST_INSERT_HEAD(&parent->i_child, ino, i_next);
178433d6423SLionel Sambuc
179433d6423SLionel Sambuc ino->i_parent = parent;
180433d6423SLionel Sambuc }
181433d6423SLionel Sambuc
182433d6423SLionel Sambuc /*===========================================================================*
183433d6423SLionel Sambuc * unlink_inode *
184433d6423SLionel Sambuc *===========================================================================*/
unlink_inode(struct inode * ino)185433d6423SLionel Sambuc void unlink_inode(struct inode *ino)
186433d6423SLionel Sambuc {
187433d6423SLionel Sambuc /* Unlink an inode from its parent. If both reference and link count have
188433d6423SLionel Sambuc * reached zero, mark the inode as cached or free. This function should only
189433d6423SLionel Sambuc * be used from del_dentry().
190433d6423SLionel Sambuc */
191433d6423SLionel Sambuc struct inode *parent;
192433d6423SLionel Sambuc
193433d6423SLionel Sambuc parent = ino->i_parent;
194433d6423SLionel Sambuc
195433d6423SLionel Sambuc LIST_REMOVE(ino, i_next);
196433d6423SLionel Sambuc
197433d6423SLionel Sambuc if (parent->i_ref == 0 && !HAS_CHILDREN(parent)) {
198433d6423SLionel Sambuc if (parent->i_parent == NULL)
199433d6423SLionel Sambuc TAILQ_INSERT_HEAD(&free_list, parent, i_free);
200433d6423SLionel Sambuc else
201433d6423SLionel Sambuc TAILQ_INSERT_TAIL(&free_list, parent, i_free);
202433d6423SLionel Sambuc }
203433d6423SLionel Sambuc
204433d6423SLionel Sambuc ino->i_parent = NULL;
205433d6423SLionel Sambuc }
206433d6423SLionel Sambuc
207433d6423SLionel Sambuc /*===========================================================================*
208433d6423SLionel Sambuc * get_free_inode *
209433d6423SLionel Sambuc *===========================================================================*/
get_free_inode(void)210433d6423SLionel Sambuc struct inode *get_free_inode(void)
211433d6423SLionel Sambuc {
212433d6423SLionel Sambuc /* Return a free inode object (with reference count 1), if available.
213433d6423SLionel Sambuc */
214433d6423SLionel Sambuc struct inode *ino;
215433d6423SLionel Sambuc
216433d6423SLionel Sambuc /* [CACHED -> FREE,] FREE -> DELETED */
217433d6423SLionel Sambuc
218433d6423SLionel Sambuc /* If there are no inodes on the free list, we cannot satisfy the request. */
219433d6423SLionel Sambuc if (TAILQ_EMPTY(&free_list)) {
220433d6423SLionel Sambuc printf("%s: out of inodes!\n", sffs_name);
221433d6423SLionel Sambuc
222433d6423SLionel Sambuc return NULL;
223433d6423SLionel Sambuc }
224433d6423SLionel Sambuc
225433d6423SLionel Sambuc ino = TAILQ_FIRST(&free_list);
226433d6423SLionel Sambuc TAILQ_REMOVE(&free_list, ino, i_free);
227433d6423SLionel Sambuc
228433d6423SLionel Sambuc assert(ino->i_ref == 0);
229433d6423SLionel Sambuc assert(!HAS_CHILDREN(ino));
230433d6423SLionel Sambuc
231433d6423SLionel Sambuc /* If this was a cached inode, free it first. */
232433d6423SLionel Sambuc if (ino->i_parent != NULL)
233433d6423SLionel Sambuc del_dentry(ino);
234433d6423SLionel Sambuc
235433d6423SLionel Sambuc assert(ino->i_parent == NULL);
236433d6423SLionel Sambuc
237433d6423SLionel Sambuc /* Initialize a subset of its fields */
238433d6423SLionel Sambuc ino->i_gen++;
239433d6423SLionel Sambuc ino->i_ref = 1;
240433d6423SLionel Sambuc
241433d6423SLionel Sambuc return ino;
242433d6423SLionel Sambuc }
243433d6423SLionel Sambuc
244433d6423SLionel Sambuc /*===========================================================================*
245433d6423SLionel Sambuc * have_free_inode *
246433d6423SLionel Sambuc *===========================================================================*/
have_free_inode(void)247433d6423SLionel Sambuc int have_free_inode(void)
248433d6423SLionel Sambuc {
249433d6423SLionel Sambuc /* Check whether there are any free inodes at the moment. Kind of lame, but
250433d6423SLionel Sambuc * this allows for easier error recovery in some places.
251433d6423SLionel Sambuc */
252433d6423SLionel Sambuc
253433d6423SLionel Sambuc return !TAILQ_EMPTY(&free_list);
254433d6423SLionel Sambuc }
255433d6423SLionel Sambuc
256433d6423SLionel Sambuc /*===========================================================================*
257433d6423SLionel Sambuc * have_used_inode *
258433d6423SLionel Sambuc *===========================================================================*/
have_used_inode(void)259433d6423SLionel Sambuc int have_used_inode(void)
260433d6423SLionel Sambuc {
261433d6423SLionel Sambuc /* Check whether any inodes are still in use, that is, any of the inodes have
262433d6423SLionel Sambuc * a reference count larger than zero.
263433d6423SLionel Sambuc */
264*7c48de6cSDavid van Moolenbroek unsigned int i;
265433d6423SLionel Sambuc
266*7c48de6cSDavid van Moolenbroek for (i = 0; i < NUM_INODES; i++)
267*7c48de6cSDavid van Moolenbroek if (inodes[i].i_ref > 0)
268433d6423SLionel Sambuc return TRUE;
269433d6423SLionel Sambuc
270433d6423SLionel Sambuc return FALSE;
271433d6423SLionel Sambuc }
272433d6423SLionel Sambuc
273433d6423SLionel Sambuc /*===========================================================================*
274433d6423SLionel Sambuc * do_putnode *
275433d6423SLionel Sambuc *===========================================================================*/
do_putnode(ino_t ino_nr,unsigned int count)276a99c939dSDavid van Moolenbroek int do_putnode(ino_t ino_nr, unsigned int count)
277433d6423SLionel Sambuc {
278433d6423SLionel Sambuc /* Decrease an inode's reference count.
279433d6423SLionel Sambuc */
280433d6423SLionel Sambuc struct inode *ino;
281433d6423SLionel Sambuc
282a99c939dSDavid van Moolenbroek if ((ino = find_inode(ino_nr)) == NULL)
283433d6423SLionel Sambuc return EINVAL;
284433d6423SLionel Sambuc
285a99c939dSDavid van Moolenbroek if (count > ino->i_ref) return EINVAL;
286433d6423SLionel Sambuc
287433d6423SLionel Sambuc ino->i_ref -= count - 1;
288433d6423SLionel Sambuc
289433d6423SLionel Sambuc put_inode(ino);
290433d6423SLionel Sambuc
291433d6423SLionel Sambuc return OK;
292433d6423SLionel Sambuc }
293