summaryrefslogtreecommitdiff
path: root/lnode.c
blob: aed83a0f50707495f4bd66dededd3fb31707ee23 (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
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
/*----------------------------------------------------------------------------*/
/*lnode.c*/
/*----------------------------------------------------------------------------*/
/*Implementation of policies of management of 'light nodes'*/
/*----------------------------------------------------------------------------*/
/*Based on the code of unionfs translator.*/
/*----------------------------------------------------------------------------*/
/*Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc.
  Written by Sergiu Ivanov <unlimitedscolobb@gmail.com>.

  This program 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.

  This program is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  USA.*/
/*----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*/
#define _GNU_SOURCE
/*----------------------------------------------------------------------------*/
#include "lnode.h"
#include "debug.h"
/*----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*/
/*--------Functions-----------------------------------------------------------*/
/*Adds a reference to the `lnode` (which must be locked)*/
void
lnode_ref_add
	(
	lnode_t * node
	)
	{
	/*Increment the number of references*/
	++node->references;
	}/*lnode_ref_add*/
/*----------------------------------------------------------------------------*/
/*Removes a reference from `node` (which must be locked). If that was the last
	reference, destroy the node*/
void
lnode_ref_remove
	(
	lnode_t * node
	)
	{
	/*Fail if the node is not referenced by anybody*/
	assert(node->references);
	
	/*Decrement the number of references to `node`*/
	--node->references;
	
	/*If there are no references remaining*/
	if(node->references == 0)
		{
		/*uninstall the node from the directory it is in and destroy it*/
		lnode_uninstall(node);
		lnode_destroy(node);
		}
	else
		/*simply unlock the node*/
		mutex_unlock(&node->lock);
	}/*lnode_ref_remove*/
/*----------------------------------------------------------------------------*/
/*Creates a new lnode with `name`; the new node is locked and contains
	a single reference*/
error_t
lnode_create
	(
	char * name,
	lnode_t ** node	/*put the result here*/
	)
	{
	/*Allocate the memory for the node*/
	lnode_t * node_new = malloc(sizeof(lnode_t));
	
	/*If the memory has not been allocated*/
	if(!node_new)
		{
		/*stop*/
		return ENOMEM;
		}
		
	/*The copy of the name*/
	char * name_cp = NULL;
	
	/*If the name exists*/
	if(name)
		{
		/*duplicate it*/
		name_cp = strdup(name);
		
		/*If the name has not been duplicated*/
		if(!name_cp)
			{
			/*free the node*/
			free(node_new);
			
			/*stop*/
			return ENOMEM;
			}
		}
		
	/*Setup the new node*/
	memset(node_new, 0, sizeof(lnode_t));
	node_new->name 				= name_cp;
	node_new->name_len		= (name_cp) ? (strlen(name_cp)) : (0);
	
	/*Setup one reference to this lnode*/
	node_new->references = 1;
	
	/*Initialize the mutex and acquire a lock on this lnode*/
	mutex_init(&node_new->lock);
	mutex_lock(&node_new->lock);
	
	/*Store the result in the second parameter*/
	*node = node_new;

	/*Return success*/
	return 0;
	}/*lnode_create*/
/*----------------------------------------------------------------------------*/
/*Destroys the given lnode*/
void
lnode_destroy
	(
	lnode_t * node	/*destroy this*/
	)
	{
	/*Destroy the name of the node*/
	free(node->name);
	
	/*If there is a list of translators*/
	if(node->trans)
		free(node->trans);
	
	/*Destroy the node itself*/
	free(node);
	}/*lnode_destroy*/
/*----------------------------------------------------------------------------*/
/*Constructs the full path for the given lnode and stores the result both in
	the parameter and inside the lnode (the same string, actually)*/
error_t
lnode_path_construct
	(
	lnode_t * node,
	char ** path	/*store the path here*/
	)
	{
	error_t err = 0;

	/*The final path*/
	char * p;
	
	/*The final length of the path*/
	int p_len = 0;
	
	/*A temporary pointer to an lnode*/
	lnode_t * n;
	
	/*While the root node of the proxy filesystem has not been reached*/
	for(n = node; n && n->dir; n = n->dir)
		/*add the length of the name of `n` to `p_len` make some space for
			the delimiter '/', if we are not approaching the root node*/
		/*p_len += n->name_len + ((n->dir->dir) ? (1) : (0));*/
		/*There is some path to our root node, so we will anyway have to
			add a '/'*/
		p_len += n->name_len + 1;
		
	/*Include the space for the path to the root node of the proxy
		(n is now the root of the filesystem)*/
	p_len += strlen(n->path) + 1;
		
	/*Try to allocate the space for the string*/
	p = malloc(p_len * sizeof(char));
	if(!p)
		err = ENOMEM;
	/*If memory allocation has been successful*/
	else
		{
		/*put a terminal 0 at the end of the path*/
		p[--p_len] = 0;
		
		/*While the root node of the proxy filesystem has not been reached*/
		for(n = node; n && n->dir; n = n->dir)
			{
			/*compute the position where the name of `n` is to be inserted*/
			p_len -= n->name_len;
			
			/*copy the name of the node into the path (omit the terminal 0)*/
			strncpy(p + p_len, n->name, n->name_len);
			
			/*If we are not at the root node of the proxy filesystem, add the
				separator*/
			/*if(n->dir->dir)
				p[--p_len] = '/';*/
			/*we anyway have to add the separator slash*/
			p[--p_len] = '/';
			}
		
		/*put the path to the root node at the beginning of the first path
			(n is at the root now)*/
		strncpy(p, n->path, strlen(n->path));
		
		/*destroy the former path in lnode, if it exists*/
		if(node->path)
			free(node->path);
		
		/*store the new path inside the lnode*/
		node->path = p;

		/*store the path in the parameter*/
		if(path)
			*path = p;
		}
		
	/*Return the result of operations*/
	return err;
	}/*lnode_path_construct*/
/*----------------------------------------------------------------------------*/
/*Gets a light node by its name, locks it and increments its refcount*/
error_t
lnode_get
	(
	lnode_t * dir,	/*search here*/
	char * name,		/*search for this name*/
	lnode_t ** node	/*put the result here*/
	)
	{
	error_t err = 0;
	
	/*The pointer to the required lnode*/
	lnode_t * n;
	
	/*Find `name` among the names of entries in `dir`*/
	for(n = dir->entries; n && (strcmp(n->name, name) != 0); n = n->next);
	
	/*If the search has been successful*/
	if(n)
		{
		/*lock the node*/
		mutex_lock(&n->lock);
		
		/*increment the refcount of the found lnode*/
		lnode_ref_add(n);
		
		/*put a pointer to `n` into the parameter*/
		*node = n;
		}
	else
		err = ENOENT;
		
	/*Return the result of operations*/
	return err;
	}/*lnode_get*/
/*----------------------------------------------------------------------------*/
/*Install the lnode into the lnode tree: add a reference to `dir` (which must
	be locked)*/
void
lnode_install
	(
	lnode_t * dir,	/*install here*/
	lnode_t * node	/*install this*/
	)
	{
	/*Install `node` into the list of entries in `dir`*/
	node->next = dir->entries;
	node->prevp = &dir->entries; /*this node is the first on the list*/
	if(dir->entries)
		dir->entries->prevp = &node->next;	/*here `prevp` gets the value
																					corresponding to its meaning*/
	dir->entries = node;
	
	/*Add a new reference to dir*/
	lnode_ref_add(dir);
	
	/*Setup the `dir` link in node*/
	node->dir = dir;
	}/*lnode_install*/
/*----------------------------------------------------------------------------*/
/*Unistall the node from the node tree; remove a reference from the lnode
	containing `node`*/
void
lnode_uninstall
	(
	lnode_t * node
	)
	{
	/*Remove a reference from the parent*/
	lnode_ref_remove(node->dir);
	
	/*Make the next pointer in the previous element point to the element,
		which follows `node`*/
	*node->prevp = node->next;

	/*If the current node is not the last one, connect the list after removal
		of the current node*/
	if(node->next)
		node->next->prevp = &node->next;
	}/*lnode_uninstall*/
/*----------------------------------------------------------------------------*/
/*Constructs a list of translators that were set on the ancestors of `node`*/
error_t
lnode_list_translators
	(
	lnode_t * node,
	char ** trans,	/*the malloced list of 0-separated strings*/
	size_t * ntrans	/*the number of elements in `trans`*/
	)
	{
	/*The size of block of memory for the list of translators*/
	size_t sz = 0;
	
	/*Used for tracing the lineage of `node`*/
	lnode_t * ln = node;
	
	/*Used in computing the lengths of lists of translators in every node
		we will go through and for constructing the final list of translators*/
	char * p;
	
	/*The current position in *data (used in filling out the list of
		translators)*/
	char * transp;
	
	/*The length of the current translator name*/
	size_t complen;
	
	size_t i;
	
	/*Trace the lineage of the `node` (including itself) and compute the
		total length of the list of translators*/
	for(; ln; sz += ln->translen, ln = ln->dir);
	
	/*Try to allocate a block of memory sufficient for storing the list of
		translators*/
	*trans = malloc(sz);
	if(!*trans)
		return ENOMEM;
	
	/*No translators at first*/
	*ntrans = 0;
	
	/*Again trace the lineage of the `node` (including itself)*/
	for(transp = *trans + sz, ln = node; ln; ln = ln->dir)
		{
		/*Go through each translator name in the list of names*/
		for(i = 0, p = ln->trans + ln->translen - 2; i < ln->ntrans; ++i)
			{
			/*position p at the beginning of the current component and
				compute its length at the same time*/
			for(complen = 0; *p; --p, ++complen);
			--p;
			
			/*move the current position backwards*/
			transp -= complen + 1;

			/*copy the current translator name into the list*/
			strcpy(transp, p + 2);
			
			/*we've got another translator*/
			++*ntrans;
			}
		}
		
	/*Everything OK*/
	return 0;
	}/*lnode_list_translators*/
/*----------------------------------------------------------------------------*/