From: Thomas Veerman Date: Wed, 21 Dec 2011 10:52:51 +0000 (+0000) Subject: AVFS: Use scratchpad instead of m_in to pass around file descriptors X-Git-Tag: v3.2.0~164 X-Git-Url: http://zhaoyanbai.com/repos/%22http:/www.isc.org/icons/rndc-confgen.html?a=commitdiff_plain;h=de5a9a3e8bbfec66aefafb16e109a0680ef13f97;p=minix.git AVFS: Use scratchpad instead of m_in to pass around file descriptors Some code relies on having the file descriptor in m_in.fd. Consequently, m_in is not only used to provide syscall parameters from user space to VFS, but also as a global variable to store temporary data within VFS. This has the ugly side effect that m_in gets overwritten during core dumping.* To work around this problem VFS now uses a so called "scratchpad" to store temporary data that has to be globally accessible. This is a simple table indexed by process number, just like fproc. The scratchpad allows us to store the buffer pointer and buffer size for suspended system calls (i.e., read, write, open, lock) instead of using fproc. This makes fproc a bit smaller and fproc iterators a bit faster. Moreover, suspension of processes becomes simpler altogether and suspended operations on pipes are now less of a special case. * This patch fixes a bug where due to unexpected m_in overwriting a coredump would fail, and consequently resources are leaked. The coredump was triggered with: $ a() { a; } $ a --- diff --git a/servers/avfs/device.c b/servers/avfs/device.c index 21b42dddc..29130df29 100644 --- a/servers/avfs/device.c +++ b/servers/avfs/device.c @@ -32,6 +32,7 @@ #include #include "file.h" #include "fproc.h" +#include "scratchpad.h" #include "dmap.h" #include #include "vnode.h" @@ -878,7 +879,8 @@ PUBLIC int clone_opcl( } /* Drop old node and use the new values */ - vp = fp->fp_filp[m_in.fd]->filp_vno; + assert(FD_ISSET(scratch(fp).file.fd_nr, &fp->fp_filp_inuse)); + vp = fp->fp_filp[scratch(fp).file.fd_nr]->filp_vno; unlock_vnode(vp); put_vnode(vp); @@ -896,7 +898,7 @@ PUBLIC int clone_opcl( vp->v_sdev = dev; vp->v_fs_count = 1; vp->v_ref_count = 1; - fp->fp_filp[m_in.fd]->filp_vno = vp; + fp->fp_filp[scratch(fp).file.fd_nr]->filp_vno = vp; } dev_mess.REP_STATUS = OK; } @@ -983,9 +985,9 @@ PUBLIC void cdev_up(int maj) if(rfp->fp_pid == PID_FREE) continue; if(rfp->fp_blocked_on != FP_BLOCKED_ON_DOPEN) continue; + fd_nr = scratch(rfp).file.fd_nr; printf("VFS: dev_up: found process in FP_BLOCKED_ON_DOPEN, fd %d\n", - rfp->fp_blocked.fd_nr); - fd_nr = rfp->fp_blocked.fd_nr; + fd_nr); rfilp = rfp->fp_filp[fd_nr]; vp = rfilp->filp_vno; if (!vp) panic("VFS: restart_reopen: no vp"); @@ -1091,7 +1093,7 @@ int maj; if (rfp->fp_blocked_on == FP_BLOCKED_ON_DOPEN || !(rfp->fp_flags & FP_SUSP_REOPEN)) continue; - fd_nr = rfp->fp_blocked.fd_nr; + fd_nr = scratch(rfp).file.fd_nr; printf("VFS: restart_reopen: process in FP_BLOCKED_ON_DOPEN fd=%d\n", fd_nr); rfilp = rfp->fp_filp[fd_nr]; diff --git a/servers/avfs/fproc.h b/servers/avfs/fproc.h index ce33ecd52..f0ae8419d 100644 --- a/servers/avfs/fproc.h +++ b/servers/avfs/fproc.h @@ -25,14 +25,9 @@ EXTERN struct fproc { fd_set fp_cloexec_set; /* bit map for POSIX Table 6-2 FD_CLOEXEC */ dev_t fp_tty; /* major/minor of controlling tty */ + int fp_blocked_on; /* what is it blocked on */ int fp_block_callnr; /* blocked call if rd/wr can't finish */ - union blocked { - int fd_nr; /* place to save fd if rd/wr can't finish */ - struct filp *bfilp; /* place to save filp if rd/wr can't finish */ - } fp_blocked; - char *fp_buffer; /* place to save buffer if rd/wr can't finish*/ - int fp_nbytes; /* place to save bytes if rd/wr can't finish */ int fp_cum_io_partial; /* partial byte count if rd/wr can't finish */ endpoint_t fp_task; /* which task is proc suspended on */ endpoint_t fp_ioproc; /* proc no. in suspended-on i/o message */ @@ -46,6 +41,7 @@ EXTERN struct fproc { int fp_ngroups; /* number of supplemental groups */ gid_t fp_sgroups[NGROUPS_MAX];/* supplemental groups */ mode_t fp_umask; /* mask set by umask system call */ + message *fp_sendrec; /* request/reply to/from FS/driver */ mutex_t fp_lock; /* mutex to lock fproc object */ struct job fp_job; /* pending job */ diff --git a/servers/avfs/glo.h b/servers/avfs/glo.h index 95a38f02b..0995c144b 100644 --- a/servers/avfs/glo.h +++ b/servers/avfs/glo.h @@ -33,6 +33,7 @@ EXTERN message m_out; /* the output message used for reply */ #endif # define call_nr (m_in.m_type) # define super_user (fp->fp_effuid == SU_UID ? 1 : 0) +# define scratch(p) (scratchpad[((int) ((p) - fproc))]) EXTERN struct worker_thread *self; EXTERN endpoint_t receive_from;/* endpoint with pending reply */ EXTERN int force_sync; /* toggle forced synchronous communication */ diff --git a/servers/avfs/lock.c b/servers/avfs/lock.c index 58eddb077..36ab59351 100644 --- a/servers/avfs/lock.c +++ b/servers/avfs/lock.c @@ -12,6 +12,7 @@ #include #include "file.h" #include "fproc.h" +#include "scratchpad.h" #include "lock.h" #include "vnode.h" #include "param.h" @@ -29,13 +30,11 @@ int req; /* either F_SETLK or F_SETLKW */ mode_t mo; off_t first, last; struct flock flock; - vir_bytes user_flock; struct file_lock *flp, *flp2, *empty; /* Fetch the flock structure from user space. */ - user_flock = (vir_bytes) m_in.name1; - r = sys_datacopy(who_e, (vir_bytes) user_flock, VFS_PROC_NR, - (vir_bytes) &flock, (phys_bytes) sizeof(flock)); + r = sys_datacopy(who_e, (vir_bytes) scratch(fp).io.io_buffer, VFS_PROC_NR, + (vir_bytes) &flock, sizeof(flock)); if (r != OK) return(EINVAL); /* Make some error checks. */ @@ -149,7 +148,7 @@ int req; /* either F_SETLK or F_SETLKW */ /* Copy the flock structure back to the caller. */ r = sys_datacopy(VFS_PROC_NR, (vir_bytes) &flock, - who_e, (vir_bytes) user_flock, (phys_bytes) sizeof(flock)); + who_e, (vir_bytes) scratch(fp).io.io_buffer, sizeof(flock)); return(r); } diff --git a/servers/avfs/main.c b/servers/avfs/main.c index 02deb4ce9..85ac294b8 100644 --- a/servers/avfs/main.c +++ b/servers/avfs/main.c @@ -29,6 +29,7 @@ #include "file.h" #include "dmap.h" #include "fproc.h" +#include "scratchpad.h" #include "vmnt.h" #include "vnode.h" #include "job.h" @@ -348,15 +349,15 @@ PRIVATE void *do_pending_pipe(void *arg) lock_proc(fp, 1 /* force lock */); - f = fp->fp_blocked.bfilp; + f = scratch(fp).file.filp; assert(f != NULL); - fp->fp_blocked.bfilp = NULL; + scratch(fp).file.filp = NULL; locktype = (call_nr == READ) ? VNODE_READ : VNODE_WRITE; op = (call_nr == READ) ? READING : WRITING; lock_filp(f, locktype); - r = rw_pipe(op, who_e, f, fp->fp_buffer, fp->fp_nbytes); + r = rw_pipe(op, who_e, f, scratch(fp).io.io_buffer, scratch(fp).io.io_nbytes); if (r != SUSPEND) /* Do we have results to report? */ reply(who_e, r); @@ -821,14 +822,14 @@ PRIVATE void service_pm_postponed(void) /* Copy parameters first. m_in gets overwritten when creating core * file. */ - m_out.m_type = PM_CORE_REPLY; - m_out.PM_PROC = m_in.PM_PROC; - m_out.PM_TRACED_PROC = m_in.PM_TRACED_PROC; r = pm_dumpcore(m_in.PM_PROC, m_in.PM_TERM_SIG, (vir_bytes) m_in.PM_PATH); /* Reply status to PM */ + m_out.m_type = PM_CORE_REPLY; + m_out.PM_PROC = m_in.PM_PROC; + m_out.PM_TRACED_PROC = m_in.PM_TRACED_PROC; m_out.PM_STATUS = r; break; @@ -961,9 +962,9 @@ struct fproc *rfp; fp = rfp; blocked_on = rfp->fp_blocked_on; m_in.m_type = rfp->fp_block_callnr; - m_in.fd = rfp->fp_blocked.fd_nr; - m_in.buffer = rfp->fp_buffer; - m_in.nbytes = rfp->fp_nbytes; + m_in.fd = scratch(fp).file.fd_nr; + m_in.buffer = scratch(fp).io.io_buffer; + m_in.nbytes = scratch(fp).io.io_nbytes; rfp->fp_blocked_on = FP_BLOCKED_ON_NONE; /* no longer blocked */ rfp->fp_flags &= ~FP_REVIVED; diff --git a/servers/avfs/misc.c b/servers/avfs/misc.c index 2cb7f3b8b..08a1afa48 100644 --- a/servers/avfs/misc.c +++ b/servers/avfs/misc.c @@ -33,6 +33,7 @@ #include #include "file.h" #include "fproc.h" +#include "scratchpad.h" #include "dmap.h" #include #include "vnode.h" @@ -157,9 +158,14 @@ PUBLIC int do_fcntl() int new_fd, fl, r = OK; tll_access_t locktype; + scratch(fp).file.fd_nr = m_in.fd; + scratch(fp).io.io_buffer = m_in.buffer; + scratch(fp).io.io_nbytes = m_in.nbytes; /* a.k.a. m_in.request */ + /* Is the file descriptor valid? */ locktype = (m_in.request == F_FREESP) ? VNODE_WRITE : VNODE_READ; - if ((f = get_filp(m_in.fd, locktype)) == NULL) return(err_code); + if ((f = get_filp(scratch(fp).file.fd_nr, locktype)) == NULL) + return(err_code); switch (m_in.request) { case F_DUPFD: @@ -175,15 +181,17 @@ PUBLIC int do_fcntl() case F_GETFD: /* Get close-on-exec flag (FD_CLOEXEC in POSIX Table 6-2). */ - r = FD_ISSET(m_in.fd, &fp->fp_cloexec_set) ? FD_CLOEXEC : 0; + r = 0; + if (FD_ISSET(scratch(fp).file.fd_nr, &fp->fp_cloexec_set)) + r = FD_CLOEXEC; break; case F_SETFD: /* Set close-on-exec flag (FD_CLOEXEC in POSIX Table 6-2). */ if(m_in.addr & FD_CLOEXEC) - FD_SET(m_in.fd, &fp->fp_cloexec_set); + FD_SET(scratch(fp).file.fd_nr, &fp->fp_cloexec_set); else - FD_CLR(m_in.fd, &fp->fp_cloexec_set); + FD_CLR(scratch(fp).file.fd_nr, &fp->fp_cloexec_set); break; case F_GETFL: @@ -207,9 +215,7 @@ PUBLIC int do_fcntl() case F_FREESP: { - /* Free a section of a file. Preparation is done here, actual freeing - * in freesp_inode(). - */ + /* Free a section of a file */ off_t start, end; struct flock flock_arg; signed long offset; diff --git a/servers/avfs/open.c b/servers/avfs/open.c index 5358c3171..dc8948ca6 100644 --- a/servers/avfs/open.c +++ b/servers/avfs/open.c @@ -21,6 +21,7 @@ #include #include "file.h" #include "fproc.h" +#include "scratchpad.h" #include "dmap.h" #include "lock.h" #include "param.h" @@ -99,7 +100,7 @@ PUBLIC int common_open(char path[PATH_MAX], int oflags, mode_t omode) if (!bits) return(EINVAL); /* See if file descriptor and filp slots are available. */ - if ((r = get_fd(0, bits, &m_in.fd, &filp)) != OK) return(r); + if ((r = get_fd(0, bits, &(scratch(fp).file.fd_nr), &filp)) != OK) return(r); lookup_init(&resolve, path, PATH_NOFLAGS, &vmp, &vp); @@ -129,8 +130,8 @@ PUBLIC int common_open(char path[PATH_MAX], int oflags, mode_t omode) } /* Claim the file descriptor and filp slot and fill them in. */ - fp->fp_filp[m_in.fd] = filp; - FD_SET(m_in.fd, &fp->fp_filp_inuse); + fp->fp_filp[scratch(fp).file.fd_nr] = filp; + FD_SET(scratch(fp).file.fd_nr, &fp->fp_filp_inuse); filp->filp_count = 1; filp->filp_vno = vp; filp->filp_flags = oflags; @@ -242,7 +243,7 @@ PUBLIC int common_open(char path[PATH_MAX], int oflags, mode_t omode) filp->filp_count = 0; /* don't find self */ if ((filp2 = find_filp(vp, b)) != NULL) { /* Co-reader or writer found. Use it.*/ - fp->fp_filp[m_in.fd] = filp2; + fp->fp_filp[scratch(fp).file.fd_nr] = filp2; filp2->filp_count++; filp2->filp_vno = vp; filp2->filp_flags = oflags; @@ -271,14 +272,14 @@ PUBLIC int common_open(char path[PATH_MAX], int oflags, mode_t omode) /* If error, release inode. */ if (r != OK) { if (r != SUSPEND) { - fp->fp_filp[m_in.fd] = NULL; - FD_CLR(m_in.fd, &fp->fp_filp_inuse); + fp->fp_filp[scratch(fp).file.fd_nr] = NULL; + FD_CLR(scratch(fp).file.fd_nr, &fp->fp_filp_inuse); filp->filp_count = 0; filp->filp_vno = NULL; put_vnode(vp); } } else { - r = m_in.fd; + r = scratch(fp).file.fd_nr; } return(r); diff --git a/servers/avfs/pipe.c b/servers/avfs/pipe.c index 2a841ba19..b339f6baf 100644 --- a/servers/avfs/pipe.c +++ b/servers/avfs/pipe.c @@ -27,6 +27,7 @@ #include #include "file.h" #include "fproc.h" +#include "scratchpad.h" #include "dmap.h" #include "param.h" #include "select.h" @@ -284,9 +285,6 @@ PUBLIC void suspend(int why) */ #if DO_SANITYCHECKS - if (why == FP_BLOCKED_ON_PIPE) - panic("suspend: called for FP_BLOCKED_ON_PIPE"); - if(fp_is_blocked(fp)) panic("suspend: called for suspended process"); @@ -294,29 +292,16 @@ PUBLIC void suspend(int why) panic("suspend: called for FP_BLOCKED_ON_NONE"); #endif - if (why == FP_BLOCKED_ON_POPEN) + if (why == FP_BLOCKED_ON_POPEN || why == FP_BLOCKED_ON_PIPE) /* #procs susp'ed on pipe*/ susp_count++; - if (why == FP_BLOCKED_ON_POPEN || why == FP_BLOCKED_ON_DOPEN || - why == FP_BLOCKED_ON_LOCK || why == FP_BLOCKED_ON_OTHER) - fp->fp_blocked.fd_nr = m_in.fd; - else - fp->fp_blocked.fd_nr = 0; - fp->fp_blocked_on = why; assert(fp->fp_grant == GRANT_INVALID || !GRANT_VALID(fp->fp_grant)); fp->fp_block_callnr = call_nr; fp->fp_flags &= ~FP_SUSP_REOPEN; /* Clear this flag. The caller * can set it when needed. */ - if (why == FP_BLOCKED_ON_LOCK) { - fp->fp_buffer = (char *) m_in.name1; /* third arg to fcntl() */ - fp->fp_nbytes = m_in.request; /* second arg to fcntl() */ - } else { - fp->fp_buffer = m_in.buffer; /* for reads and writes */ - fp->fp_nbytes = m_in.nbytes; - } } /*===========================================================================* @@ -334,30 +319,23 @@ PUBLIC void wait_for(endpoint_t who) /*===========================================================================* * pipe_suspend * *===========================================================================*/ -PUBLIC void pipe_suspend(rw_flag, filp, buf, size) -int rw_flag; +PUBLIC void pipe_suspend(filp, buf, size) struct filp *filp; char *buf; size_t size; { /* Take measures to suspend the processing of the present system call. * Store the parameters to be used upon resuming in the process table. - * (Actually they are not used when a process is waiting for an I/O device, - * but they are needed for pipes, and it is not worth making the distinction.) - * The SUSPEND pseudo error should be returned after calling suspend(). */ #if DO_SANITYCHECKS if(fp_is_blocked(fp)) panic("pipe_suspend: called for suspended process"); #endif - susp_count++; /* #procs susp'ed on pipe*/ - fp->fp_blocked_on = FP_BLOCKED_ON_PIPE; - assert(!GRANT_VALID(fp->fp_grant)); - fp->fp_blocked.bfilp = filp; - fp->fp_block_callnr = (rw_flag == READING) ? READ : WRITE; - fp->fp_buffer = buf; - fp->fp_nbytes = size; + scratch(fp).file.filp = filp; + scratch(fp).io.io_buffer = buf; + scratch(fp).io.io_nbytes = size; + suspend(FP_BLOCKED_ON_PIPE); } @@ -430,16 +408,18 @@ int count; /* max number of processes to release */ rp->fp_blocked_on == FP_BLOCKED_ON_DOPEN || rp->fp_blocked_on == FP_BLOCKED_ON_LOCK || rp->fp_blocked_on == FP_BLOCKED_ON_OTHER) { - if (rp->fp_filp[rp->fp_blocked.fd_nr] == NULL) + if (!FD_ISSET(scratch(rp).file.fd_nr, + &rp->fp_filp_inuse)) continue; - if (rp->fp_filp[rp->fp_blocked.fd_nr]->filp_vno != vp) + if (rp->fp_filp[scratch(rp).file.fd_nr]->filp_vno != vp) continue; - } else { - if (rp->fp_blocked.bfilp == NULL) + } else if (rp->fp_blocked_on == FP_BLOCKED_ON_PIPE) { + if (scratch(rp).file.filp == NULL) continue; - if (rp->fp_blocked.bfilp->filp_vno != vp) + if (scratch(rp).file.filp->filp_vno != vp) continue; - } + } else + continue; /* We found the vnode. Revive process. */ revive(rp->fp_endpoint, 0); @@ -478,14 +458,14 @@ int returned; /* if hanging on task, how many bytes read */ * the proc must be restarted so it can try again. */ blocked_on = rfp->fp_blocked_on; - fd_nr = rfp->fp_blocked.fd_nr; + fd_nr = scratch(rfp).file.fd_nr; if (blocked_on == FP_BLOCKED_ON_PIPE || blocked_on == FP_BLOCKED_ON_LOCK) { /* Revive a process suspended on a pipe or lock. */ rfp->fp_flags |= FP_REVIVED; reviving++; /* process was waiting on pipe or lock */ } else if (blocked_on == FP_BLOCKED_ON_DOPEN) { rfp->fp_blocked_on = FP_BLOCKED_ON_NONE; - rfp->fp_blocked.fd_nr = 0; + scratch(rfp).file.fd_nr = 0; if (returned < 0) { fil_ptr = rfp->fp_filp[fd_nr]; lock_filp(fil_ptr, VNODE_OPCL); @@ -505,7 +485,7 @@ int returned; /* if hanging on task, how many bytes read */ } } else { rfp->fp_blocked_on = FP_BLOCKED_ON_NONE; - rfp->fp_blocked.fd_nr = 0; + scratch(rfp).file.fd_nr = 0; if (blocked_on == FP_BLOCKED_ON_POPEN) { /* process blocked in open or create */ reply(proc_nr_e, fd_nr); @@ -515,7 +495,7 @@ int returned; /* if hanging on task, how many bytes read */ /* Revive a process suspended on TTY or other device. * Pretend it wants only what there is. */ - rfp->fp_nbytes = returned; + scratch(rfp).io.io_nbytes = returned; /* If a grant has been issued by FS for this I/O, revoke * it again now that I/O is done. */ @@ -592,7 +572,7 @@ int proc_nr_e; break; } - fild = rfp->fp_blocked.fd_nr; + fild = scratch(rfp).file.fd_nr; if (fild < 0 || fild >= OPEN_MAX) panic("file descriptor out-of-range"); f = rfp->fp_filp[fild]; diff --git a/servers/avfs/proto.h b/servers/avfs/proto.h index 12db0386e..327935bd8 100644 --- a/servers/avfs/proto.h +++ b/servers/avfs/proto.h @@ -196,8 +196,7 @@ _PROTOTYPE( int pipe_check, (struct vnode *vp, int rw_flag, _PROTOTYPE( void release, (struct vnode *vp, int call_nr, int count) ); _PROTOTYPE( void revive, (int proc_nr, int bytes) ); _PROTOTYPE( void suspend, (int task) ); -_PROTOTYPE( void pipe_suspend, (int rw_flag, struct filp *rfilp, - char *buf, size_t size) ); +_PROTOTYPE( void pipe_suspend, (struct filp *rfilp, char *buf, size_t size)); _PROTOTYPE( void unsuspend_by_endpt, (endpoint_t) ); _PROTOTYPE( void wait_for, (endpoint_t) ); #if DO_SANITYCHECKS diff --git a/servers/avfs/read.c b/servers/avfs/read.c index 24f3eb677..13df38ae0 100644 --- a/servers/avfs/read.c +++ b/servers/avfs/read.c @@ -17,6 +17,7 @@ #include #include "file.h" #include "fproc.h" +#include "scratchpad.h" #include "param.h" #include #include @@ -78,18 +79,24 @@ int rw_flag; /* READING or WRITING */ tll_access_t locktype; int r; + scratch(fp).file.fd_nr = m_in.fd; + scratch(fp).io.io_buffer = m_in.buffer; + scratch(fp).io.io_nbytes = (size_t) m_in.nbytes; + locktype = (rw_flag == READING) ? VNODE_READ : VNODE_WRITE; - if ((f = get_filp(m_in.fd, locktype)) == NULL) return(err_code); + if ((f = get_filp(scratch(fp).file.fd_nr, locktype)) == NULL) + return(err_code); if (((f->filp_mode) & (rw_flag == READING ? R_BIT : W_BIT)) == 0) { unlock_filp(f); return(f->filp_mode == FILP_CLOSED ? EIO : EBADF); } - if (m_in.nbytes == 0) { + if (scratch(fp).io.io_nbytes == 0) { unlock_filp(f); return(0); /* so char special files need not check for 0*/ } - r = read_write(rw_flag, f, m_in.buffer, m_in.nbytes, who_e); + r = read_write(rw_flag, f, scratch(fp).io.io_buffer, scratch(fp).io.io_nbytes, + who_e); unlock_filp(f); return(r); @@ -260,7 +267,7 @@ size_t req_size; r = pipe_check(vp, rw_flag, oflags, req_size, position, 0); if (r <= 0) { - if (r == SUSPEND) pipe_suspend(rw_flag, f, buf, req_size); + if (r == SUSPEND) pipe_suspend(f, buf, req_size); return(r); } @@ -328,7 +335,7 @@ size_t req_size; * non-atomic */ fp->fp_cum_io_partial = cum_io; - pipe_suspend(rw_flag, f, buf, req_size); + pipe_suspend(f, buf, req_size); return(SUSPEND); } } diff --git a/servers/avfs/scratchpad.h b/servers/avfs/scratchpad.h new file mode 100644 index 000000000..f33a01c3a --- /dev/null +++ b/servers/avfs/scratchpad.h @@ -0,0 +1,18 @@ +#ifndef __VFS_SCRATCHPAD_H__ +#define __VFS_SCRATCHPAD_H__ + +/* This is the per-process information. A slot is reserved for each potential + * process. Thus NR_PROCS must be the same as in the kernel. + */ +EXTERN struct scratchpad { + union sp_data { + int fd_nr; + struct filp *filp; + } file; + struct io_cmd { + char *io_buffer; + size_t io_nbytes; + } io; +} scratchpad[NR_PROCS]; + +#endif diff --git a/servers/avfs/table.c b/servers/avfs/table.c index 792e3ac74..c4a4173bc 100644 --- a/servers/avfs/table.c +++ b/servers/avfs/table.c @@ -10,6 +10,7 @@ #include "file.h" #include "fproc.h" #include "lock.h" +#include "scratchpad.h" #include "vnode.h" #include "vmnt.h"