Whamcloud - gitweb
new upstream libsysio snapshot (20041101)
[fs/lustre-release.git] / libsysio / src / file_hack.c
1 /*
2  *    This Cplant(TM) source code is the property of Sandia National
3  *    Laboratories.
4  *
5  *    This Cplant(TM) source code is copyrighted by Sandia National
6  *    Laboratories.
7  *
8  *    The redistribution of this Cplant(TM) source code is subject to the
9  *    terms of the GNU Lesser General Public License
10  *    (see cit/LGPL or http://www.gnu.org/licenses/lgpl.html)
11  *
12  *    Cplant(TM) Copyright 1998-2003 Sandia Corporation. 
13  *    Under the terms of Contract DE-AC04-94AL85000, there is a non-exclusive
14  *    license for use of this work by or on behalf of the US Government.
15  *    Export of this program may require a license from the United States
16  *    Government.
17  */
18
19 /*
20  * This library is free software; you can redistribute it and/or
21  * modify it under the terms of the GNU Lesser General Public
22  * License as published by the Free Software Foundation; either
23  * version 2.1 of the License, or (at your option) any later version.
24  * 
25  * This library is distributed in the hope that it will be useful,
26  * but WITHOUT ANY WARRANTY; without even the implied warranty of
27  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28  * Lesser General Public License for more details.
29  * 
30  * You should have received a copy of the GNU Lesser General Public
31  * License along with this library; if not, write to the Free Software
32  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
33  *
34  * Questions or comments about this library should be sent to:
35  *
36  * Lee Ward
37  * Sandia National Laboratories, New Mexico
38  * P.O. Box 5800
39  * Albuquerque, NM 87185-1110
40  *
41  * lee@sandia.gov
42  */
43
44 #include <unistd.h>
45 #include <stdlib.h>
46 #include <assert.h>
47 #include <errno.h>
48 #include <sys/types.h>
49 #include <sys/stat.h>
50 #include <sys/queue.h>
51
52 #include "sysio.h"
53 #include "file.h"
54 #include "inode.h"
55
56 /*
57  * Support for file IO.
58  */
59
60 /*
61  * The open files table
62  */
63 typedef struct oftab {
64         struct file   **table;  /* table array */
65         size_t          size;   /* current table size */
66         int             offset; /* base fd number */
67         int             max;    /* max size */
68 } oftab_t;
69
70 #define OFTAB_NATIVE    (0)
71 #define OFTAB_VIRTUAL   (1)
72
73 static oftab_t _sysio_oftab[2] = {
74         {NULL, 0, 0, 0},
75         {NULL, 0, 0, 1024*1024},
76 };
77
78 static int native_max_fds = 0;
79
80 static inline void init_oftab()
81 {
82         if (!native_max_fds) {
83                 native_max_fds = sysconf(_SC_OPEN_MAX);
84                 if (native_max_fds <= 0)
85                         abort();
86                 _sysio_oftab[OFTAB_NATIVE].max = native_max_fds - 1;
87                 _sysio_oftab[OFTAB_VIRTUAL].offset = native_max_fds;
88         }
89 }
90
91 static inline oftab_t *select_oftab(int fd)
92 {
93         return & _sysio_oftab[fd >= native_max_fds || fd < 0];
94 }
95
96 /*
97  * Create and initialize open file record.
98  */
99 struct file *
100 _sysio_fnew(struct inode *ino, int flags)
101 {
102         struct file *fil;
103
104         fil = malloc(sizeof(struct file));
105         if (!fil)
106                 return NULL;
107
108         _SYSIO_FINIT(fil, ino, flags);
109         I_REF(ino);
110
111         return fil;
112 }
113
114 /*
115  * Destroy open file record.
116  */
117 void
118 _sysio_fgone(struct file *fil)
119 {
120         int     err;
121
122         assert(!fil->f_ref);
123         assert(fil->f_ino);
124         err = (*fil->f_ino->i_ops.inop_close)(fil->f_ino);
125         assert(!err);
126         free(fil);
127 }
128
129 /*
130  * IO operation completion handler.
131  */
132 void
133 _sysio_fcompletio(struct ioctx *ioctx, struct file *fil)
134 {
135         _SYSIO_OFF_T off;
136
137         if (ioctx->ioctx_cc <= 0)
138                 return;
139
140         assert(ioctx->ioctx_ino == fil->f_ino);
141         off = fil->f_pos + ioctx->ioctx_cc;
142         if (fil->f_pos && off <= fil->f_pos)
143                 abort();
144         fil->f_pos = off;
145 }
146
147 /*
148  * Grow (or truncate) the file descriptor table.
149  */
150 static int
151 fd_grow(oftab_t *oftab, size_t n)
152 {
153         int     fd;
154         size_t  count;
155         struct file **noftab, **filp;
156
157         /*
158          * Sanity check the new size.
159          */
160         fd = (int )n;
161         if ((size_t )fd != n)
162                 return -EMFILE;
163
164         n++;    /* index -> size */
165         assert(n > oftab->size);
166
167         if (n > oftab->max)
168                 return -ERANGE;
169
170         if (n < 8)
171                 n = 8;
172         if (n - oftab->size < oftab->size)
173                 n = (n + 1) * 2;
174         noftab = realloc(oftab->table, n * sizeof(struct file *));
175         if (!noftab)
176                 return -ENOMEM;
177         oftab->table = noftab;
178         count = oftab->size;
179         oftab->size = n;
180         if (n < count)
181                 return 0;
182         filp = oftab->table + count;
183         n -= count;
184         while (n--)
185                 *filp++ = NULL;
186         return 0;
187 }
188
189 #if ZERO_SUM_MEMORY
190 static void free_oftab(oftab_t *ot)
191 {
192         if (ot->table) {
193                 free(ot->table);
194                 ot->size = 0;
195         }
196 }
197
198 void
199 _sysio_fd_shutdown()
200 {
201         free_oftab(&_sysio_oftab[OFTAB_NATIVE]);
202         free_oftab(&_sysio_oftab[OFTAB_VIRTUAL]);
203 }
204 #endif
205
206 /*
207  * Find a free slot in the open files table which >= @low
208  * low < 0 means any
209  */
210 static int
211 find_free_fildes(oftab_t *oftab, int low)
212  {
213         int     n;
214         int     err;
215         struct file **filp;
216  
217         if (low < 0)
218                 low = oftab->offset;
219
220         n = low - oftab->offset;
221         if (n < 0)
222                 return -ENFILE;
223
224         for (filp = oftab->table + n;
225              n < oftab->size && *filp;
226              n++, filp++)
227                 ;
228
229         if (n >= oftab->size) {
230                 err = fd_grow(oftab, n);
231                 if (err)
232                         return err;
233                 filp = &oftab->table[n];
234                 assert(!*filp);
235         }
236  
237         return oftab->offset + n;
238 }
239
240 /*
241  * Find open file record from file descriptor.
242  * clear this entry if 'clear' is non-zero
243  */
244 static struct file *
245 __sysio_fd_get(int fd, int clear)
246 {
247         oftab_t *oftab;
248         struct file *file;
249
250         init_oftab();
251
252         if (fd < 0)
253                 return NULL;
254
255         oftab = select_oftab(fd);
256         if (!oftab->table || fd >= oftab->offset + oftab->size)
257                 return NULL;
258
259         file = oftab->table[fd - oftab->offset];
260         if (clear)
261                 oftab->table[fd - oftab->offset] = NULL;
262
263         return file;
264 }
265
266 /*
267  * Find open file record from file descriptor.
268  */
269 struct file *
270 _sysio_fd_find(int fd)
271 {
272         return __sysio_fd_get(fd, 0);
273 }
274
275 /*
276  * Close an open descriptor.
277  */
278 int
279 _sysio_fd_close(int fd)
280 {
281         struct file *fil;
282
283         fil = fil = __sysio_fd_get(fd, 1);
284         if (!fil)
285                 return -EBADF;
286
287         F_RELE(fil);
288
289         return 0;
290 }
291
292 /*
293  * Associate open file record with given file descriptor (if forced), or any
294  * available file descriptor if less than zero, or any available descriptor
295  * greater than or equal to the given one if not forced.
296  */
297 int
298 _sysio_fd_set(struct file *fil, int fd, int force)
299 {
300         int     err;
301         struct file *ofil;
302         oftab_t *oftab;
303
304         if (force && fd < 0)
305                 abort();
306
307         init_oftab();
308
309         oftab = select_oftab(fd);
310
311         /*
312          * Search for a free descriptor if needed.
313          */
314         if (!force) {
315                 fd = find_free_fildes(oftab, fd);
316                 if (fd < 0)
317                         return fd;
318         }
319
320         if (fd - oftab->offset >= oftab->size) {
321                 err = fd_grow(oftab, fd - oftab->offset);
322                 if (err)
323                         return err;
324         }
325
326         /*
327          * Remember old.
328          */
329         ofil = __sysio_fd_get(fd, 1);
330         if (ofil) {
331                 /* FIXME sometimes we could intercept open/socket to create
332                  * a fd, but missing close()? currently we have this problem
333                  * with resolv lib. as a workaround simply destroy the file
334                  * struct here. And this hack will break the behavior of
335                  * DUPFD.
336                  */
337                 if (fd >= 0 && oftab == &_sysio_oftab[0])
338                         free(ofil);
339                 else
340                         F_RELE(ofil);
341         }
342
343         oftab->table[fd - oftab->offset] = fil;
344
345         return fd;
346 }
347
348 /*
349  * Duplicate old file descriptor.
350  *
351  * If the new file descriptor is less than zero, the new file descriptor
352  * is chosen freely. Otherwise, choose an available descriptor greater
353  * than or equal to the new, if not forced. Otherwise, if forced, (re)use
354  * the new.
355  */
356 int
357 _sysio_fd_dup(int oldfd, int newfd, int force)
358 {
359         struct file *fil;
360         int     fd;
361
362         init_oftab();
363
364         if (oldfd == newfd)
365                 return 0;
366
367         fil = _sysio_fd_find(oldfd);
368         if (!fil)
369                 return -EBADF;
370
371         /* old & new must belong to the same oftab */
372         if (select_oftab(oldfd) != select_oftab(newfd))
373                 return -EINVAL;
374
375         fd = _sysio_fd_set(fil, newfd, force);
376         if (fd >= 0)
377                 F_REF(fil);
378         return fd;
379 }
380
381 void
382 _sysio_oftable_close_all(oftab_t *oftab)
383 {
384         struct file **filp;
385         int fd;
386
387         for (fd = 0, filp = oftab->table;
388              (size_t )fd < oftab->size;
389              fd++, filp++) {
390                 if (!*filp)
391                         continue;
392                 F_RELE(*filp);
393                 *filp = NULL;
394         }
395 }
396
397 int
398 _sysio_fd_close_all()
399 {
400         int     fd;
401         struct file **filp;
402         oftab_t *oftab;
403         int i;
404
405         /*
406          * Close all open descriptors.
407          */
408         _sysio_oftable_close_all(&_sysio_oftab[OFTAB_VIRTUAL]);
409         /* XXX see liblustre/llite_lib.c for explaination */
410 #if 0
411         _sysio_oftable_close_all(&_sysio_oftab[OFTAB_NATIVE]);
412 #endif
413
414         /*
415          * Release current working directory.
416          */
417         if (_sysio_cwd) {
418                 P_RELE(_sysio_cwd);
419                 _sysio_cwd = NULL;
420         }
421
422         return 0;
423 }