summaryrefslogtreecommitdiff
path: root/libhurd-cap/cap.c
blob: c5ed1615b0b8558a0dad8beb9b3bc39538ea13ad (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
/* Copyright (C) 2003, 2005 Free Software Foundation, Inc.
   Written by Marcus Brinkmann <marcus@gnu.org>

   This file is part of the GNU Hurd.

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 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
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser 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.  */

#if HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdlib.h>
#include <assert.h>
#include <errno.h>
#include <stdint.h>

#include <pthread.h>

#include <hurd/cap.h>

#include "cap-intern.h"


/* The slab space for capability objects.  */
hurd_slab_space_t cap_space;


/* Initialize a new capability, allocated by the slab allocator.  */
static error_t
cap_constructor (void *hook, void *buffer)
{
  hurd_cap_t cap = (hurd_cap_t) buffer;
  error_t err;

  err = pthread_mutex_init (&cap->lock, NULL);
  if (err)
    return err;

  cap->srefs = 0;
  cap->orefs = 0;

  /* The other data is filled in by the creator.  */
  return 0;
}


/* Release all resources allocated by the capability, which is in its
   freshly initialized state.  */
static void
cap_destructor (void *hook, void *buffer)
{
  hurd_cap_t cap = (hurd_cap_t) buffer;

  assert (cap->srefs == 0);
  assert (cap->orefs == 0);

  pthread_mutex_destroy (&cap->lock);
}


/* Initialize the capability system.  */
error_t
hurd_cap_init (void)
{
  return hurd_slab_create (sizeof (struct hurd_cap), 0, NULL, NULL,
			   cap_constructor, cap_destructor, NULL,
			   &cap_space);
}


/* Modify the number of send references for the capability CAP by
   DELTA.  */
error_t
hurd_cap_mod_refs (hurd_cap_t cap, int delta)
{
  hurd_cap_sconn_t sconn;

  pthread_mutex_lock (&cap->lock);

  /* Verify that CAP->srefs is not 0 and will not become negative.  */
  if (cap->srefs == 0 || (delta < 0 && cap->srefs < -delta))
    {
      pthread_mutex_unlock (&cap->lock);
      return EINVAL;
    }

  /* Verify that CAP->srefs will not overflow.  */
  if (delta > 0 && cap->srefs > UINT32_MAX - delta)
    {
      pthread_mutex_unlock (&cap->lock);
      return EOVERFLOW;
    }

  cap->srefs += delta;
  if (cap->srefs != 0)
    {
      pthread_mutex_unlock (&cap->lock);
      return 0;
    }

  /* This was the last send reference we held.  Deallocate the server
     capability.  This is not so easy, though, as some other thread
     might concurrently try to enter a new reference for this
     capability.  Instead of doing reference counting for the
     capability IDs in the server connection, we get a temporary
     reference and acquire the server connection lock while the
     capability is temporarily unlocked.  Then we can check if we
     still have to deallocate the capabilty.  */
  sconn = cap->sconn;

  cap->srefs = 1;
  pthread_mutex_unlock (&cap->lock);

  /* Now we can try to remove ourselve from the server capability
     list.  CAP->sconn will not change while we hold our
     reference.  */
  pthread_mutex_lock (&sconn->lock);
  pthread_mutex_lock (&cap->lock);

  assert (cap->sconn == sconn);
  assert (cap->srefs != 0);
  cap->srefs--;
  if (cap->srefs != 0)
    {
      /* Someone else came in and got a reference to the almost dead
	 send capability.  Give up.  */
      pthread_mutex_unlock (&cap->lock);
      pthread_mutex_unlock (&sconn->lock);
      return 0;
    }
  
  /* The capability can now finally be removed.  */
  _hurd_cap_sconn_remove (sconn, cap->scid);

  if (cap->orefs == 0)
    {
      /* Return the capability to the pool.  */
      pthread_mutex_unlock (&cap->lock);
      hurd_slab_dealloc (cap_space, (void *) cap);
    }
  else
    pthread_mutex_unlock (&cap->lock);

  return 0;
}


/* Modify the number of object references for the capability CAP by
   DELTA.  */
error_t
hurd_cap_obj_mod_refs (hurd_cap_t cap, int delta)
{
  hurd_cap_ulist_t ulist;

  pthread_mutex_lock (&cap->lock);

  /* Verify that CAP->orefs is not 0 and will not become negative.  */
  if (cap->orefs == 0 || (delta < 0 && cap->orefs < -delta))
    {
      pthread_mutex_unlock (&cap->lock);
      return EINVAL;
    }

  /* Verify that CAP->orefs will not overflow.  */
  if (delta > 0 && cap->orefs > UINT32_MAX - delta)
    {
      pthread_mutex_unlock (&cap->lock);
      return EOVERFLOW;
    }

  cap->orefs += delta;
  if (cap->orefs != 0)
    {
      pthread_mutex_unlock (&cap->lock);
      return 0;
    }

  /* The object is going to be destroyed.  Remove the capability from
     each user.  This is not so easy, though, as some other thread
     might concurrently try to enter a new reference for this
     capability (for example, because of an incoming RPC).  Instead of
     doing reference counting for the capability in each user list, we
     get a temporary reference and acquire the user list lock while
     the capability is temporarily unlocked.  Then we can check if we
     still have to deallocate the capabilty.  */
  ulist = cap->ouser;

  cap->orefs = 1;
  pthread_mutex_unlock (&cap->lock);

  /* Now we can try to remove ourselve from the user lists.
     CAP->ulist will not change while we hold our reference.  */
  pthread_mutex_lock (&ulist->lock);
  pthread_mutex_lock (&cap->lock);

  assert (cap->ouser == ulist);
  assert (cap->orefs != 0);
  cap->orefs--;
  if (cap->orefs != 0)
    {
      /* Someone else came in and got a reference to the almost dead
	 capability object.  Give up.  */
      pthread_mutex_unlock (&cap->lock);
      pthread_mutex_unlock (&ulist->lock);
      return 0;
    }
  
  /* The capability object can now finally be removed.  */
  _hurd_cap_ulist_remove (ulist, cap);
  pthread_mutex_unlock (&ulist->lock);

  if (cap->srefs == 0)
    {
      /* Return the capability to the pool.  */
      pthread_mutex_unlock (&cap->lock);
      hurd_slab_dealloc (cap_space, (void *) cap);
    }
  else
    pthread_mutex_unlock (&cap->lock);

  return 0;

}