2008-12-21 12:58:22 +03:00
|
|
|
/* $NetBSD: kern_descrip.c,v 1.185 2008/12/21 09:58:22 ad Exp $ */
|
2008-03-22 00:53:35 +03:00
|
|
|
|
|
|
|
/*-
|
|
|
|
* Copyright (c) 2008 The NetBSD Foundation, Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
|
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
|
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
1994-06-29 10:29:24 +04:00
|
|
|
|
1994-05-17 08:21:49 +04:00
|
|
|
/*
|
1994-05-19 12:13:09 +04:00
|
|
|
* Copyright (c) 1982, 1986, 1989, 1991, 1993
|
|
|
|
* The Regents of the University of California. All rights reserved.
|
1994-05-17 08:21:49 +04:00
|
|
|
* (c) UNIX System Laboratories, Inc.
|
|
|
|
* All or some portions of this file are derived from material licensed
|
|
|
|
* to the University of California by American Telephone and Telegraph
|
|
|
|
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
|
|
|
|
* the permission of UNIX System Laboratories, Inc.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
2003-08-07 20:26:28 +04:00
|
|
|
* 3. Neither the name of the University nor the names of its contributors
|
1994-05-17 08:21:49 +04:00
|
|
|
* may be used to endorse or promote products derived from this software
|
|
|
|
* without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
*
|
1998-03-01 05:20:01 +03:00
|
|
|
* @(#)kern_descrip.c 8.8 (Berkeley) 2/14/95
|
1994-05-17 08:21:49 +04:00
|
|
|
*/
|
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
/*
|
|
|
|
* File descriptor management.
|
|
|
|
*/
|
|
|
|
|
2001-11-12 18:25:01 +03:00
|
|
|
#include <sys/cdefs.h>
|
2008-12-21 12:58:22 +03:00
|
|
|
__KERNEL_RCSID(0, "$NetBSD: kern_descrip.c,v 1.185 2008/12/21 09:58:22 ad Exp $");
|
2001-11-12 18:25:01 +03:00
|
|
|
|
1994-05-17 08:21:49 +04:00
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/filedesc.h>
|
|
|
|
#include <sys/kernel.h>
|
|
|
|
#include <sys/proc.h>
|
|
|
|
#include <sys/file.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/socketvar.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/fcntl.h>
|
1998-09-01 03:55:37 +04:00
|
|
|
#include <sys/pool.h>
|
1994-05-19 12:13:09 +04:00
|
|
|
#include <sys/unistd.h>
|
1994-05-17 08:21:49 +04:00
|
|
|
#include <sys/resourcevar.h>
|
1996-03-31 01:24:38 +03:00
|
|
|
#include <sys/conf.h>
|
2002-10-23 13:10:23 +04:00
|
|
|
#include <sys/event.h>
|
2006-05-15 01:15:11 +04:00
|
|
|
#include <sys/kauth.h>
|
2007-11-29 21:15:14 +03:00
|
|
|
#include <sys/atomic.h>
|
1994-10-20 07:22:35 +03:00
|
|
|
#include <sys/syscallargs.h>
|
2008-04-24 19:35:27 +04:00
|
|
|
#include <sys/cpu.h>
|
2008-11-18 16:01:41 +03:00
|
|
|
#include <sys/kmem.h>
|
|
|
|
#include <sys/vnode.h>
|
1994-10-20 07:22:35 +03:00
|
|
|
|
2007-12-26 19:01:34 +03:00
|
|
|
static int file_ctor(void *, void *, int);
|
|
|
|
static void file_dtor(void *, void *);
|
2008-03-22 00:53:35 +03:00
|
|
|
static int fdfile_ctor(void *, void *, int);
|
|
|
|
static void fdfile_dtor(void *, void *);
|
|
|
|
static int filedesc_ctor(void *, void *, int);
|
|
|
|
static void filedesc_dtor(void *, void *);
|
|
|
|
static int filedescopen(dev_t, int, int, lwp_t *);
|
2007-11-07 03:23:13 +03:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
kmutex_t filelist_lock; /* lock on filehead */
|
2001-02-26 23:24:30 +03:00
|
|
|
struct filelist filehead; /* head of list of open files */
|
2007-12-26 19:01:34 +03:00
|
|
|
u_int nfiles; /* actual number of open files */
|
2007-10-08 19:12:05 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
static pool_cache_t filedesc_cache;
|
2007-11-07 03:23:13 +03:00
|
|
|
static pool_cache_t file_cache;
|
2008-03-22 00:53:35 +03:00
|
|
|
static pool_cache_t fdfile_cache;
|
1994-05-17 08:21:49 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
const struct cdevsw filedesc_cdevsw = {
|
|
|
|
filedescopen, noclose, noread, nowrite, noioctl,
|
|
|
|
nostop, notty, nopoll, nommap, nokqfilter, D_OTHER | D_MPSAFE,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* For ease of reading. */
|
|
|
|
__strong_alias(fd_putvnode,fd_putfile)
|
|
|
|
__strong_alias(fd_putsock,fd_putfile)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize the descriptor system.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
fd_sys_init(void)
|
|
|
|
{
|
|
|
|
|
|
|
|
mutex_init(&filelist_lock, MUTEX_DEFAULT, IPL_NONE);
|
|
|
|
|
2008-03-27 21:30:15 +03:00
|
|
|
file_cache = pool_cache_init(sizeof(file_t), coherency_unit, 0,
|
2008-03-22 00:53:35 +03:00
|
|
|
0, "file", NULL, IPL_NONE, file_ctor, file_dtor, NULL);
|
|
|
|
KASSERT(file_cache != NULL);
|
|
|
|
|
2008-03-27 21:30:15 +03:00
|
|
|
fdfile_cache = pool_cache_init(sizeof(fdfile_t), coherency_unit, 0,
|
2008-03-22 00:53:35 +03:00
|
|
|
PR_LARGECACHE, "fdfile", NULL, IPL_NONE, fdfile_ctor, fdfile_dtor,
|
|
|
|
NULL);
|
|
|
|
KASSERT(fdfile_cache != NULL);
|
|
|
|
|
2008-03-27 21:30:15 +03:00
|
|
|
filedesc_cache = pool_cache_init(sizeof(filedesc_t), coherency_unit,
|
2008-03-22 00:53:35 +03:00
|
|
|
0, 0, "filedesc", NULL, IPL_NONE, filedesc_ctor, filedesc_dtor,
|
|
|
|
NULL);
|
|
|
|
KASSERT(filedesc_cache != NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fd_next_zero(filedesc_t *fdp, uint32_t *bitmap, int want, u_int bits)
|
2003-10-30 10:27:02 +03:00
|
|
|
{
|
|
|
|
int i, off, maxoff;
|
|
|
|
uint32_t sub;
|
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
KASSERT(mutex_owned(&fdp->fd_lock));
|
|
|
|
|
2003-10-30 10:27:02 +03:00
|
|
|
if (want > bits)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
off = want >> NDENTRYSHIFT;
|
|
|
|
i = want & NDENTRYMASK;
|
|
|
|
if (i) {
|
|
|
|
sub = bitmap[off] | ((u_int)~0 >> (NDENTRIES - i));
|
|
|
|
if (sub != ~0)
|
|
|
|
goto found;
|
|
|
|
off++;
|
|
|
|
}
|
|
|
|
|
|
|
|
maxoff = NDLOSLOTS(bits);
|
|
|
|
while (off < maxoff) {
|
|
|
|
if ((sub = bitmap[off]) != ~0)
|
|
|
|
goto found;
|
|
|
|
off++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (-1);
|
|
|
|
|
|
|
|
found:
|
|
|
|
return (off << NDENTRYSHIFT) + ffs(~sub) - 1;
|
|
|
|
}
|
|
|
|
|
2005-06-24 03:15:12 +04:00
|
|
|
static int
|
2008-03-22 00:53:35 +03:00
|
|
|
fd_last_set(filedesc_t *fd, int last)
|
2003-10-30 10:27:02 +03:00
|
|
|
{
|
|
|
|
int off, i;
|
2008-03-22 00:53:35 +03:00
|
|
|
fdfile_t **ofiles = fd->fd_ofiles;
|
2003-10-30 10:27:02 +03:00
|
|
|
uint32_t *bitmap = fd->fd_lomap;
|
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
KASSERT(mutex_owned(&fd->fd_lock));
|
|
|
|
|
2003-10-30 10:27:02 +03:00
|
|
|
off = (last - 1) >> NDENTRYSHIFT;
|
|
|
|
|
2003-11-30 21:16:45 +03:00
|
|
|
while (off >= 0 && !bitmap[off])
|
2003-10-30 10:27:02 +03:00
|
|
|
off--;
|
|
|
|
|
|
|
|
if (off < 0)
|
2005-01-12 23:41:45 +03:00
|
|
|
return (-1);
|
2005-02-27 00:34:55 +03:00
|
|
|
|
2003-10-30 10:27:02 +03:00
|
|
|
i = ((off + 1) << NDENTRYSHIFT) - 1;
|
|
|
|
if (i >= last)
|
|
|
|
i = last - 1;
|
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
/* XXX should use bitmap */
|
|
|
|
/* XXXAD does not work for fd_copy() */
|
|
|
|
while (i > 0 && (ofiles[i] == NULL || !ofiles[i]->ff_allocated))
|
2003-10-30 10:27:02 +03:00
|
|
|
i--;
|
|
|
|
|
|
|
|
return (i);
|
|
|
|
}
|
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
void
|
|
|
|
fd_used(filedesc_t *fdp, unsigned fd)
|
1994-12-04 06:09:50 +03:00
|
|
|
{
|
2003-10-30 10:27:02 +03:00
|
|
|
u_int off = fd >> NDENTRYSHIFT;
|
2008-03-22 00:53:35 +03:00
|
|
|
fdfile_t *ff;
|
|
|
|
|
|
|
|
ff = fdp->fd_ofiles[fd];
|
2003-10-30 10:27:02 +03:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
KASSERT(mutex_owned(&fdp->fd_lock));
|
|
|
|
KASSERT((fdp->fd_lomap[off] & (1 << (fd & NDENTRYMASK))) == 0);
|
|
|
|
KASSERT(ff != NULL);
|
|
|
|
KASSERT(ff->ff_file == NULL);
|
|
|
|
KASSERT(!ff->ff_allocated);
|
2004-04-05 14:10:29 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
ff->ff_allocated = 1;
|
2003-10-30 10:27:02 +03:00
|
|
|
fdp->fd_lomap[off] |= 1 << (fd & NDENTRYMASK);
|
2004-04-05 14:10:29 +04:00
|
|
|
if (fdp->fd_lomap[off] == ~0) {
|
2008-03-22 00:53:35 +03:00
|
|
|
KASSERT((fdp->fd_himap[off >> NDENTRYSHIFT] &
|
2004-04-05 14:10:29 +04:00
|
|
|
(1 << (off & NDENTRYMASK))) == 0);
|
2003-10-30 10:27:02 +03:00
|
|
|
fdp->fd_himap[off >> NDENTRYSHIFT] |= 1 << (off & NDENTRYMASK);
|
2004-04-05 14:10:29 +04:00
|
|
|
}
|
1994-12-04 06:09:50 +03:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
if ((int)fd > fdp->fd_lastfile) {
|
1994-12-04 06:09:50 +03:00
|
|
|
fdp->fd_lastfile = fd;
|
2008-03-22 00:53:35 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (fd >= NDFDFILE) {
|
|
|
|
fdp->fd_nused++;
|
|
|
|
} else {
|
|
|
|
KASSERT(ff == (fdfile_t *)fdp->fd_dfdfile[fd]);
|
|
|
|
}
|
1994-12-04 06:09:50 +03:00
|
|
|
}
|
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
void
|
|
|
|
fd_unused(filedesc_t *fdp, unsigned fd)
|
1994-12-04 06:09:50 +03:00
|
|
|
{
|
2003-10-30 10:27:02 +03:00
|
|
|
u_int off = fd >> NDENTRYSHIFT;
|
2008-03-22 00:53:35 +03:00
|
|
|
fdfile_t *ff;
|
1994-12-04 06:09:50 +03:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
ff = fdp->fd_ofiles[fd];
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Don't assert the lock is held here, as we may be copying
|
|
|
|
* the table during exec() and it is not needed there.
|
|
|
|
* procfs and sysctl are locked out by proc::p_reflock.
|
|
|
|
*
|
|
|
|
* KASSERT(mutex_owned(&fdp->fd_lock));
|
|
|
|
*/
|
|
|
|
KASSERT(ff != NULL);
|
|
|
|
KASSERT(ff->ff_file == NULL);
|
|
|
|
KASSERT(ff->ff_allocated);
|
|
|
|
|
|
|
|
if (fd < fdp->fd_freefile) {
|
1994-12-04 06:09:50 +03:00
|
|
|
fdp->fd_freefile = fd;
|
2008-03-22 00:53:35 +03:00
|
|
|
}
|
2003-10-30 10:27:02 +03:00
|
|
|
|
2004-04-05 14:10:29 +04:00
|
|
|
if (fdp->fd_lomap[off] == ~0) {
|
2008-03-22 00:53:35 +03:00
|
|
|
KASSERT((fdp->fd_himap[off >> NDENTRYSHIFT] &
|
2004-04-05 14:10:29 +04:00
|
|
|
(1 << (off & NDENTRYMASK))) != 0);
|
|
|
|
fdp->fd_himap[off >> NDENTRYSHIFT] &=
|
|
|
|
~(1 << (off & NDENTRYMASK));
|
|
|
|
}
|
2008-03-22 00:53:35 +03:00
|
|
|
KASSERT((fdp->fd_lomap[off] & (1 << (fd & NDENTRYMASK))) != 0);
|
2003-10-30 10:27:02 +03:00
|
|
|
fdp->fd_lomap[off] &= ~(1 << (fd & NDENTRYMASK));
|
2008-03-22 00:53:35 +03:00
|
|
|
ff->ff_allocated = 0;
|
2003-10-30 10:27:02 +03:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
KASSERT(fd <= fdp->fd_lastfile);
|
|
|
|
if (fd == fdp->fd_lastfile) {
|
|
|
|
fdp->fd_lastfile = fd_last_set(fdp, fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fd >= NDFDFILE) {
|
|
|
|
KASSERT(fdp->fd_nused > 0);
|
|
|
|
fdp->fd_nused--;
|
|
|
|
} else {
|
|
|
|
KASSERT(ff == (fdfile_t *)fdp->fd_dfdfile[fd]);
|
|
|
|
}
|
1994-12-04 06:09:50 +03:00
|
|
|
}
|
|
|
|
|
2003-02-23 17:37:32 +03:00
|
|
|
/*
|
2008-03-22 00:53:35 +03:00
|
|
|
* Custom version of fd_unused() for fd_copy(), where the descriptor
|
|
|
|
* table is not yet fully initialized.
|
2003-02-23 17:37:32 +03:00
|
|
|
*/
|
2008-03-22 00:53:35 +03:00
|
|
|
static inline void
|
|
|
|
fd_zap(filedesc_t *fdp, unsigned fd)
|
2001-06-15 00:32:41 +04:00
|
|
|
{
|
2008-03-22 00:53:35 +03:00
|
|
|
u_int off = fd >> NDENTRYSHIFT;
|
2001-06-15 00:32:41 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
if (fd < fdp->fd_freefile) {
|
|
|
|
fdp->fd_freefile = fd;
|
2007-10-08 19:12:05 +04:00
|
|
|
}
|
2001-06-15 00:32:41 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
if (fdp->fd_lomap[off] == ~0) {
|
|
|
|
KASSERT((fdp->fd_himap[off >> NDENTRYSHIFT] &
|
|
|
|
(1 << (off & NDENTRYMASK))) != 0);
|
|
|
|
fdp->fd_himap[off >> NDENTRYSHIFT] &=
|
|
|
|
~(1 << (off & NDENTRYMASK));
|
2003-02-23 17:37:32 +03:00
|
|
|
}
|
2008-03-22 00:53:35 +03:00
|
|
|
KASSERT((fdp->fd_lomap[off] & (1 << (fd & NDENTRYMASK))) != 0);
|
|
|
|
fdp->fd_lomap[off] &= ~(1 << (fd & NDENTRYMASK));
|
2001-06-15 00:32:41 +04:00
|
|
|
}
|
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
bool
|
|
|
|
fd_isused(filedesc_t *fdp, unsigned fd)
|
2005-06-24 03:15:12 +04:00
|
|
|
{
|
2008-03-22 00:53:35 +03:00
|
|
|
u_int off = fd >> NDENTRYSHIFT;
|
2005-06-24 03:15:12 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
KASSERT(fd < fdp->fd_nfiles);
|
2005-06-24 03:15:12 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
return (fdp->fd_lomap[off] & (1 << (fd & NDENTRYMASK))) != 0;
|
2005-06-24 03:15:12 +04:00
|
|
|
}
|
|
|
|
|
2007-10-08 19:12:05 +04:00
|
|
|
/*
|
2008-03-22 00:53:35 +03:00
|
|
|
* Look up the file structure corresponding to a file descriptor
|
|
|
|
* and return the file, holding a reference on the descriptor.
|
2007-10-08 19:12:05 +04:00
|
|
|
*/
|
2008-03-22 00:53:35 +03:00
|
|
|
inline file_t *
|
|
|
|
fd_getfile(unsigned fd)
|
2007-10-08 19:12:05 +04:00
|
|
|
{
|
2008-03-22 00:53:35 +03:00
|
|
|
filedesc_t *fdp;
|
|
|
|
fdfile_t *ff;
|
|
|
|
file_t *fp;
|
2007-10-08 19:12:05 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
fdp = curlwp->l_fd;
|
2007-10-08 19:12:05 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
/*
|
|
|
|
* Look up the fdfile structure representing this descriptor.
|
|
|
|
* Ensure that we see fd_nfiles before fd_ofiles since we
|
|
|
|
* are doing this unlocked. See fd_tryexpand().
|
|
|
|
*/
|
|
|
|
if (__predict_false(fd >= fdp->fd_nfiles)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
membar_consumer();
|
|
|
|
ff = fdp->fd_ofiles[fd];
|
|
|
|
KASSERT(fd >= NDFDFILE || ff == (fdfile_t *)fdp->fd_dfdfile[fd]);
|
|
|
|
if (__predict_false(ff == NULL)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2007-10-08 19:12:05 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
/*
|
|
|
|
* Now get a reference to the descriptor. Issue a memory
|
|
|
|
* barrier to ensure that we acquire the file pointer _after_
|
|
|
|
* adding a reference. If no memory barrier, we could fetch
|
|
|
|
* a stale pointer.
|
|
|
|
*/
|
|
|
|
atomic_inc_uint(&ff->ff_refcnt);
|
|
|
|
#ifndef __HAVE_ATOMIC_AS_MEMBAR
|
|
|
|
membar_enter();
|
|
|
|
#endif
|
2007-10-08 19:12:05 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
/*
|
|
|
|
* If the file is not open or is being closed then put the
|
|
|
|
* reference back.
|
|
|
|
*/
|
|
|
|
fp = ff->ff_file;
|
|
|
|
if (__predict_true(fp != NULL)) {
|
|
|
|
return fp;
|
|
|
|
}
|
|
|
|
fd_putfile(fd);
|
|
|
|
return NULL;
|
2007-10-08 19:12:05 +04:00
|
|
|
}
|
|
|
|
|
1994-05-17 08:21:49 +04:00
|
|
|
/*
|
2008-03-22 00:53:35 +03:00
|
|
|
* Release a reference to a file descriptor acquired with fd_getfile().
|
1994-05-17 08:21:49 +04:00
|
|
|
*/
|
2008-03-22 00:53:35 +03:00
|
|
|
void
|
|
|
|
fd_putfile(unsigned fd)
|
1994-05-17 08:21:49 +04:00
|
|
|
{
|
2008-03-22 00:53:35 +03:00
|
|
|
filedesc_t *fdp;
|
|
|
|
fdfile_t *ff;
|
|
|
|
u_int u, v;
|
2001-02-26 23:24:30 +03:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
fdp = curlwp->l_fd;
|
|
|
|
ff = fdp->fd_ofiles[fd];
|
1994-05-17 08:21:49 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
KASSERT(fd < fdp->fd_nfiles);
|
|
|
|
KASSERT(ff != NULL);
|
|
|
|
KASSERT((ff->ff_refcnt & FR_MASK) > 0);
|
|
|
|
KASSERT(fd >= NDFDFILE || ff == (fdfile_t *)fdp->fd_dfdfile[fd]);
|
1999-05-06 00:01:01 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
/*
|
|
|
|
* Ensure that any use of the file is complete and globally
|
|
|
|
* visible before dropping the final reference. If no membar,
|
|
|
|
* the current CPU could still access memory associated with
|
|
|
|
* the file after it has been freed or recycled by another
|
|
|
|
* CPU.
|
|
|
|
*/
|
|
|
|
#ifndef __HAVE_ATOMIC_AS_MEMBAR
|
|
|
|
membar_exit();
|
|
|
|
#endif
|
1999-05-06 00:01:01 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
/*
|
|
|
|
* Be optimistic and start out with the assumption that no other
|
|
|
|
* threads are trying to close the descriptor. If the CAS fails,
|
|
|
|
* we lost a race and/or it's being closed.
|
|
|
|
*/
|
|
|
|
for (u = ff->ff_refcnt & FR_MASK;; u = v) {
|
|
|
|
v = atomic_cas_uint(&ff->ff_refcnt, u, u - 1);
|
|
|
|
if (__predict_true(u == v)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (__predict_false((v & FR_CLOSING) != 0)) {
|
|
|
|
break;
|
Rework fdalloc() even further: split fdalloc() into fdalloc() and
fdexpand(). The former will return ENOSPC if there is not space
in the current filedesc table. The latter performs the expansion
of the filedesc table. This means that fdalloc() won't ever block,
and it gives callers an opportunity to clean up before the
potentially-blocking fdexpand() call.
Update all fdalloc() callers to deal with the need-to-fdexpand() case.
Rewrite unp_externalize() to use fdalloc() and fdexpand() in a
safe way, using an algorithm suggested by Bill Sommerfeld:
- Use a temporary array of integers to hold the new filedesc table
indexes. This allows us to repeat the loop if necessary.
- Loop through the array of file *'s, assigning them to filedesc table
slots. If fdalloc() indicates expansion is necessary, undo the
assignments we've done so far, expand, and retry the whole process.
- Once all file *'s have been assigned to slots, update the f_msgcount
and unp_rights counters.
- Right before we return, copy the temporary integer array to the message
buffer, and trim the length as before.
Note that once locking is added to the filedesc array, this entire
operation will be `atomic', in that the lock will be held while
file *'s are assigned to embryonic table slots, thus preventing anything
else from using them.
2001-06-07 05:29:16 +04:00
|
|
|
}
|
1999-05-06 00:01:01 +04:00
|
|
|
}
|
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
/* Another thread is waiting to close the file: join it. */
|
|
|
|
(void)fd_close(fd);
|
1994-05-17 08:21:49 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2008-03-22 00:53:35 +03:00
|
|
|
* Convenience wrapper around fd_getfile() that returns reference
|
|
|
|
* to a vnode.
|
1994-05-17 08:21:49 +04:00
|
|
|
*/
|
1996-02-04 05:15:01 +03:00
|
|
|
int
|
2008-03-22 00:53:35 +03:00
|
|
|
fd_getvnode(unsigned fd, file_t **fpp)
|
1995-09-20 01:40:36 +04:00
|
|
|
{
|
2008-03-22 00:53:35 +03:00
|
|
|
vnode_t *vp;
|
|
|
|
file_t *fp;
|
2001-06-15 00:32:41 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
fp = fd_getfile(fd);
|
|
|
|
if (__predict_false(fp == NULL)) {
|
|
|
|
return EBADF;
|
2003-02-23 17:37:32 +03:00
|
|
|
}
|
2008-03-22 00:53:35 +03:00
|
|
|
if (__predict_false(fp->f_type != DTYPE_VNODE)) {
|
|
|
|
fd_putfile(fd);
|
|
|
|
return EINVAL;
|
1994-05-19 12:13:09 +04:00
|
|
|
}
|
2008-03-22 00:53:35 +03:00
|
|
|
vp = fp->f_data;
|
|
|
|
if (__predict_false(vp->v_type == VBAD)) {
|
|
|
|
/* XXX Is this case really necessary? */
|
|
|
|
fd_putfile(fd);
|
|
|
|
return EBADF;
|
1994-05-17 08:21:49 +04:00
|
|
|
}
|
2008-03-22 00:53:35 +03:00
|
|
|
*fpp = fp;
|
|
|
|
return 0;
|
1994-05-17 08:21:49 +04:00
|
|
|
}
|
|
|
|
|
2005-06-24 03:15:12 +04:00
|
|
|
/*
|
2008-03-22 00:53:35 +03:00
|
|
|
* Convenience wrapper around fd_getfile() that returns reference
|
|
|
|
* to a socket.
|
2005-06-24 03:15:12 +04:00
|
|
|
*/
|
2008-03-22 00:53:35 +03:00
|
|
|
int
|
|
|
|
fd_getsock(unsigned fd, struct socket **sop)
|
2005-06-24 03:15:12 +04:00
|
|
|
{
|
2008-03-22 00:53:35 +03:00
|
|
|
file_t *fp;
|
2005-06-24 03:15:12 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
fp = fd_getfile(fd);
|
|
|
|
if (__predict_false(fp == NULL)) {
|
|
|
|
return EBADF;
|
|
|
|
}
|
|
|
|
if (__predict_false(fp->f_type != DTYPE_SOCKET)) {
|
|
|
|
fd_putfile(fd);
|
|
|
|
return ENOTSOCK;
|
|
|
|
}
|
|
|
|
*sop = fp->f_data;
|
|
|
|
return 0;
|
2005-06-24 03:15:12 +04:00
|
|
|
}
|
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
/*
|
|
|
|
* Look up the file structure corresponding to a file descriptor
|
|
|
|
* and return it with a reference held on the file, not the
|
|
|
|
* descriptor.
|
|
|
|
*
|
|
|
|
* This is heavyweight and only used when accessing descriptors
|
|
|
|
* from a foreign process. The caller must ensure that `p' does
|
|
|
|
* not exit or fork across this call.
|
|
|
|
*
|
|
|
|
* To release the file (not descriptor) reference, use closef().
|
|
|
|
*/
|
|
|
|
file_t *
|
|
|
|
fd_getfile2(proc_t *p, unsigned fd)
|
2007-05-13 03:02:49 +04:00
|
|
|
{
|
2008-03-22 00:53:35 +03:00
|
|
|
filedesc_t *fdp;
|
|
|
|
fdfile_t *ff;
|
|
|
|
file_t *fp;
|
2007-05-13 03:02:49 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
fdp = p->p_fd;
|
|
|
|
mutex_enter(&fdp->fd_lock);
|
|
|
|
if (fd > fdp->fd_nfiles) {
|
|
|
|
mutex_exit(&fdp->fd_lock);
|
|
|
|
return NULL;
|
2007-05-13 03:02:49 +04:00
|
|
|
}
|
2008-03-22 00:53:35 +03:00
|
|
|
if ((ff = fdp->fd_ofiles[fd]) == NULL) {
|
|
|
|
mutex_exit(&fdp->fd_lock);
|
|
|
|
return NULL;
|
2007-05-13 03:02:49 +04:00
|
|
|
}
|
2008-03-22 00:53:35 +03:00
|
|
|
mutex_enter(&ff->ff_lock);
|
|
|
|
if ((fp = ff->ff_file) == NULL) {
|
|
|
|
mutex_exit(&ff->ff_lock);
|
|
|
|
mutex_exit(&fdp->fd_lock);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
mutex_enter(&fp->f_lock);
|
|
|
|
fp->f_count++;
|
|
|
|
mutex_exit(&fp->f_lock);
|
|
|
|
mutex_exit(&ff->ff_lock);
|
|
|
|
mutex_exit(&fdp->fd_lock);
|
2007-05-13 03:02:49 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
return fp;
|
2007-05-13 03:02:49 +04:00
|
|
|
}
|
|
|
|
|
1994-05-17 08:21:49 +04:00
|
|
|
/*
|
2008-03-22 00:53:35 +03:00
|
|
|
* Internal form of close. Must be called with a reference to the
|
|
|
|
* descriptor, and will drop the reference. When all descriptor
|
|
|
|
* references are dropped, releases the descriptor slot and a single
|
|
|
|
* reference to the file structure.
|
1994-05-17 08:21:49 +04:00
|
|
|
*/
|
1996-02-04 05:15:01 +03:00
|
|
|
int
|
2008-03-22 00:53:35 +03:00
|
|
|
fd_close(unsigned fd)
|
1995-09-20 01:40:36 +04:00
|
|
|
{
|
2008-03-22 00:53:35 +03:00
|
|
|
struct flock lf;
|
|
|
|
filedesc_t *fdp;
|
|
|
|
fdfile_t *ff;
|
|
|
|
file_t *fp;
|
|
|
|
proc_t *p;
|
|
|
|
lwp_t *l;
|
|
|
|
|
|
|
|
l = curlwp;
|
2003-01-18 13:06:22 +03:00
|
|
|
p = l->l_proc;
|
2008-03-22 00:53:35 +03:00
|
|
|
fdp = l->l_fd;
|
|
|
|
ff = fdp->fd_ofiles[fd];
|
1994-05-17 08:21:49 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
KASSERT(fd >= NDFDFILE || ff == (fdfile_t *)fdp->fd_dfdfile[fd]);
|
2004-01-05 03:36:49 +03:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
mutex_enter(&ff->ff_lock);
|
|
|
|
KASSERT((ff->ff_refcnt & FR_MASK) > 0);
|
|
|
|
if (ff->ff_file == NULL) {
|
|
|
|
/*
|
|
|
|
* Another user of the file is already closing, and is
|
|
|
|
* waiting for other users of the file to drain. Release
|
|
|
|
* our reference, and wake up the closer.
|
|
|
|
*/
|
|
|
|
atomic_dec_uint(&ff->ff_refcnt);
|
|
|
|
cv_broadcast(&ff->ff_closing);
|
|
|
|
mutex_exit(&ff->ff_lock);
|
2004-01-05 03:36:49 +03:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
/*
|
|
|
|
* An application error, so pretend that the descriptor
|
|
|
|
* was already closed. We can't safely wait for it to
|
|
|
|
* be closed without potentially deadlocking.
|
|
|
|
*/
|
1994-05-17 08:21:49 +04:00
|
|
|
return (EBADF);
|
1999-08-04 00:19:16 +04:00
|
|
|
}
|
2008-03-22 00:53:35 +03:00
|
|
|
KASSERT((ff->ff_refcnt & FR_CLOSING) == 0);
|
1999-08-04 00:19:16 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
/*
|
|
|
|
* There may be multiple users of this file within the process.
|
|
|
|
* Notify existing and new users that the file is closing. This
|
|
|
|
* will prevent them from adding additional uses to this file
|
|
|
|
* while we are closing it.
|
|
|
|
*/
|
|
|
|
fp = ff->ff_file;
|
|
|
|
ff->ff_file = NULL;
|
2008-07-02 20:45:19 +04:00
|
|
|
ff->ff_exclose = false;
|
1994-05-17 08:21:49 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
/*
|
|
|
|
* We expect the caller to hold a descriptor reference - drop it.
|
|
|
|
* The reference count may increase beyond zero at this point due
|
|
|
|
* to an erroneous descriptor reference by an application, but
|
|
|
|
* fd_getfile() will notice that the file is being closed and drop
|
|
|
|
* the reference again.
|
|
|
|
*/
|
|
|
|
#ifndef __HAVE_ATOMIC_AS_MEMBAR
|
|
|
|
membar_producer();
|
|
|
|
#endif
|
|
|
|
if (__predict_false(atomic_dec_uint_nv(&ff->ff_refcnt) != 0)) {
|
|
|
|
/*
|
|
|
|
* Wait for other references to drain. This is typically
|
|
|
|
* an application error - the descriptor is being closed
|
|
|
|
* while still in use.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
atomic_or_uint(&ff->ff_refcnt, FR_CLOSING);
|
|
|
|
/*
|
|
|
|
* Remove any knotes attached to the file. A knote
|
|
|
|
* attached to the descriptor can hold references on it.
|
|
|
|
*/
|
|
|
|
if (!SLIST_EMPTY(&ff->ff_knlist)) {
|
|
|
|
mutex_exit(&ff->ff_lock);
|
|
|
|
knote_fdclose(fd);
|
|
|
|
mutex_enter(&ff->ff_lock);
|
2003-03-22 13:39:47 +03:00
|
|
|
}
|
2008-03-22 00:53:35 +03:00
|
|
|
/*
|
|
|
|
* We need to see the count drop to zero at least once,
|
|
|
|
* in order to ensure that all pre-existing references
|
|
|
|
* have been drained. New references past this point are
|
|
|
|
* of no interest.
|
|
|
|
*/
|
|
|
|
while ((ff->ff_refcnt & FR_MASK) != 0) {
|
|
|
|
cv_wait(&ff->ff_closing, &ff->ff_lock);
|
2003-03-22 13:39:47 +03:00
|
|
|
}
|
2008-03-22 00:53:35 +03:00
|
|
|
atomic_and_uint(&ff->ff_refcnt, ~FR_CLOSING);
|
|
|
|
} else {
|
|
|
|
/* If no references, there must be no knotes. */
|
|
|
|
KASSERT(SLIST_EMPTY(&ff->ff_knlist));
|
1994-05-17 08:21:49 +04:00
|
|
|
}
|
2008-03-22 00:53:35 +03:00
|
|
|
mutex_exit(&ff->ff_lock);
|
1999-05-06 00:01:01 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
/*
|
|
|
|
* POSIX record locking dictates that any close releases ALL
|
|
|
|
* locks owned by this process. This is handled by setting
|
|
|
|
* a flag in the unlock to free ONLY locks obeying POSIX
|
|
|
|
* semantics, and not to free BSD-style file locks.
|
|
|
|
* If the descriptor was in a message, POSIX-style locks
|
|
|
|
* aren't passed with the descriptor.
|
|
|
|
*/
|
|
|
|
if ((p->p_flag & PK_ADVLOCK) != 0 && fp->f_type == DTYPE_VNODE) {
|
|
|
|
lf.l_whence = SEEK_SET;
|
|
|
|
lf.l_start = 0;
|
|
|
|
lf.l_len = 0;
|
|
|
|
lf.l_type = F_UNLCK;
|
|
|
|
(void)VOP_ADVLOCK(fp->f_data, p, F_UNLCK, &lf, F_POSIX);
|
2003-02-23 17:37:32 +03:00
|
|
|
}
|
|
|
|
|
1999-05-06 00:01:01 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
/* Free descriptor slot. */
|
|
|
|
mutex_enter(&fdp->fd_lock);
|
2004-05-31 19:30:55 +04:00
|
|
|
fd_unused(fdp, fd);
|
2008-03-22 00:53:35 +03:00
|
|
|
mutex_exit(&fdp->fd_lock);
|
1994-12-04 06:09:50 +03:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
/* Now drop reference to the file itself. */
|
|
|
|
return closef(fp);
|
1994-05-17 08:21:49 +04:00
|
|
|
}
|
|
|
|
|
2007-03-10 19:50:01 +03:00
|
|
|
/*
|
2008-03-22 00:53:35 +03:00
|
|
|
* Duplicate a file descriptor.
|
2007-03-10 19:50:01 +03:00
|
|
|
*/
|
|
|
|
int
|
2008-07-02 20:45:19 +04:00
|
|
|
fd_dup(file_t *fp, int minfd, int *newp, bool exclose)
|
2007-03-10 19:50:01 +03:00
|
|
|
{
|
2008-03-22 00:53:35 +03:00
|
|
|
proc_t *p;
|
|
|
|
int error;
|
2007-03-10 19:50:01 +03:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
p = curproc;
|
2007-03-10 19:50:01 +03:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
while ((error = fd_alloc(p, minfd, newp)) != 0) {
|
|
|
|
if (error != ENOSPC) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
fd_tryexpand(p);
|
|
|
|
}
|
2007-03-10 19:50:01 +03:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
curlwp->l_fd->fd_ofiles[*newp]->ff_exclose = exclose;
|
|
|
|
fd_affix(p, fp, *newp);
|
|
|
|
return 0;
|
2007-03-10 19:50:01 +03:00
|
|
|
}
|
|
|
|
|
1994-05-19 12:13:09 +04:00
|
|
|
/*
|
2008-03-22 00:53:35 +03:00
|
|
|
* dup2 operation.
|
1994-05-19 12:13:09 +04:00
|
|
|
*/
|
1996-02-04 05:15:01 +03:00
|
|
|
int
|
2008-03-22 00:53:35 +03:00
|
|
|
fd_dup2(file_t *fp, unsigned new)
|
1995-09-20 01:40:36 +04:00
|
|
|
{
|
2008-03-22 00:53:35 +03:00
|
|
|
filedesc_t *fdp;
|
|
|
|
fdfile_t *ff;
|
1994-05-17 08:21:49 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
fdp = curlwp->l_fd;
|
1999-05-06 00:01:01 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
/*
|
|
|
|
* Ensure there are enough slots in the descriptor table,
|
|
|
|
* and allocate an fdfile_t up front in case we need it.
|
|
|
|
*/
|
|
|
|
while (new >= fdp->fd_nfiles) {
|
|
|
|
fd_tryexpand(curproc);
|
|
|
|
}
|
|
|
|
ff = pool_cache_get(fdfile_cache, PR_WAITOK);
|
2001-04-07 13:00:57 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
/*
|
|
|
|
* If there is already a file open, close it. If the file is
|
|
|
|
* half open, wait for it to be constructed before closing it.
|
|
|
|
* XXX Potential for deadlock here?
|
|
|
|
*/
|
|
|
|
mutex_enter(&fdp->fd_lock);
|
|
|
|
while (fd_isused(fdp, new)) {
|
|
|
|
mutex_exit(&fdp->fd_lock);
|
|
|
|
if (fd_getfile(new) != NULL) {
|
|
|
|
(void)fd_close(new);
|
|
|
|
} else {
|
|
|
|
/* XXX Crummy, but unlikely to happen. */
|
|
|
|
kpause("dup2", false, 1, NULL);
|
|
|
|
}
|
|
|
|
mutex_enter(&fdp->fd_lock);
|
|
|
|
}
|
|
|
|
if (fdp->fd_ofiles[new] == NULL) {
|
|
|
|
KASSERT(new >= NDFDFILE);
|
|
|
|
fdp->fd_ofiles[new] = ff;
|
|
|
|
ff = NULL;
|
|
|
|
}
|
|
|
|
fd_used(fdp, new);
|
|
|
|
mutex_exit(&fdp->fd_lock);
|
|
|
|
|
|
|
|
/* Slot is now allocated. Insert copy of the file. */
|
|
|
|
fd_affix(curproc, fp, new);
|
|
|
|
if (ff != NULL) {
|
|
|
|
pool_cache_put(fdfile_cache, ff);
|
|
|
|
}
|
|
|
|
return 0;
|
1994-05-17 08:21:49 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2008-03-22 00:53:35 +03:00
|
|
|
* Drop reference to a file structure.
|
1994-05-17 08:21:49 +04:00
|
|
|
*/
|
1996-02-04 05:15:01 +03:00
|
|
|
int
|
2008-03-22 00:53:35 +03:00
|
|
|
closef(file_t *fp)
|
1995-09-20 01:40:36 +04:00
|
|
|
{
|
2008-03-22 00:53:35 +03:00
|
|
|
struct flock lf;
|
|
|
|
int error;
|
1994-05-19 12:13:09 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
/*
|
|
|
|
* Drop reference. If referenced elsewhere it's still open
|
|
|
|
* and we have nothing more to do.
|
|
|
|
*/
|
|
|
|
mutex_enter(&fp->f_lock);
|
|
|
|
KASSERT(fp->f_count > 0);
|
|
|
|
if (--fp->f_count > 0) {
|
|
|
|
mutex_exit(&fp->f_lock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
KASSERT(fp->f_count == 0);
|
|
|
|
mutex_exit(&fp->f_lock);
|
2002-10-23 13:10:23 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
/* We held the last reference - release locks, close and free. */
|
|
|
|
if ((fp->f_flag & FHASLOCK) && fp->f_type == DTYPE_VNODE) {
|
|
|
|
lf.l_whence = SEEK_SET;
|
|
|
|
lf.l_start = 0;
|
|
|
|
lf.l_len = 0;
|
|
|
|
lf.l_type = F_UNLCK;
|
|
|
|
(void)VOP_ADVLOCK(fp->f_data, fp, F_UNLCK, &lf, F_FLOCK);
|
1994-05-19 12:13:09 +04:00
|
|
|
}
|
2008-03-22 00:53:35 +03:00
|
|
|
if (fp->f_ops != NULL) {
|
|
|
|
error = (*fp->f_ops->fo_close)(fp);
|
|
|
|
} else {
|
|
|
|
error = 0;
|
|
|
|
}
|
|
|
|
ffree(fp);
|
1999-05-06 00:01:01 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
return error;
|
1994-05-17 08:21:49 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate a file descriptor for the process.
|
|
|
|
*/
|
1996-02-04 05:15:01 +03:00
|
|
|
int
|
2008-03-22 00:53:35 +03:00
|
|
|
fd_alloc(proc_t *p, int want, int *result)
|
1994-05-17 08:21:49 +04:00
|
|
|
{
|
2008-03-22 00:53:35 +03:00
|
|
|
filedesc_t *fdp;
|
2004-05-31 19:30:55 +04:00
|
|
|
int i, lim, last, error;
|
2003-10-30 10:27:02 +03:00
|
|
|
u_int off, new;
|
2008-03-22 00:53:35 +03:00
|
|
|
fdfile_t *ff;
|
|
|
|
|
|
|
|
KASSERT(p == curproc || p == &proc0);
|
2001-02-26 23:24:30 +03:00
|
|
|
|
|
|
|
fdp = p->p_fd;
|
2008-03-22 00:53:35 +03:00
|
|
|
ff = pool_cache_get(fdfile_cache, PR_WAITOK);
|
|
|
|
KASSERT(ff->ff_refcnt == 0);
|
|
|
|
KASSERT(ff->ff_file == NULL);
|
1994-05-17 08:21:49 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Search for a free descriptor starting at the higher
|
2008-03-22 00:53:35 +03:00
|
|
|
* of want or fd_freefile.
|
1994-05-17 08:21:49 +04:00
|
|
|
*/
|
2008-03-22 00:53:35 +03:00
|
|
|
mutex_enter(&fdp->fd_lock);
|
|
|
|
KASSERT(fdp->fd_ofiles[0] == (fdfile_t *)fdp->fd_dfdfile[0]);
|
1994-05-19 12:13:09 +04:00
|
|
|
lim = min((int)p->p_rlimit[RLIMIT_NOFILE].rlim_cur, maxfiles);
|
2002-04-28 01:36:50 +04:00
|
|
|
last = min(fdp->fd_nfiles, lim);
|
2008-03-22 00:53:35 +03:00
|
|
|
for (;;) {
|
|
|
|
if ((i = want) < fdp->fd_freefile)
|
|
|
|
i = fdp->fd_freefile;
|
|
|
|
off = i >> NDENTRYSHIFT;
|
|
|
|
new = fd_next_zero(fdp, fdp->fd_himap, off,
|
|
|
|
(last + NDENTRIES - 1) >> NDENTRYSHIFT);
|
|
|
|
if (new == -1)
|
|
|
|
break;
|
|
|
|
i = fd_next_zero(fdp, &fdp->fd_lomap[new],
|
2003-10-30 10:27:02 +03:00
|
|
|
new > off ? 0 : i & NDENTRYMASK, NDENTRIES);
|
|
|
|
if (i == -1) {
|
2005-02-27 00:34:55 +03:00
|
|
|
/*
|
2008-03-22 00:53:35 +03:00
|
|
|
* Free file descriptor in this block was
|
2003-10-30 10:27:02 +03:00
|
|
|
* below want, try again with higher want.
|
|
|
|
*/
|
|
|
|
want = (new + 1) << NDENTRYSHIFT;
|
2008-03-22 00:53:35 +03:00
|
|
|
continue;
|
2003-10-30 10:27:02 +03:00
|
|
|
}
|
|
|
|
i += (new << NDENTRYSHIFT);
|
2008-03-22 00:53:35 +03:00
|
|
|
if (i >= last) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (fdp->fd_ofiles[i] == NULL) {
|
|
|
|
KASSERT(i >= NDFDFILE);
|
|
|
|
fdp->fd_ofiles[i] = ff;
|
|
|
|
} else {
|
|
|
|
pool_cache_put(fdfile_cache, ff);
|
1994-05-17 08:21:49 +04:00
|
|
|
}
|
2008-03-22 00:53:35 +03:00
|
|
|
KASSERT(fdp->fd_ofiles[i]->ff_file == NULL);
|
|
|
|
fd_used(fdp, i);
|
|
|
|
if (want <= fdp->fd_freefile) {
|
|
|
|
fdp->fd_freefile = i;
|
|
|
|
}
|
|
|
|
*result = i;
|
|
|
|
mutex_exit(&fdp->fd_lock);
|
|
|
|
KASSERT(i >= NDFDFILE ||
|
|
|
|
fdp->fd_ofiles[i] == (fdfile_t *)fdp->fd_dfdfile[i]);
|
|
|
|
return 0;
|
2002-04-28 01:36:50 +04:00
|
|
|
}
|
1994-05-17 08:21:49 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
/* No space in current array. Let the caller expand and retry. */
|
2004-05-31 19:30:55 +04:00
|
|
|
error = (fdp->fd_nfiles >= lim) ? EMFILE : ENOSPC;
|
2008-03-22 00:53:35 +03:00
|
|
|
mutex_exit(&fdp->fd_lock);
|
|
|
|
pool_cache_put(fdfile_cache, ff);
|
|
|
|
return error;
|
1994-05-17 08:21:49 +04:00
|
|
|
}
|
|
|
|
|
2008-12-21 12:58:22 +03:00
|
|
|
/*
|
|
|
|
* Allocate memory for the open files array.
|
|
|
|
*/
|
|
|
|
static fdfile_t **
|
|
|
|
fd_ofile_alloc(int n)
|
|
|
|
{
|
|
|
|
uintptr_t *ptr, sz;
|
|
|
|
|
|
|
|
KASSERT(n > NDFILE);
|
|
|
|
|
|
|
|
sz = (n + 2) * sizeof(uintptr_t);
|
|
|
|
ptr = kmem_alloc((size_t)sz, KM_SLEEP);
|
|
|
|
ptr[1] = sz;
|
|
|
|
|
|
|
|
return (fdfile_t **)(ptr + 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Free an open files array.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
fd_ofile_free(int n, fdfile_t **of)
|
|
|
|
{
|
|
|
|
uintptr_t *ptr, sz;
|
|
|
|
|
|
|
|
KASSERT(n > NDFILE);
|
|
|
|
|
|
|
|
sz = (n + 2) * sizeof(uintptr_t);
|
|
|
|
ptr = (uintptr_t *)of - 2;
|
|
|
|
KASSERT(ptr[1] == sz);
|
|
|
|
kmem_free(ptr, sz);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate descriptor bitmap.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
fd_map_alloc(int n, uint32_t **lo, uint32_t **hi)
|
|
|
|
{
|
|
|
|
uint8_t *ptr;
|
|
|
|
size_t szlo, szhi;
|
|
|
|
|
|
|
|
KASSERT(n > NDENTRIES);
|
|
|
|
|
|
|
|
szlo = NDLOSLOTS(n) * sizeof(uint32_t);
|
|
|
|
szhi = NDHISLOTS(n) * sizeof(uint32_t);
|
|
|
|
ptr = kmem_alloc(szlo + szhi, KM_SLEEP);
|
|
|
|
*lo = (uint32_t *)ptr;
|
|
|
|
*hi = (uint32_t *)(ptr + szlo);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Free descriptor bitmap.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
fd_map_free(int n, uint32_t *lo, uint32_t *hi)
|
|
|
|
{
|
|
|
|
size_t szlo, szhi;
|
|
|
|
|
|
|
|
KASSERT(n > NDENTRIES);
|
|
|
|
|
|
|
|
szlo = NDLOSLOTS(n) * sizeof(uint32_t);
|
|
|
|
szhi = NDHISLOTS(n) * sizeof(uint32_t);
|
|
|
|
KASSERT(hi == (uint32_t *)((uint8_t *)lo + szlo));
|
|
|
|
kmem_free(lo, szlo + szhi);
|
|
|
|
}
|
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
/*
|
|
|
|
* Expand a process' descriptor table.
|
|
|
|
*/
|
Rework fdalloc() even further: split fdalloc() into fdalloc() and
fdexpand(). The former will return ENOSPC if there is not space
in the current filedesc table. The latter performs the expansion
of the filedesc table. This means that fdalloc() won't ever block,
and it gives callers an opportunity to clean up before the
potentially-blocking fdexpand() call.
Update all fdalloc() callers to deal with the need-to-fdexpand() case.
Rewrite unp_externalize() to use fdalloc() and fdexpand() in a
safe way, using an algorithm suggested by Bill Sommerfeld:
- Use a temporary array of integers to hold the new filedesc table
indexes. This allows us to repeat the loop if necessary.
- Loop through the array of file *'s, assigning them to filedesc table
slots. If fdalloc() indicates expansion is necessary, undo the
assignments we've done so far, expand, and retry the whole process.
- Once all file *'s have been assigned to slots, update the f_msgcount
and unp_rights counters.
- Right before we return, copy the temporary integer array to the message
buffer, and trim the length as before.
Note that once locking is added to the filedesc array, this entire
operation will be `atomic', in that the lock will be held while
file *'s are assigned to embryonic table slots, thus preventing anything
else from using them.
2001-06-07 05:29:16 +04:00
|
|
|
void
|
2008-03-22 00:53:35 +03:00
|
|
|
fd_tryexpand(proc_t *p)
|
Rework fdalloc() even further: split fdalloc() into fdalloc() and
fdexpand(). The former will return ENOSPC if there is not space
in the current filedesc table. The latter performs the expansion
of the filedesc table. This means that fdalloc() won't ever block,
and it gives callers an opportunity to clean up before the
potentially-blocking fdexpand() call.
Update all fdalloc() callers to deal with the need-to-fdexpand() case.
Rewrite unp_externalize() to use fdalloc() and fdexpand() in a
safe way, using an algorithm suggested by Bill Sommerfeld:
- Use a temporary array of integers to hold the new filedesc table
indexes. This allows us to repeat the loop if necessary.
- Loop through the array of file *'s, assigning them to filedesc table
slots. If fdalloc() indicates expansion is necessary, undo the
assignments we've done so far, expand, and retry the whole process.
- Once all file *'s have been assigned to slots, update the f_msgcount
and unp_rights counters.
- Right before we return, copy the temporary integer array to the message
buffer, and trim the length as before.
Note that once locking is added to the filedesc array, this entire
operation will be `atomic', in that the lock will be held while
file *'s are assigned to embryonic table slots, thus preventing anything
else from using them.
2001-06-07 05:29:16 +04:00
|
|
|
{
|
2008-03-22 00:53:35 +03:00
|
|
|
filedesc_t *fdp;
|
|
|
|
int i, numfiles, oldnfiles;
|
|
|
|
fdfile_t **newofile;
|
|
|
|
uint32_t *newhimap, *newlomap;
|
Rework fdalloc() even further: split fdalloc() into fdalloc() and
fdexpand(). The former will return ENOSPC if there is not space
in the current filedesc table. The latter performs the expansion
of the filedesc table. This means that fdalloc() won't ever block,
and it gives callers an opportunity to clean up before the
potentially-blocking fdexpand() call.
Update all fdalloc() callers to deal with the need-to-fdexpand() case.
Rewrite unp_externalize() to use fdalloc() and fdexpand() in a
safe way, using an algorithm suggested by Bill Sommerfeld:
- Use a temporary array of integers to hold the new filedesc table
indexes. This allows us to repeat the loop if necessary.
- Loop through the array of file *'s, assigning them to filedesc table
slots. If fdalloc() indicates expansion is necessary, undo the
assignments we've done so far, expand, and retry the whole process.
- Once all file *'s have been assigned to slots, update the f_msgcount
and unp_rights counters.
- Right before we return, copy the temporary integer array to the message
buffer, and trim the length as before.
Note that once locking is added to the filedesc array, this entire
operation will be `atomic', in that the lock will be held while
file *'s are assigned to embryonic table slots, thus preventing anything
else from using them.
2001-06-07 05:29:16 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
KASSERT(p == curproc || p == &proc0);
|
Rework fdalloc() even further: split fdalloc() into fdalloc() and
fdexpand(). The former will return ENOSPC if there is not space
in the current filedesc table. The latter performs the expansion
of the filedesc table. This means that fdalloc() won't ever block,
and it gives callers an opportunity to clean up before the
potentially-blocking fdexpand() call.
Update all fdalloc() callers to deal with the need-to-fdexpand() case.
Rewrite unp_externalize() to use fdalloc() and fdexpand() in a
safe way, using an algorithm suggested by Bill Sommerfeld:
- Use a temporary array of integers to hold the new filedesc table
indexes. This allows us to repeat the loop if necessary.
- Loop through the array of file *'s, assigning them to filedesc table
slots. If fdalloc() indicates expansion is necessary, undo the
assignments we've done so far, expand, and retry the whole process.
- Once all file *'s have been assigned to slots, update the f_msgcount
and unp_rights counters.
- Right before we return, copy the temporary integer array to the message
buffer, and trim the length as before.
Note that once locking is added to the filedesc array, this entire
operation will be `atomic', in that the lock will be held while
file *'s are assigned to embryonic table slots, thus preventing anything
else from using them.
2001-06-07 05:29:16 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
fdp = p->p_fd;
|
|
|
|
newhimap = NULL;
|
|
|
|
newlomap = NULL;
|
2004-05-31 19:30:55 +04:00
|
|
|
oldnfiles = fdp->fd_nfiles;
|
|
|
|
|
|
|
|
if (oldnfiles < NDEXTENT)
|
2005-05-30 02:24:14 +04:00
|
|
|
numfiles = NDEXTENT;
|
Rework fdalloc() even further: split fdalloc() into fdalloc() and
fdexpand(). The former will return ENOSPC if there is not space
in the current filedesc table. The latter performs the expansion
of the filedesc table. This means that fdalloc() won't ever block,
and it gives callers an opportunity to clean up before the
potentially-blocking fdexpand() call.
Update all fdalloc() callers to deal with the need-to-fdexpand() case.
Rewrite unp_externalize() to use fdalloc() and fdexpand() in a
safe way, using an algorithm suggested by Bill Sommerfeld:
- Use a temporary array of integers to hold the new filedesc table
indexes. This allows us to repeat the loop if necessary.
- Loop through the array of file *'s, assigning them to filedesc table
slots. If fdalloc() indicates expansion is necessary, undo the
assignments we've done so far, expand, and retry the whole process.
- Once all file *'s have been assigned to slots, update the f_msgcount
and unp_rights counters.
- Right before we return, copy the temporary integer array to the message
buffer, and trim the length as before.
Note that once locking is added to the filedesc array, this entire
operation will be `atomic', in that the lock will be held while
file *'s are assigned to embryonic table slots, thus preventing anything
else from using them.
2001-06-07 05:29:16 +04:00
|
|
|
else
|
2005-05-30 02:24:14 +04:00
|
|
|
numfiles = 2 * oldnfiles;
|
2004-05-31 19:30:55 +04:00
|
|
|
|
2008-12-21 12:58:22 +03:00
|
|
|
newofile = fd_ofile_alloc(numfiles);
|
2005-05-30 02:24:14 +04:00
|
|
|
if (NDHISLOTS(numfiles) > NDHISLOTS(oldnfiles)) {
|
2008-12-21 12:58:22 +03:00
|
|
|
fd_map_alloc(numfiles, &newlomap, &newhimap);
|
2004-05-31 19:30:55 +04:00
|
|
|
}
|
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
mutex_enter(&fdp->fd_lock);
|
|
|
|
KASSERT(fdp->fd_ofiles[0] == (fdfile_t *)fdp->fd_dfdfile[0]);
|
2004-05-31 19:30:55 +04:00
|
|
|
if (fdp->fd_nfiles != oldnfiles) {
|
2008-03-22 00:53:35 +03:00
|
|
|
/* fdp changed; caller must retry */
|
|
|
|
mutex_exit(&fdp->fd_lock);
|
2008-12-21 12:58:22 +03:00
|
|
|
fd_ofile_free(numfiles, newofile);
|
|
|
|
if (NDHISLOTS(numfiles) > NDHISLOTS(oldnfiles)) {
|
|
|
|
fd_map_free(numfiles, newlomap, newhimap);
|
|
|
|
}
|
2008-03-22 00:53:35 +03:00
|
|
|
return;
|
2004-05-31 19:30:55 +04:00
|
|
|
}
|
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
/* Copy the existing ofile array and zero the new portion. */
|
|
|
|
i = sizeof(fdfile_t *) * fdp->fd_nfiles;
|
|
|
|
memcpy(newofile, fdp->fd_ofiles, i);
|
|
|
|
memset((uint8_t *)newofile + i, 0, numfiles * sizeof(fdfile_t *) - i);
|
|
|
|
|
Rework fdalloc() even further: split fdalloc() into fdalloc() and
fdexpand(). The former will return ENOSPC if there is not space
in the current filedesc table. The latter performs the expansion
of the filedesc table. This means that fdalloc() won't ever block,
and it gives callers an opportunity to clean up before the
potentially-blocking fdexpand() call.
Update all fdalloc() callers to deal with the need-to-fdexpand() case.
Rewrite unp_externalize() to use fdalloc() and fdexpand() in a
safe way, using an algorithm suggested by Bill Sommerfeld:
- Use a temporary array of integers to hold the new filedesc table
indexes. This allows us to repeat the loop if necessary.
- Loop through the array of file *'s, assigning them to filedesc table
slots. If fdalloc() indicates expansion is necessary, undo the
assignments we've done so far, expand, and retry the whole process.
- Once all file *'s have been assigned to slots, update the f_msgcount
and unp_rights counters.
- Right before we return, copy the temporary integer array to the message
buffer, and trim the length as before.
Note that once locking is added to the filedesc array, this entire
operation will be `atomic', in that the lock will be held while
file *'s are assigned to embryonic table slots, thus preventing anything
else from using them.
2001-06-07 05:29:16 +04:00
|
|
|
/*
|
2008-03-22 00:53:35 +03:00
|
|
|
* Link old ofiles array into list to be discarded. We defer
|
|
|
|
* freeing until process exit if the descriptor table is visble
|
|
|
|
* to other threads.
|
Rework fdalloc() even further: split fdalloc() into fdalloc() and
fdexpand(). The former will return ENOSPC if there is not space
in the current filedesc table. The latter performs the expansion
of the filedesc table. This means that fdalloc() won't ever block,
and it gives callers an opportunity to clean up before the
potentially-blocking fdexpand() call.
Update all fdalloc() callers to deal with the need-to-fdexpand() case.
Rewrite unp_externalize() to use fdalloc() and fdexpand() in a
safe way, using an algorithm suggested by Bill Sommerfeld:
- Use a temporary array of integers to hold the new filedesc table
indexes. This allows us to repeat the loop if necessary.
- Loop through the array of file *'s, assigning them to filedesc table
slots. If fdalloc() indicates expansion is necessary, undo the
assignments we've done so far, expand, and retry the whole process.
- Once all file *'s have been assigned to slots, update the f_msgcount
and unp_rights counters.
- Right before we return, copy the temporary integer array to the message
buffer, and trim the length as before.
Note that once locking is added to the filedesc array, this entire
operation will be `atomic', in that the lock will be held while
file *'s are assigned to embryonic table slots, thus preventing anything
else from using them.
2001-06-07 05:29:16 +04:00
|
|
|
*/
|
2008-03-22 00:53:35 +03:00
|
|
|
if (oldnfiles > NDFILE) {
|
|
|
|
if ((fdp->fd_refcnt | p->p_nlwps) > 1) {
|
2008-12-21 12:58:22 +03:00
|
|
|
fdp->fd_ofiles[-2] = (void *)fdp->fd_discard;
|
|
|
|
fdp->fd_discard = fdp->fd_ofiles - 2;
|
2008-03-22 00:53:35 +03:00
|
|
|
} else {
|
2008-12-21 12:58:22 +03:00
|
|
|
fd_ofile_free(oldnfiles, fdp->fd_ofiles);
|
2008-03-22 00:53:35 +03:00
|
|
|
}
|
|
|
|
}
|
2003-10-30 10:27:02 +03:00
|
|
|
|
2005-05-30 02:24:14 +04:00
|
|
|
if (NDHISLOTS(numfiles) > NDHISLOTS(oldnfiles)) {
|
2008-03-22 00:53:35 +03:00
|
|
|
i = NDHISLOTS(oldnfiles) * sizeof(uint32_t);
|
|
|
|
memcpy(newhimap, fdp->fd_himap, i);
|
|
|
|
memset((uint8_t *)newhimap + i, 0,
|
2005-05-30 02:24:14 +04:00
|
|
|
NDHISLOTS(numfiles) * sizeof(uint32_t) - i);
|
2003-10-30 10:27:02 +03:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
i = NDLOSLOTS(oldnfiles) * sizeof(uint32_t);
|
|
|
|
memcpy(newlomap, fdp->fd_lomap, i);
|
|
|
|
memset((uint8_t *)newlomap + i, 0,
|
2005-05-30 02:24:14 +04:00
|
|
|
NDLOSLOTS(numfiles) * sizeof(uint32_t) - i);
|
2003-10-30 10:27:02 +03:00
|
|
|
|
2004-05-31 19:30:55 +04:00
|
|
|
if (NDHISLOTS(oldnfiles) > NDHISLOTS(NDFILE)) {
|
2008-12-21 12:58:22 +03:00
|
|
|
fd_map_free(oldnfiles, fdp->fd_lomap, fdp->fd_himap);
|
2003-10-30 10:27:02 +03:00
|
|
|
}
|
|
|
|
fdp->fd_himap = newhimap;
|
|
|
|
fdp->fd_lomap = newlomap;
|
|
|
|
}
|
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
/*
|
|
|
|
* All other modifications must become globally visible before
|
|
|
|
* the change to fd_nfiles. See fd_getfile().
|
|
|
|
*/
|
Rework fdalloc() even further: split fdalloc() into fdalloc() and
fdexpand(). The former will return ENOSPC if there is not space
in the current filedesc table. The latter performs the expansion
of the filedesc table. This means that fdalloc() won't ever block,
and it gives callers an opportunity to clean up before the
potentially-blocking fdexpand() call.
Update all fdalloc() callers to deal with the need-to-fdexpand() case.
Rewrite unp_externalize() to use fdalloc() and fdexpand() in a
safe way, using an algorithm suggested by Bill Sommerfeld:
- Use a temporary array of integers to hold the new filedesc table
indexes. This allows us to repeat the loop if necessary.
- Loop through the array of file *'s, assigning them to filedesc table
slots. If fdalloc() indicates expansion is necessary, undo the
assignments we've done so far, expand, and retry the whole process.
- Once all file *'s have been assigned to slots, update the f_msgcount
and unp_rights counters.
- Right before we return, copy the temporary integer array to the message
buffer, and trim the length as before.
Note that once locking is added to the filedesc array, this entire
operation will be `atomic', in that the lock will be held while
file *'s are assigned to embryonic table slots, thus preventing anything
else from using them.
2001-06-07 05:29:16 +04:00
|
|
|
fdp->fd_ofiles = newofile;
|
2008-03-22 00:53:35 +03:00
|
|
|
membar_producer();
|
2005-05-30 02:24:14 +04:00
|
|
|
fdp->fd_nfiles = numfiles;
|
2008-03-22 00:53:35 +03:00
|
|
|
mutex_exit(&fdp->fd_lock);
|
2004-05-31 19:30:55 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
KASSERT(fdp->fd_ofiles[0] == (fdfile_t *)fdp->fd_dfdfile[0]);
|
Rework fdalloc() even further: split fdalloc() into fdalloc() and
fdexpand(). The former will return ENOSPC if there is not space
in the current filedesc table. The latter performs the expansion
of the filedesc table. This means that fdalloc() won't ever block,
and it gives callers an opportunity to clean up before the
potentially-blocking fdexpand() call.
Update all fdalloc() callers to deal with the need-to-fdexpand() case.
Rewrite unp_externalize() to use fdalloc() and fdexpand() in a
safe way, using an algorithm suggested by Bill Sommerfeld:
- Use a temporary array of integers to hold the new filedesc table
indexes. This allows us to repeat the loop if necessary.
- Loop through the array of file *'s, assigning them to filedesc table
slots. If fdalloc() indicates expansion is necessary, undo the
assignments we've done so far, expand, and retry the whole process.
- Once all file *'s have been assigned to slots, update the f_msgcount
and unp_rights counters.
- Right before we return, copy the temporary integer array to the message
buffer, and trim the length as before.
Note that once locking is added to the filedesc array, this entire
operation will be `atomic', in that the lock will be held while
file *'s are assigned to embryonic table slots, thus preventing anything
else from using them.
2001-06-07 05:29:16 +04:00
|
|
|
}
|
|
|
|
|
1994-05-17 08:21:49 +04:00
|
|
|
/*
|
2008-03-22 00:53:35 +03:00
|
|
|
* Create a new open file structure and allocate a file descriptor
|
|
|
|
* for the current process.
|
1994-05-17 08:21:49 +04:00
|
|
|
*/
|
1996-02-04 05:15:01 +03:00
|
|
|
int
|
2008-03-22 00:53:35 +03:00
|
|
|
fd_allocfile(file_t **resultfp, int *resultfd)
|
1994-05-17 08:21:49 +04:00
|
|
|
{
|
2008-03-22 00:53:35 +03:00
|
|
|
file_t *fp;
|
|
|
|
proc_t *p;
|
|
|
|
int error;
|
1994-05-17 08:21:49 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
p = curproc;
|
2006-07-24 02:06:03 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
while ((error = fd_alloc(p, 0, resultfd)) != 0) {
|
|
|
|
if (error != ENOSPC) {
|
|
|
|
return error;
|
Rework fdalloc() even further: split fdalloc() into fdalloc() and
fdexpand(). The former will return ENOSPC if there is not space
in the current filedesc table. The latter performs the expansion
of the filedesc table. This means that fdalloc() won't ever block,
and it gives callers an opportunity to clean up before the
potentially-blocking fdexpand() call.
Update all fdalloc() callers to deal with the need-to-fdexpand() case.
Rewrite unp_externalize() to use fdalloc() and fdexpand() in a
safe way, using an algorithm suggested by Bill Sommerfeld:
- Use a temporary array of integers to hold the new filedesc table
indexes. This allows us to repeat the loop if necessary.
- Loop through the array of file *'s, assigning them to filedesc table
slots. If fdalloc() indicates expansion is necessary, undo the
assignments we've done so far, expand, and retry the whole process.
- Once all file *'s have been assigned to slots, update the f_msgcount
and unp_rights counters.
- Right before we return, copy the temporary integer array to the message
buffer, and trim the length as before.
Note that once locking is added to the filedesc array, this entire
operation will be `atomic', in that the lock will be held while
file *'s are assigned to embryonic table slots, thus preventing anything
else from using them.
2001-06-07 05:29:16 +04:00
|
|
|
}
|
2008-03-22 00:53:35 +03:00
|
|
|
fd_tryexpand(p);
|
2001-06-06 21:00:00 +04:00
|
|
|
}
|
2003-02-15 00:50:10 +03:00
|
|
|
|
2007-11-07 03:23:13 +03:00
|
|
|
fp = pool_cache_get(file_cache, PR_WAITOK);
|
2008-03-22 00:53:35 +03:00
|
|
|
KASSERT(fp->f_count == 0);
|
|
|
|
fp->f_cred = kauth_cred_get();
|
|
|
|
kauth_cred_hold(fp->f_cred);
|
2007-12-26 19:01:34 +03:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
if (__predict_false(atomic_inc_uint_nv(&nfiles) >= maxfiles)) {
|
|
|
|
fd_abort(p, fp, *resultfd);
|
2000-07-04 19:33:28 +04:00
|
|
|
tablefull("file", "increase kern.maxfiles or MAXFILES");
|
2008-03-22 00:53:35 +03:00
|
|
|
return ENFILE;
|
1994-05-17 08:21:49 +04:00
|
|
|
}
|
2007-12-26 19:01:34 +03:00
|
|
|
|
|
|
|
fp->f_advice = 0;
|
|
|
|
fp->f_msgcount = 0;
|
|
|
|
fp->f_offset = 0;
|
2008-03-22 00:53:35 +03:00
|
|
|
fp->f_iflags = 0;
|
|
|
|
*resultfp = fp;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Successful creation of a new descriptor: make visible to the process.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
fd_affix(proc_t *p, file_t *fp, unsigned fd)
|
|
|
|
{
|
|
|
|
fdfile_t *ff;
|
|
|
|
filedesc_t *fdp;
|
|
|
|
|
|
|
|
KASSERT(p == curproc || p == &proc0);
|
|
|
|
|
|
|
|
/* Add a reference to the file structure. */
|
|
|
|
mutex_enter(&fp->f_lock);
|
|
|
|
fp->f_count++;
|
|
|
|
mutex_exit(&fp->f_lock);
|
2007-12-26 19:01:34 +03:00
|
|
|
|
1994-05-17 08:21:49 +04:00
|
|
|
/*
|
2008-03-22 00:53:35 +03:00
|
|
|
* Insert the new file into the descriptor slot.
|
|
|
|
*
|
|
|
|
* The memory barriers provided by lock activity in this routine
|
|
|
|
* ensure that any updates to the file structure become globally
|
|
|
|
* visible before the file becomes visible to other LWPs in the
|
|
|
|
* current process.
|
1994-05-17 08:21:49 +04:00
|
|
|
*/
|
2008-03-22 00:53:35 +03:00
|
|
|
fdp = p->p_fd;
|
|
|
|
ff = fdp->fd_ofiles[fd];
|
2007-12-26 19:01:34 +03:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
KASSERT(ff != NULL);
|
|
|
|
KASSERT(ff->ff_file == NULL);
|
|
|
|
KASSERT(ff->ff_allocated);
|
|
|
|
KASSERT(fd_isused(fdp, fd));
|
|
|
|
KASSERT(fd >= NDFDFILE ||
|
|
|
|
fdp->fd_ofiles[fd] == (fdfile_t *)fdp->fd_dfdfile[fd]);
|
2007-12-26 19:01:34 +03:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
/* No need to lock in order to make file initially visible. */
|
|
|
|
ff->ff_file = fp;
|
|
|
|
}
|
2007-12-26 19:01:34 +03:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
/*
|
|
|
|
* Abort creation of a new descriptor: free descriptor slot and file.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
fd_abort(proc_t *p, file_t *fp, unsigned fd)
|
|
|
|
{
|
|
|
|
filedesc_t *fdp;
|
|
|
|
fdfile_t *ff;
|
2007-10-08 19:12:05 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
KASSERT(p == curproc || p == &proc0);
|
|
|
|
|
|
|
|
fdp = p->p_fd;
|
|
|
|
ff = fdp->fd_ofiles[fd];
|
|
|
|
|
|
|
|
KASSERT(fd >= NDFDFILE ||
|
|
|
|
fdp->fd_ofiles[fd] == (fdfile_t *)fdp->fd_dfdfile[fd]);
|
|
|
|
|
|
|
|
mutex_enter(&fdp->fd_lock);
|
|
|
|
KASSERT(fd_isused(fdp, fd));
|
|
|
|
fd_unused(fdp, fd);
|
|
|
|
mutex_exit(&fdp->fd_lock);
|
|
|
|
|
|
|
|
if (fp != NULL) {
|
|
|
|
ffree(fp);
|
|
|
|
}
|
1994-05-17 08:21:49 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Free a file descriptor.
|
|
|
|
*/
|
1996-02-04 05:15:01 +03:00
|
|
|
void
|
2008-03-22 00:53:35 +03:00
|
|
|
ffree(file_t *fp)
|
1994-05-17 08:21:49 +04:00
|
|
|
{
|
1999-05-06 00:01:01 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
KASSERT(fp->f_count == 0);
|
1999-05-06 00:01:01 +04:00
|
|
|
|
2007-12-26 19:01:34 +03:00
|
|
|
atomic_dec_uint(&nfiles);
|
|
|
|
kauth_cred_free(fp->f_cred);
|
2007-11-07 03:23:13 +03:00
|
|
|
pool_cache_put(file_cache, fp);
|
1994-05-17 08:21:49 +04:00
|
|
|
}
|
|
|
|
|
2007-12-26 19:01:34 +03:00
|
|
|
static int
|
|
|
|
file_ctor(void *arg, void *obj, int flags)
|
|
|
|
{
|
2008-03-22 00:53:35 +03:00
|
|
|
file_t *fp = obj;
|
2007-12-26 19:01:34 +03:00
|
|
|
|
|
|
|
memset(fp, 0, sizeof(*fp));
|
|
|
|
mutex_init(&fp->f_lock, MUTEX_DEFAULT, IPL_NONE);
|
|
|
|
|
|
|
|
mutex_enter(&filelist_lock);
|
|
|
|
LIST_INSERT_HEAD(&filehead, fp, f_list);
|
|
|
|
mutex_exit(&filelist_lock);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
file_dtor(void *arg, void *obj)
|
|
|
|
{
|
2008-03-22 00:53:35 +03:00
|
|
|
file_t *fp = obj;
|
2007-12-26 19:01:34 +03:00
|
|
|
|
|
|
|
mutex_enter(&filelist_lock);
|
|
|
|
LIST_REMOVE(fp, f_list);
|
|
|
|
mutex_exit(&filelist_lock);
|
|
|
|
|
|
|
|
mutex_destroy(&fp->f_lock);
|
|
|
|
}
|
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
static int
|
|
|
|
fdfile_ctor(void *arg, void *obj, int flags)
|
|
|
|
{
|
|
|
|
fdfile_t *ff = obj;
|
|
|
|
|
|
|
|
memset(ff, 0, sizeof(*ff));
|
|
|
|
mutex_init(&ff->ff_lock, MUTEX_DEFAULT, IPL_NONE);
|
|
|
|
cv_init(&ff->ff_closing, "fdclose");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
fdfile_dtor(void *arg, void *obj)
|
|
|
|
{
|
|
|
|
fdfile_t *ff = obj;
|
|
|
|
|
|
|
|
mutex_destroy(&ff->ff_lock);
|
|
|
|
cv_destroy(&ff->ff_closing);
|
|
|
|
}
|
|
|
|
|
|
|
|
file_t *
|
2008-01-06 02:53:21 +03:00
|
|
|
fgetdummy(void)
|
|
|
|
{
|
2008-03-22 00:53:35 +03:00
|
|
|
file_t *fp;
|
2008-01-06 02:53:21 +03:00
|
|
|
|
|
|
|
fp = kmem_alloc(sizeof(*fp), KM_SLEEP);
|
|
|
|
if (fp != NULL) {
|
|
|
|
memset(fp, 0, sizeof(*fp));
|
|
|
|
mutex_init(&fp->f_lock, MUTEX_DEFAULT, IPL_NONE);
|
|
|
|
}
|
|
|
|
return fp;
|
|
|
|
}
|
2008-03-22 00:53:35 +03:00
|
|
|
|
1999-04-30 22:42:58 +04:00
|
|
|
void
|
2008-03-22 00:53:35 +03:00
|
|
|
fputdummy(file_t *fp)
|
1999-04-30 22:42:58 +04:00
|
|
|
{
|
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
mutex_destroy(&fp->f_lock);
|
|
|
|
kmem_free(fp, sizeof(*fp));
|
1999-04-30 22:42:58 +04:00
|
|
|
}
|
|
|
|
|
1998-01-05 07:51:15 +03:00
|
|
|
/*
|
2008-03-22 00:53:35 +03:00
|
|
|
* Create an initial filedesc structure.
|
1998-01-05 07:51:15 +03:00
|
|
|
*/
|
2008-03-22 00:53:35 +03:00
|
|
|
filedesc_t *
|
|
|
|
fd_init(filedesc_t *fdp)
|
1998-01-05 07:51:15 +03:00
|
|
|
{
|
2008-03-22 00:53:35 +03:00
|
|
|
unsigned fd;
|
1998-01-05 07:51:15 +03:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
if (fdp == NULL) {
|
|
|
|
fdp = pool_cache_get(filedesc_cache, PR_WAITOK);
|
|
|
|
} else {
|
|
|
|
filedesc_ctor(NULL, fdp, PR_WAITOK);
|
|
|
|
}
|
1998-01-05 07:51:15 +03:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
fdp->fd_refcnt = 1;
|
|
|
|
fdp->fd_ofiles = fdp->fd_dfiles;
|
|
|
|
fdp->fd_nfiles = NDFILE;
|
|
|
|
fdp->fd_himap = fdp->fd_dhimap;
|
|
|
|
fdp->fd_lomap = fdp->fd_dlomap;
|
|
|
|
KASSERT(fdp->fd_lastfile == -1);
|
|
|
|
KASSERT(fdp->fd_lastkqfile == -1);
|
|
|
|
KASSERT(fdp->fd_knhash == NULL);
|
|
|
|
|
|
|
|
memset(&fdp->fd_startzero, 0, sizeof(*fdp) -
|
|
|
|
offsetof(filedesc_t, fd_startzero));
|
|
|
|
for (fd = 0; fd < NDFDFILE; fd++) {
|
|
|
|
fdp->fd_ofiles[fd] = (fdfile_t *)fdp->fd_dfdfile[fd];
|
|
|
|
}
|
1998-01-05 07:51:15 +03:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
return fdp;
|
1998-01-05 07:51:15 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize a file descriptor table.
|
|
|
|
*/
|
2008-03-22 00:53:35 +03:00
|
|
|
static int
|
|
|
|
filedesc_ctor(void *arg, void *obj, int flag)
|
1998-01-05 07:51:15 +03:00
|
|
|
{
|
2008-03-22 00:53:35 +03:00
|
|
|
filedesc_t *fdp = obj;
|
|
|
|
int i;
|
1998-01-05 07:51:15 +03:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
memset(fdp, 0, sizeof(*fdp));
|
|
|
|
mutex_init(&fdp->fd_lock, MUTEX_DEFAULT, IPL_NONE);
|
|
|
|
fdp->fd_lastfile = -1;
|
|
|
|
fdp->fd_lastkqfile = -1;
|
1998-01-05 07:51:15 +03:00
|
|
|
|
2008-07-02 18:47:34 +04:00
|
|
|
CTASSERT(sizeof(fdp->fd_dfdfile[0]) >= sizeof(fdfile_t));
|
2008-03-22 00:53:35 +03:00
|
|
|
for (i = 0; i < NDFDFILE; i++) {
|
|
|
|
fdfile_ctor(NULL, fdp->fd_dfdfile[i], PR_WAITOK);
|
|
|
|
}
|
1998-01-05 07:51:15 +03:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
return 0;
|
1998-01-05 07:51:15 +03:00
|
|
|
}
|
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
static void
|
|
|
|
filedesc_dtor(void *arg, void *obj)
|
1998-01-05 07:51:15 +03:00
|
|
|
{
|
2008-03-22 00:53:35 +03:00
|
|
|
filedesc_t *fdp = obj;
|
|
|
|
int i;
|
1998-01-05 07:51:15 +03:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
for (i = 0; i < NDFDFILE; i++) {
|
|
|
|
fdfile_dtor(NULL, fdp->fd_dfdfile[i]);
|
|
|
|
}
|
1998-01-05 07:51:15 +03:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
mutex_destroy(&fdp->fd_lock);
|
1998-01-05 07:51:15 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2008-03-22 00:53:35 +03:00
|
|
|
* Make p2 share p1's filedesc structure.
|
1998-01-05 07:51:15 +03:00
|
|
|
*/
|
|
|
|
void
|
2008-03-22 00:53:35 +03:00
|
|
|
fd_share(struct proc *p2)
|
1998-01-05 07:51:15 +03:00
|
|
|
{
|
2008-03-22 00:53:35 +03:00
|
|
|
filedesc_t *fdp;
|
1998-01-05 07:51:15 +03:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
fdp = curlwp->l_fd;
|
|
|
|
p2->p_fd = fdp;
|
|
|
|
atomic_inc_uint(&fdp->fd_refcnt);
|
1998-01-05 07:51:15 +03:00
|
|
|
}
|
|
|
|
|
1994-05-17 08:21:49 +04:00
|
|
|
/*
|
|
|
|
* Copy a filedesc structure.
|
|
|
|
*/
|
2008-03-22 00:53:35 +03:00
|
|
|
filedesc_t *
|
|
|
|
fd_copy(void)
|
1994-05-17 08:21:49 +04:00
|
|
|
{
|
2008-03-22 00:53:35 +03:00
|
|
|
filedesc_t *newfdp, *fdp;
|
|
|
|
fdfile_t *ff, *fflist, **ffp, **nffp, *ff2;
|
|
|
|
int i, nused, numfiles, lastfile, j, newlast;
|
|
|
|
file_t *fp;
|
1994-05-17 08:21:49 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
fdp = curproc->p_fd;
|
|
|
|
newfdp = pool_cache_get(filedesc_cache, PR_WAITOK);
|
1994-05-17 08:21:49 +04:00
|
|
|
newfdp->fd_refcnt = 1;
|
2004-05-31 19:30:55 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
KASSERT(newfdp->fd_knhash == NULL);
|
|
|
|
KASSERT(newfdp->fd_knhashmask == 0);
|
|
|
|
KASSERT(newfdp->fd_discard == NULL);
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
numfiles = fdp->fd_nfiles;
|
|
|
|
lastfile = fdp->fd_lastfile;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the number of open files fits in the internal arrays
|
|
|
|
* of the open file structure, use them, otherwise allocate
|
|
|
|
* additional memory for the number of descriptors currently
|
|
|
|
* in use.
|
|
|
|
*/
|
|
|
|
if (lastfile < NDFILE) {
|
|
|
|
i = NDFILE;
|
|
|
|
newfdp->fd_ofiles = newfdp->fd_dfiles;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Compute the smallest multiple of NDEXTENT needed
|
|
|
|
* for the file descriptors currently in use,
|
|
|
|
* allowing the table to shrink.
|
|
|
|
*/
|
|
|
|
i = numfiles;
|
|
|
|
while (i >= 2 * NDEXTENT && i > lastfile * 2) {
|
|
|
|
i /= 2;
|
|
|
|
}
|
2008-12-21 12:58:22 +03:00
|
|
|
newfdp->fd_ofiles = fd_ofile_alloc(i);
|
2008-03-22 00:53:35 +03:00
|
|
|
KASSERT(i >= NDFILE);
|
|
|
|
}
|
|
|
|
if (NDHISLOTS(i) <= NDHISLOTS(NDFILE)) {
|
|
|
|
newfdp->fd_himap = newfdp->fd_dhimap;
|
|
|
|
newfdp->fd_lomap = newfdp->fd_dlomap;
|
|
|
|
} else {
|
2008-12-21 12:58:22 +03:00
|
|
|
fd_map_alloc(i, &newfdp->fd_lomap,
|
|
|
|
&newfdp->fd_himap);
|
2008-03-22 00:53:35 +03:00
|
|
|
}
|
1994-05-17 08:21:49 +04:00
|
|
|
|
|
|
|
/*
|
2008-03-22 00:53:35 +03:00
|
|
|
* Allocate and string together fdfile structures.
|
|
|
|
* We abuse fdfile_t::ff_file here, but it will be
|
|
|
|
* cleared before this routine returns.
|
1994-05-17 08:21:49 +04:00
|
|
|
*/
|
2008-03-22 00:53:35 +03:00
|
|
|
nused = fdp->fd_nused;
|
|
|
|
fflist = NULL;
|
|
|
|
for (j = nused; j != 0; j--) {
|
|
|
|
ff = pool_cache_get(fdfile_cache, PR_WAITOK);
|
|
|
|
ff->ff_file = (void *)fflist;
|
|
|
|
fflist = ff;
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_enter(&fdp->fd_lock);
|
|
|
|
if (numfiles == fdp->fd_nfiles && nused == fdp->fd_nused &&
|
|
|
|
lastfile == fdp->fd_lastfile) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
mutex_exit(&fdp->fd_lock);
|
|
|
|
if (i >= NDFILE) {
|
2008-12-21 12:58:22 +03:00
|
|
|
fd_ofile_free(i, newfdp->fd_ofiles);
|
2008-03-22 00:53:35 +03:00
|
|
|
}
|
2004-05-31 19:30:55 +04:00
|
|
|
if (NDHISLOTS(i) > NDHISLOTS(NDFILE)) {
|
2008-12-21 12:58:22 +03:00
|
|
|
fd_map_free(i, newfdp->fd_lomap, newfdp->fd_himap);
|
2004-05-31 19:30:55 +04:00
|
|
|
}
|
2008-03-22 00:53:35 +03:00
|
|
|
while (fflist != NULL) {
|
|
|
|
ff = fflist;
|
|
|
|
fflist = (void *)ff->ff_file;
|
|
|
|
ff->ff_file = NULL;
|
|
|
|
pool_cache_put(fdfile_cache, ff);
|
|
|
|
}
|
2003-10-30 10:27:02 +03:00
|
|
|
}
|
|
|
|
|
1994-05-17 08:21:49 +04:00
|
|
|
newfdp->fd_nfiles = i;
|
2004-05-31 19:30:55 +04:00
|
|
|
newfdp->fd_freefile = fdp->fd_freefile;
|
2008-03-22 00:53:35 +03:00
|
|
|
newfdp->fd_exclose = fdp->fd_exclose;
|
2004-05-31 19:30:55 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
/*
|
|
|
|
* Clear the entries that will not be copied over.
|
|
|
|
* Avoid calling memset with 0 size.
|
|
|
|
*/
|
|
|
|
if (lastfile < (i-1)) {
|
2005-01-12 23:41:45 +03:00
|
|
|
memset(newfdp->fd_ofiles + lastfile + 1, 0,
|
2008-03-22 00:53:35 +03:00
|
|
|
(i - lastfile - 1) * sizeof(file_t **));
|
|
|
|
}
|
|
|
|
if (i < NDENTRIES * NDENTRIES) {
|
2003-11-26 15:42:28 +03:00
|
|
|
i = NDENTRIES * NDENTRIES; /* size of inlined bitmaps */
|
2008-03-22 00:53:35 +03:00
|
|
|
}
|
2003-10-30 10:27:02 +03:00
|
|
|
memcpy(newfdp->fd_himap, fdp->fd_himap, NDHISLOTS(i)*sizeof(uint32_t));
|
|
|
|
memcpy(newfdp->fd_lomap, fdp->fd_lomap, NDLOSLOTS(i)*sizeof(uint32_t));
|
2004-05-31 19:30:55 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
ffp = fdp->fd_ofiles;
|
|
|
|
nffp = newfdp->fd_ofiles;
|
|
|
|
j = imax(lastfile, (NDFDFILE - 1));
|
|
|
|
newlast = -1;
|
|
|
|
KASSERT(j < fdp->fd_nfiles);
|
|
|
|
for (i = 0; i <= j; i++, ffp++, *nffp++ = ff2) {
|
|
|
|
ff = *ffp;
|
|
|
|
/* Install built-in fdfiles even if unused here. */
|
|
|
|
if (i < NDFDFILE) {
|
|
|
|
ff2 = (fdfile_t *)newfdp->fd_dfdfile[i];
|
|
|
|
} else {
|
|
|
|
ff2 = NULL;
|
|
|
|
}
|
|
|
|
/* Determine if descriptor is active in parent. */
|
|
|
|
if (ff == NULL || !fd_isused(fdp, i)) {
|
|
|
|
KASSERT(ff != NULL || i >= NDFDFILE);
|
2004-05-31 19:30:55 +04:00
|
|
|
continue;
|
2008-03-22 00:53:35 +03:00
|
|
|
}
|
|
|
|
mutex_enter(&ff->ff_lock);
|
|
|
|
fp = ff->ff_file;
|
|
|
|
if (fp == NULL) {
|
|
|
|
/* Descriptor is half-open: free slot. */
|
|
|
|
fd_zap(newfdp, i);
|
|
|
|
mutex_exit(&ff->ff_lock);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (fp->f_type == DTYPE_KQUEUE) {
|
|
|
|
/* kqueue descriptors cannot be copied. */
|
|
|
|
fd_zap(newfdp, i);
|
|
|
|
mutex_exit(&ff->ff_lock);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
/* It's active: add a reference to the file. */
|
|
|
|
mutex_enter(&fp->f_lock);
|
|
|
|
fp->f_count++;
|
|
|
|
mutex_exit(&fp->f_lock);
|
|
|
|
/* Consume one fdfile_t to represent it. */
|
|
|
|
if (i >= NDFDFILE) {
|
|
|
|
ff2 = fflist;
|
|
|
|
fflist = (void *)ff2->ff_file;
|
|
|
|
}
|
|
|
|
ff2->ff_file = fp;
|
|
|
|
ff2->ff_exclose = ff->ff_exclose;
|
2008-07-02 20:45:19 +04:00
|
|
|
ff2->ff_allocated = true;
|
2008-03-22 00:53:35 +03:00
|
|
|
mutex_exit(&ff->ff_lock);
|
|
|
|
if (i > newlast) {
|
|
|
|
newlast = i;
|
2002-10-23 13:10:23 +04:00
|
|
|
}
|
|
|
|
}
|
2008-03-22 00:53:35 +03:00
|
|
|
mutex_exit(&fdp->fd_lock);
|
|
|
|
|
|
|
|
/* Discard unused fdfile_t structures. */
|
|
|
|
while (__predict_false(fflist != NULL)) {
|
|
|
|
ff = fflist;
|
|
|
|
fflist = (void *)ff->ff_file;
|
|
|
|
ff->ff_file = NULL;
|
|
|
|
pool_cache_put(fdfile_cache, ff);
|
|
|
|
nused--;
|
|
|
|
}
|
|
|
|
KASSERT(nused >= 0);
|
|
|
|
KASSERT(newfdp->fd_ofiles[0] == (fdfile_t *)newfdp->fd_dfdfile[0]);
|
2004-05-31 19:30:55 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
newfdp->fd_nused = nused;
|
|
|
|
newfdp->fd_lastfile = newlast;
|
2004-05-31 19:30:55 +04:00
|
|
|
|
1994-05-17 08:21:49 +04:00
|
|
|
return (newfdp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Release a filedesc structure.
|
|
|
|
*/
|
|
|
|
void
|
2008-03-22 00:53:35 +03:00
|
|
|
fd_free(void)
|
1994-05-17 08:21:49 +04:00
|
|
|
{
|
2008-03-22 00:53:35 +03:00
|
|
|
filedesc_t *fdp;
|
|
|
|
fdfile_t *ff;
|
|
|
|
file_t *fp;
|
|
|
|
int fd, lastfd;
|
2008-12-21 12:58:22 +03:00
|
|
|
void **discard;
|
2004-05-31 19:30:55 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
fdp = curlwp->l_fd;
|
1994-05-17 08:21:49 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
KASSERT(fdp->fd_ofiles[0] == (fdfile_t *)fdp->fd_dfdfile[0]);
|
1999-05-06 00:01:01 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
if (atomic_dec_uint_nv(&fdp->fd_refcnt) > 0)
|
|
|
|
return;
|
1999-05-06 00:01:01 +04:00
|
|
|
|
|
|
|
/*
|
2008-03-22 00:53:35 +03:00
|
|
|
* Close any files that the process holds open.
|
1999-05-06 00:01:01 +04:00
|
|
|
*/
|
2008-04-09 23:36:59 +04:00
|
|
|
for (fd = 0, lastfd = fdp->fd_nfiles - 1; fd <= lastfd; fd++) {
|
2008-03-22 00:53:35 +03:00
|
|
|
ff = fdp->fd_ofiles[fd];
|
|
|
|
KASSERT(fd >= NDFDFILE ||
|
|
|
|
ff == (fdfile_t *)fdp->fd_dfdfile[fd]);
|
|
|
|
if ((ff = fdp->fd_ofiles[fd]) == NULL)
|
|
|
|
continue;
|
|
|
|
if ((fp = ff->ff_file) != NULL) {
|
|
|
|
/*
|
|
|
|
* Must use fd_close() here as kqueue holds
|
|
|
|
* long term references to descriptors.
|
|
|
|
*/
|
|
|
|
ff->ff_refcnt++;
|
|
|
|
fd_close(fd);
|
|
|
|
}
|
|
|
|
KASSERT(ff->ff_refcnt == 0);
|
|
|
|
KASSERT(ff->ff_file == NULL);
|
|
|
|
KASSERT(!ff->ff_exclose);
|
|
|
|
KASSERT(!ff->ff_allocated);
|
|
|
|
if (fd >= NDFDFILE) {
|
|
|
|
pool_cache_put(fdfile_cache, ff);
|
1999-05-06 00:01:01 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2008-03-22 00:53:35 +03:00
|
|
|
* Clean out the descriptor table for the next user and return
|
|
|
|
* to the cache.
|
1999-05-06 00:01:01 +04:00
|
|
|
*/
|
2008-03-22 00:53:35 +03:00
|
|
|
while ((discard = fdp->fd_discard) != NULL) {
|
2008-12-21 12:58:22 +03:00
|
|
|
fdp->fd_discard = discard[0];
|
|
|
|
kmem_free(discard, (uintptr_t)discard[1]);
|
2005-11-30 01:52:02 +03:00
|
|
|
}
|
2008-03-22 00:53:35 +03:00
|
|
|
if (NDHISLOTS(fdp->fd_nfiles) > NDHISLOTS(NDFILE)) {
|
|
|
|
KASSERT(fdp->fd_himap != fdp->fd_dhimap);
|
|
|
|
KASSERT(fdp->fd_lomap != fdp->fd_dlomap);
|
2008-12-21 12:58:22 +03:00
|
|
|
fd_map_free(fdp->fd_nfiles, fdp->fd_lomap, fdp->fd_himap);
|
2005-11-30 01:52:02 +03:00
|
|
|
}
|
2008-03-22 00:53:35 +03:00
|
|
|
if (fdp->fd_nfiles > NDFILE) {
|
|
|
|
KASSERT(fdp->fd_ofiles != fdp->fd_dfiles);
|
2008-12-21 12:58:22 +03:00
|
|
|
fd_ofile_free(fdp->fd_nfiles, fdp->fd_ofiles);
|
2005-11-30 01:52:02 +03:00
|
|
|
}
|
2008-03-22 00:53:35 +03:00
|
|
|
if (fdp->fd_knhash != NULL) {
|
2008-05-05 21:11:16 +04:00
|
|
|
hashdone(fdp->fd_knhash, HASH_LIST, fdp->fd_knhashmask);
|
2008-03-22 00:53:35 +03:00
|
|
|
fdp->fd_knhash = NULL;
|
|
|
|
fdp->fd_knhashmask = 0;
|
|
|
|
} else {
|
|
|
|
KASSERT(fdp->fd_knhashmask == 0);
|
2005-11-30 01:52:02 +03:00
|
|
|
}
|
2008-03-22 00:53:35 +03:00
|
|
|
fdp->fd_lastkqfile = -1;
|
|
|
|
pool_cache_put(filedesc_cache, fdp);
|
2008-01-27 19:16:50 +03:00
|
|
|
}
|
|
|
|
|
1994-05-17 08:21:49 +04:00
|
|
|
/*
|
|
|
|
* File Descriptor pseudo-device driver (/dev/fd/).
|
|
|
|
*
|
|
|
|
* Opening minor device N dup()s the file (if any) connected to file
|
|
|
|
* descriptor N belonging to the calling process. Note that this driver
|
|
|
|
* consists of only the ``open()'' routine, because all subsequent
|
|
|
|
* references to this file will be direct to the other driver.
|
|
|
|
*/
|
2005-06-24 03:15:12 +04:00
|
|
|
static int
|
2008-03-22 00:53:35 +03:00
|
|
|
filedescopen(dev_t dev, int mode, int type, lwp_t *l)
|
1994-05-17 08:21:49 +04:00
|
|
|
{
|
|
|
|
|
1994-12-14 21:40:27 +03:00
|
|
|
/*
|
2003-09-13 12:32:10 +04:00
|
|
|
* XXX Kludge: set dupfd to contain the value of the
|
2002-04-28 01:31:41 +04:00
|
|
|
* the file descriptor being sought for duplication. The error
|
1994-12-14 21:40:27 +03:00
|
|
|
* return ensures that the vnode for this device will be released
|
|
|
|
* by vn_open. Open will detect this special error and take the
|
|
|
|
* actions in dupfdopen below. Other callers of vn_open or VOP_OPEN
|
|
|
|
* will simply report the error.
|
|
|
|
*/
|
2005-12-11 15:16:03 +03:00
|
|
|
l->l_dupfd = minor(dev); /* XXX */
|
2004-11-30 07:25:43 +03:00
|
|
|
return EDUPFD;
|
1994-12-04 06:09:50 +03:00
|
|
|
}
|
|
|
|
|
1994-12-14 21:40:27 +03:00
|
|
|
/*
|
|
|
|
* Duplicate the specified descriptor to a free descriptor.
|
|
|
|
*/
|
1994-12-04 06:09:50 +03:00
|
|
|
int
|
2008-03-22 00:53:35 +03:00
|
|
|
fd_dupopen(int old, int *new, int mode, int error)
|
1994-12-04 06:09:50 +03:00
|
|
|
{
|
2008-03-22 00:53:35 +03:00
|
|
|
filedesc_t *fdp;
|
|
|
|
fdfile_t *ff;
|
|
|
|
file_t *fp;
|
2001-06-15 00:32:41 +04:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
if ((fp = fd_getfile(old)) == NULL) {
|
|
|
|
return EBADF;
|
|
|
|
}
|
|
|
|
fdp = curlwp->l_fd;
|
|
|
|
ff = fdp->fd_ofiles[old];
|
1999-05-06 00:01:01 +04:00
|
|
|
|
1994-12-04 06:09:50 +03:00
|
|
|
/*
|
1994-12-14 21:40:27 +03:00
|
|
|
* There are two cases of interest here.
|
|
|
|
*
|
2004-11-30 07:25:43 +03:00
|
|
|
* For EDUPFD simply dup (dfd) to file descriptor
|
1994-12-14 21:40:27 +03:00
|
|
|
* (indx) and return.
|
|
|
|
*
|
2004-11-30 07:25:43 +03:00
|
|
|
* For EMOVEFD steal away the file structure from (dfd) and
|
1994-12-14 21:40:27 +03:00
|
|
|
* store it in (indx). (dfd) is effectively closed by
|
|
|
|
* this operation.
|
|
|
|
*
|
|
|
|
* Any other error code is just returned.
|
1994-12-04 06:09:50 +03:00
|
|
|
*/
|
1994-12-14 21:40:27 +03:00
|
|
|
switch (error) {
|
2004-11-30 07:25:43 +03:00
|
|
|
case EDUPFD:
|
1994-12-14 21:40:27 +03:00
|
|
|
/*
|
|
|
|
* Check that the mode the file is being opened for is a
|
|
|
|
* subset of the mode of the existing descriptor.
|
|
|
|
*/
|
2008-03-22 00:53:35 +03:00
|
|
|
if (((mode & (FREAD|FWRITE)) | fp->f_flag) != fp->f_flag) {
|
|
|
|
error = EACCES;
|
|
|
|
break;
|
1999-05-06 00:01:01 +04:00
|
|
|
}
|
2008-03-22 00:53:35 +03:00
|
|
|
|
|
|
|
/* Copy it. */
|
|
|
|
error = fd_dup(fp, 0, new, fdp->fd_ofiles[old]->ff_exclose);
|
|
|
|
break;
|
1994-05-17 08:21:49 +04:00
|
|
|
|
2004-11-30 07:25:43 +03:00
|
|
|
case EMOVEFD:
|
2008-03-22 00:53:35 +03:00
|
|
|
/* Copy it. */
|
|
|
|
error = fd_dup(fp, 0, new, fdp->fd_ofiles[old]->ff_exclose);
|
|
|
|
if (error != 0) {
|
|
|
|
break;
|
|
|
|
}
|
1994-12-04 06:09:50 +03:00
|
|
|
|
2008-03-22 00:53:35 +03:00
|
|
|
/* Steal away the file pointer from 'old'. */
|
|
|
|
(void)fd_close(old);
|
|
|
|
return 0;
|
1994-12-14 21:40:27 +03:00
|
|
|
}
|
2008-03-22 00:53:35 +03:00
|
|
|
|
|
|
|
fd_putfile(old);
|
|
|
|
return error;
|
1994-12-04 06:09:50 +03:00
|
|
|
}
|
1994-05-17 08:21:49 +04:00
|
|
|
|
2003-09-21 23:16:48 +04:00
|
|
|
/*
|
|
|
|
* Sets descriptor owner. If the owner is a process, 'pgid'
|
|
|
|
* is set to positive value, process ID. If the owner is process group,
|
|
|
|
* 'pgid' is set to -pg_id.
|
|
|
|
*/
|
|
|
|
int
|
2008-06-24 14:26:26 +04:00
|
|
|
fsetown(pid_t *pgid, u_long cmd, const void *data)
|
2003-09-21 23:16:48 +04:00
|
|
|
{
|
2005-05-30 02:24:14 +04:00
|
|
|
int id = *(const int *)data;
|
2003-09-21 23:16:48 +04:00
|
|
|
int error;
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case TIOCSPGRP:
|
|
|
|
if (id < 0)
|
|
|
|
return (EINVAL);
|
|
|
|
id = -id;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (id > 0 && !pfind(id))
|
|
|
|
return (ESRCH);
|
2008-03-22 00:53:35 +03:00
|
|
|
else if (id < 0 && (error = pgid_in_session(curproc, -id)))
|
2003-09-21 23:16:48 +04:00
|
|
|
return (error);
|
|
|
|
|
|
|
|
*pgid = id;
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return descriptor owner information. If the value is positive,
|
|
|
|
* it's process ID. If it's negative, it's process group ID and
|
|
|
|
* needs the sign removed before use.
|
|
|
|
*/
|
|
|
|
int
|
2008-06-24 14:26:26 +04:00
|
|
|
fgetown(pid_t pgid, u_long cmd, void *data)
|
2003-09-21 23:16:48 +04:00
|
|
|
{
|
2008-03-22 00:53:35 +03:00
|
|
|
|
2003-09-21 23:16:48 +04:00
|
|
|
switch (cmd) {
|
|
|
|
case TIOCGPGRP:
|
|
|
|
*(int *)data = -pgid;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
*(int *)data = pgid;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Send signal to descriptor owner, either process or process group.
|
|
|
|
*/
|
|
|
|
void
|
2003-09-22 16:59:55 +04:00
|
|
|
fownsignal(pid_t pgid, int signo, int code, int band, void *fdescdata)
|
2003-09-21 23:16:48 +04:00
|
|
|
{
|
|
|
|
struct proc *p1;
|
2007-02-10 00:55:00 +03:00
|
|
|
struct pgrp *pgrp;
|
2005-02-27 00:34:55 +03:00
|
|
|
ksiginfo_t ksi;
|
2003-09-21 23:16:48 +04:00
|
|
|
|
2008-04-24 19:35:27 +04:00
|
|
|
KASSERT(!cpu_intr_p());
|
|
|
|
|
2006-12-06 13:02:22 +03:00
|
|
|
KSI_INIT(&ksi);
|
2003-09-22 16:59:55 +04:00
|
|
|
ksi.ksi_signo = signo;
|
2003-09-21 23:16:48 +04:00
|
|
|
ksi.ksi_code = code;
|
|
|
|
ksi.ksi_band = band;
|
|
|
|
|
2008-04-24 19:35:27 +04:00
|
|
|
mutex_enter(proc_lock);
|
2007-02-10 00:55:00 +03:00
|
|
|
if (pgid > 0 && (p1 = p_find(pgid, PFIND_LOCKED)))
|
2003-09-21 23:16:48 +04:00
|
|
|
kpsignal(p1, &ksi, fdescdata);
|
2007-02-10 00:55:00 +03:00
|
|
|
else if (pgid < 0 && (pgrp = pg_find(-pgid, PFIND_LOCKED)))
|
|
|
|
kpgsignal(pgrp, &ksi, fdescdata, 0);
|
2008-04-24 19:35:27 +04:00
|
|
|
mutex_exit(proc_lock);
|
2003-09-21 23:16:48 +04:00
|
|
|
}
|
2004-11-30 07:25:43 +03:00
|
|
|
|
|
|
|
int
|
2008-03-22 00:53:35 +03:00
|
|
|
fd_clone(file_t *fp, unsigned fd, int flag, const struct fileops *fops,
|
|
|
|
void *data)
|
2004-11-30 07:25:43 +03:00
|
|
|
{
|
2008-03-22 00:53:35 +03:00
|
|
|
|
2005-02-13 02:14:03 +03:00
|
|
|
fp->f_flag = flag;
|
2004-11-30 07:25:43 +03:00
|
|
|
fp->f_type = DTYPE_MISC;
|
|
|
|
fp->f_ops = fops;
|
|
|
|
fp->f_data = data;
|
2008-03-22 00:53:35 +03:00
|
|
|
curlwp->l_dupfd = fd;
|
|
|
|
fd_affix(curproc, fp, fd);
|
2004-11-30 07:25:43 +03:00
|
|
|
|
|
|
|
return EMOVEFD;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2008-03-22 00:53:35 +03:00
|
|
|
fnullop_fcntl(file_t *fp, u_int cmd, void *data)
|
2004-11-30 07:25:43 +03:00
|
|
|
{
|
2006-11-01 13:17:58 +03:00
|
|
|
|
2004-11-30 07:25:43 +03:00
|
|
|
if (cmd == F_SETFL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2008-03-22 00:53:35 +03:00
|
|
|
fnullop_poll(file_t *fp, int which)
|
2004-11-30 07:25:43 +03:00
|
|
|
{
|
2006-11-01 13:17:58 +03:00
|
|
|
|
2004-11-30 07:25:43 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2008-03-22 00:53:35 +03:00
|
|
|
fnullop_kqfilter(file_t *fp, struct knote *kn)
|
2004-11-30 07:25:43 +03:00
|
|
|
{
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-09-07 22:56:02 +04:00
|
|
|
int
|
2008-03-22 00:53:35 +03:00
|
|
|
fbadop_read(file_t *fp, off_t *offset, struct uio *uio,
|
|
|
|
kauth_cred_t cred, int flags)
|
2007-09-07 22:56:02 +04:00
|
|
|
{
|
|
|
|
|
|
|
|
return EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2008-03-22 00:53:35 +03:00
|
|
|
fbadop_write(file_t *fp, off_t *offset, struct uio *uio,
|
|
|
|
kauth_cred_t cred, int flags)
|
2007-09-07 22:56:02 +04:00
|
|
|
{
|
|
|
|
|
|
|
|
return EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2008-03-22 00:53:35 +03:00
|
|
|
fbadop_ioctl(file_t *fp, u_long com, void *data)
|
2007-09-07 22:56:02 +04:00
|
|
|
{
|
|
|
|
|
|
|
|
return EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
2004-11-30 07:25:43 +03:00
|
|
|
int
|
2008-03-22 00:53:35 +03:00
|
|
|
fbadop_stat(file_t *fp, struct stat *sb)
|
2004-11-30 07:25:43 +03:00
|
|
|
{
|
2006-11-01 13:17:58 +03:00
|
|
|
|
2004-11-30 07:25:43 +03:00
|
|
|
return EOPNOTSUPP;
|
|
|
|
}
|
2007-09-07 22:56:02 +04:00
|
|
|
|
|
|
|
int
|
2008-03-22 00:53:35 +03:00
|
|
|
fbadop_close(file_t *fp)
|
2007-09-07 22:56:02 +04:00
|
|
|
{
|
|
|
|
|
|
|
|
return EOPNOTSUPP;
|
|
|
|
}
|