summaryrefslogtreecommitdiff
path: root/lnode.c
blob: b94629b34590ff90441443cddec8c041599727c1 (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
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
/*---------------------------------------------------------------------------*/
/*lnode.c*/
/*---------------------------------------------------------------------------*/
/*Implementation of policies of management of 'light nodes'*/
/*---------------------------------------------------------------------------*/
/*Based on the code of unionfs translator.*/
/*---------------------------------------------------------------------------*/
/*Copyright (C) 2001, 2002, 2005, 2008, 2009 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"
#include "node.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 */
    pthread_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)
{
  /*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 */
  pthread_mutex_init (&node_new->lock, NULL);
  pthread_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 the name of the node */
  free (node->name);

  /*While the list of proxies has not been freed */
  node_list_t p;
  for (p = node->proxies; p;)
    {
      /*shift the pointer to the head of the list forward */
      node->proxies = node->proxies->next;

      /*drop the current cell in the list */
      free (p);

      /*store the current proxy */
      p = node->proxies;
    }

  /*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)
{
  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 */
      pthread_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 */

/*---------------------------------------------------------------------------*/
/*Makes the specified lnode aware of another proxy. Both `node` and
  `proxy` must be locked*/
error_t lnode_add_proxy (lnode_t * node, node_t * proxy)
{
  /*TODO: Make the list of proxies finite */

  /*Create an new cell in the list of proxies */
  node_list_t p = malloc (sizeof (node_list_t));
  if (!p)
    return ENOMEM;

  /*If the supplied node references an lnode already
     (this should always happen, though) */
  if (proxy->nn->lnode)
    /*remove the reference held by the node to that lnode */
    lnode_ref_remove (proxy->nn->lnode);

  /*Connect the proxy to the lnode */
  proxy->nn->lnode = node;

  /*Store the pointer to the proxy in the new cell */
  p->node = proxy;

  /*Add the new cell to the list of proxies */
  p->next = node->proxies;
  node->proxies = p;

  /*Count a new reference to this lnode */
  lnode_ref_add (node);

  /*Everything is OK here */
  return 0;
}				/*lnode_add_proxy */

/*---------------------------------------------------------------------------*/
/*Removes the specified proxy from the list of proxies of the supplied lnode.
  `proxy` must not be locked*/
void lnode_remove_proxy (lnode_t * node, node_t * proxy)
{
  /*A pointer to a cell in the list of proxies */
  node_list_t p;

  /*Lock the lnode */
  pthread_mutex_lock (&node->lock);

  /*If the first cell in the list contains a reference to `proxy` */
  if (node->proxies->node == proxy)
    {
      /*store a pointer to the head of the list */
      p = node->proxies;

      /*shift the head of the list forward */
      node->proxies = node->proxies->next;

      /*destroy the former head */
      free (p);

      /*remove a reference from the supplied lnode */
      lnode_ref_remove (node);

      /*stop right here */
      pthread_mutex_unlock (&node->lock);
      return;
    }

  /*Another pointer to a cell in the list */
  node_list_t q;

  /*Go through the list of proxy nodes of the given lnode */
  for (p = node->proxies; p->next; p = p->next)
    {
      /*If the next cell does not contain a reference to the same node,
         as specified in the parameter, skip this entry */
      if (p->next->node != proxy)
	continue;

      /*store a copy of the next element */
      q = p->next;

      /*unregister the next element from the list */
      p->next = q->next;

      /*remove the unregistered element */
      free (q);

      /*stop looping */
      break;
    }

  /*Remove a reference from the supplied lnode */
  lnode_ref_remove (node);


  /*Unlock the node */
  pthread_mutex_unlock (&node->lock);

  return;
}				/*lnode_remove_proxy */

/*---------------------------------------------------------------------------*/