summaryrefslogtreecommitdiffstats
path: root/fs/hfs/brec.c
blob: 4db76fc4f62cb7fed928793efdeb5d1220307447 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
/*
 * linux/fs/hfs/brec.c
 *
 * Copyright (C) 1995-1997  Paul H. Hargrove
 * This file may be distributed under the terms of the GNU General Public License.
 *
 * This file contains the code to access records in a btree.
 *
 * "XXX" in a comment is a note to myself to consider changing something.
 *
 * In function preconditions the term "valid" applied to a pointer to
 * a structure means that the pointer is non-NULL and the structure it
 * points to has all fields initialized to consistent values.
 */

#include "hfs_btree.h"

/*================ File-local functions ================*/

/*
 * first()
 *
 * returns HFS_BPATH_FIRST if elem->record == 1, 0 otherwise
 */
static inline int first(const struct hfs_belem *elem)
{
	return (elem->record == 1) ? HFS_BPATH_FIRST : 0;
}

/*
 * overflow()
 *
 * return HFS_BPATH_OVERFLOW if the node has no room for an 
 * additional pointer record, 0 otherwise.
 */
static inline int overflow(const struct hfs_btree *tree,
			   const struct hfs_bnode *bnode)
{
	/* there is some algebra involved in getting this form */
	return ((HFS_SECTOR_SIZE - sizeof(hfs_u32)) <
		 (bnode_end(bnode) + (2+bnode->ndNRecs)*sizeof(hfs_u16) +
		  ROUND(tree->bthKeyLen+1))) ?  HFS_BPATH_OVERFLOW : 0;
}

/*
 * underflow()
 *
 * return HFS_BPATH_UNDERFLOW if the node will be less that 1/2 full
 * upon removal of a pointer record, 0 otherwise.
 */
static inline int underflow(const struct hfs_btree *tree,
			    const struct hfs_bnode *bnode)
{
	return ((bnode->ndNRecs * sizeof(hfs_u16) +
		 bnode_offset(bnode, bnode->ndNRecs)) <
		(HFS_SECTOR_SIZE - sizeof(struct NodeDescriptor))/2) ?
		HFS_BPATH_UNDERFLOW : 0;
}

/*================ Global functions ================*/

/*
 * hfs_brec_next()
 *
 * Description:
 *   Obtain access to a child of an internal node in a B-tree.
 * Input Variable(s):
 *   struct hfs_brec *brec: pointer to the (struct hfs_brec) to
 *    add an element to.
 * Output Variable(s):
 *   NONE
 * Returns:
 *   struct hfs_belem *: pointer to the new path element or NULL
 * Preconditions:
 *   'brec' points to a "valid" (struct hfs_brec), the last element of
 *   which corresponds to a record in a bnode of type ndIndxNode and the
 *   'record' field indicates the index record for the desired child.
 * Postconditions:
 *   If the call to hfs_bnode_find() fails then 'brec' is released
 *   and a NULL is returned.
 *   Otherwise:
 *    Any ancestors in 'brec' that are not needed (as determined by the
 *     'keep_flags' field of 'brec) are released from 'brec'.
 *    A new element is added to 'brec' corresponding to the desired
 *     child.
 *    The child is obtained with the same 'lock_type' field as its
 *     parent.
 *    The 'record' field is initialized to the last record.
 *    A pointer to the new path element is returned.
 */
struct hfs_belem *hfs_brec_next(struct hfs_brec *brec)
{
	struct hfs_belem *elem = brec->bottom;
	hfs_u32 node;
	int lock_type;

	/* release unneeded ancestors */
	elem->flags = first(elem) |
		      overflow(brec->tree, elem->bnr.bn) |
		      underflow(brec->tree, elem->bnr.bn);
	if (!(brec->keep_flags & elem->flags)) {
		hfs_brec_relse(brec, brec->bottom-1);
	} else if ((brec->bottom-2 >= brec->top) &&
		   !(elem->flags & (elem-1)->flags)) {
		hfs_brec_relse(brec, brec->bottom-2);
	}

	node = hfs_get_hl(belem_record(elem));
	lock_type = elem->bnr.lock_type;

	if (!node || hfs_bnode_in_brec(node, brec)) {
		hfs_warn("hfs_bfind: corrupt btree\n");
		hfs_brec_relse(brec, NULL);
		return NULL;
	}

	++elem;
	++brec->bottom;

	elem->bnr = hfs_bnode_find(brec->tree, node, lock_type);
	if (!elem->bnr.bn) {
		hfs_brec_relse(brec, NULL);
		return NULL;
	}
	elem->record = elem->bnr.bn->ndNRecs;

	return elem;
}

/*
 * hfs_brec_lock()
 *
 * Description:
 *   This function obtains HFS_LOCK_WRITE access to the bnode
 *   containing this hfs_brec.	All descendents in the path from this
 *   record to the leaf are given HFS_LOCK_WRITE access and all
 *   ancestors in the path from the root to here are released.
 * Input Variable(s):
 *   struct hfs_brec *brec: pointer to the brec to obtain
 *    HFS_LOCK_WRITE access to some of the nodes of.
 *   struct hfs_belem *elem: the first node to lock or NULL for all
 * Output Variable(s):
 *   NONE
 * Returns:
 *   void
 * Preconditions:
 *   'brec' points to a "valid" (struct hfs_brec)
 * Postconditions: 
 *   All nodes between the indicated node and the beginning of the path
 *    are released.  hfs_bnode_lock() is called in turn on each node
 *    from the indicated node to the leaf node of the path, with a
 *    lock_type argument of HFS_LOCK_WRITE.  If one of those calls
 *    results in deadlock, then this function will never return.
 */
void hfs_brec_lock(struct hfs_brec *brec, struct hfs_belem *elem) 
{
	if (!elem) {
		elem = brec->top;
	} else if (elem > brec->top) {
		hfs_brec_relse(brec, elem-1);
	}

	while (elem <= brec->bottom) {
		hfs_bnode_lock(&elem->bnr, HFS_LOCK_WRITE);
		++elem;
	}
}

/*
 * hfs_brec_init()
 *
 * Description:
 *   Obtain access to the root node of a B-tree.
 *   Note that this first must obtain access to the header node.
 * Input Variable(s):
 *   struct hfs_brec *brec: pointer to the (struct hfs_brec) to
 *    initialize
 *   struct hfs_btree *btree: pointer to the (struct hfs_btree)
 *   int lock_type: the type of access to get to the nodes.
 * Output Variable(s):
 *   NONE
 * Returns:
 *   struct hfs_belem *: pointer to the root path element or NULL
 * Preconditions:
 *   'brec' points to a (struct hfs_brec).
 *   'tree' points to a valid (struct hfs_btree).
 * Postconditions:
 *   If the two calls to brec_bnode_find() succeed then the return value
 *   points to a (struct hfs_belem) which corresponds to the root node
 *   of 'brec->tree'.
 *   Both the root and header nodes are obtained with the type of lock
 *   given by (flags & HFS_LOCK_MASK).
 *   The fields 'record' field of the root is set to its last record.
 *   If the header node is not needed to complete the appropriate
 *   operation (as determined by the 'keep_flags' field of 'brec') then
 *   it is released before this function returns.
 *   If either call to brec_bnode_find() fails, NULL is returned and the
 *   (struct hfs_brec) pointed to by 'brec' is invalid.
 */
struct hfs_belem *hfs_brec_init(struct hfs_brec *brec, struct hfs_btree *tree,
				int flags)
{
	struct hfs_belem *head = &brec->elem[0];
	struct hfs_belem *root = &brec->elem[1];
	int lock_type = flags & HFS_LOCK_MASK;

	brec->tree = tree;

	head->bnr = hfs_bnode_find(tree, 0, lock_type);
	if (!head->bnr.bn) {
		return NULL;
	}

	root->bnr = hfs_bnode_find(tree, tree->bthRoot, lock_type);
	if (!root->bnr.bn) {
		hfs_bnode_relse(&head->bnr);
		return NULL;
	}

	root->record = root->bnr.bn->ndNRecs;
	
	brec->top = head;
	brec->bottom = root;
	
	brec->keep_flags = flags & HFS_BPATH_MASK;

	/* HFS_BPATH_FIRST not applicable for root */
	/* and HFS_BPATH_UNDERFLOW is different */
	root->flags = overflow(tree, root->bnr.bn);
	if (root->record < 3) {
		root->flags |= HFS_BPATH_UNDERFLOW;
	}

	if (!(root->flags & brec->keep_flags)) {
		hfs_brec_relse(brec, head);
	}

	return root;
}