/*===========================================================================* * common_open * *===========================================================================*/ int common_open(char path[PATH_MAX], int oflags, mode_t omode) { /* Common code from do_creat and do_open. */ int b, r, exist = TRUE, major_dev; dev_t dev; mode_t bits; struct filp *filp, *filp2; struct vnode *vp; struct vmnt *vmp; struct dmap *dp; struct lookup resolve; /* Remap the bottom two bits of oflags. */ bits = (mode_t) mode_map[oflags & O_ACCMODE]; if (!bits) return(EINVAL); /* See if file descriptor and filp slots are available. */ if ((r = get_fd(0, bits, &(scratch(fp).file.fd_nr), &filp)) != OK) return(r); lookup_init(&resolve, path, PATH_NOFLAGS, &vmp, &vp); /* If O_CREATE is set, try to make the file. */ if (oflags & O_CREAT) { omode = I_REGULAR | (omode & ALLPERMS & fp->fp_umask); vp = new_node(&resolve, oflags, omode); r = err_code; if (r == OK) exist = FALSE; /* We just created the file */ else if (r != EEXIST) { /* other error */ if (vp) unlock_vnode(vp); unlock_filp(filp); return(r); } else exist = !(oflags & O_EXCL);/* file exists, if the O_EXCL flag is set this is an error */ } else { /* Scan path name */ resolve.l_vmnt_lock = VMNT_READ; resolve.l_vnode_lock = VNODE_OPCL; if ((vp = eat_path(&resolve, fp)) == NULL) { unlock_filp(filp); return(err_code); } if (vmp != NULL) unlock_vmnt(vmp); } /* Claim the file descriptor and filp slot and fill them in. */ 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; /* Only do the normal open code if we didn't just create the file. */ if (exist) { /* Check protections. */ if ((r = forbidden(fp, vp, bits)) == OK) { /* Opening reg. files, directories, and special files differ */ switch (vp->v_mode & S_IFMT) { case S_IFREG: /* Truncate regular file if O_TRUNC. */ if (oflags & O_TRUNC) { if ((r = forbidden(fp, vp, W_BIT)) != OK) break; truncate_vnode(vp, 0); } break; case S_IFDIR: /* Directories may be read but not written. */ r = (bits & W_BIT ? EISDIR : OK); break; case S_IFCHR: /* Invoke the driver for special processing. */ dev = (dev_t) vp->v_sdev; /* TTY needs to know about the O_NOCTTY flag. */ r = dev_open(dev, who_e, bits | (oflags & O_NOCTTY)); if (r == SUSPEND) suspend(FP_BLOCKED_ON_DOPEN); else vp = filp->filp_vno; /* Might be updated by * dev_open/clone_opcl */ break; case S_IFBLK: lock_bsf(); /* Invoke the driver for special processing. */ dev = (dev_t) vp->v_sdev; r = bdev_open(dev, bits); if (r != OK) { unlock_bsf(); break; } major_dev = major(vp->v_sdev); dp = &dmap[major_dev]; if (dp->dmap_driver == NONE) { printf("VFS: block driver disappeared!\n"); unlock_bsf(); r = ENXIO; break; } /* Check whether the device is mounted or not. If so, * then that FS is responsible for this device. * Otherwise we default to ROOT_FS. */ vp->v_bfs_e = ROOT_FS_E; /* By default */ for (vmp = &vmnt[0]; vmp < &vmnt[NR_MNTS]; ++vmp) if (vmp->m_dev == vp->v_sdev && !(vmp->m_flags & VMNT_FORCEROOTBSF)) { vp->v_bfs_e = vmp->m_fs_e; } /* Send the driver label to the file system that will * handle the block I/O requests (even when its label * and endpoint are known already), but only when it is * the root file system. Other file systems will * already have it anyway. */ if (vp->v_bfs_e != ROOT_FS_E) { unlock_bsf(); break; } if (req_newdriver(vp->v_bfs_e, vp->v_sdev, dp->dmap_label) != OK) { printf("VFS: error sending driver label\n"); bdev_close(dev); r = ENXIO; } unlock_bsf(); break; case S_IFIFO: /* Create a mapped inode on PFS which handles reads and writes to this named pipe. */ tll_upgrade(&vp->v_lock); r = map_vnode(vp, PFS_PROC_NR); if (r == OK) { if (vp->v_ref_count == 1) { vp->v_pipe_rd_pos = 0; vp->v_pipe_wr_pos = 0; if (vp->v_size != 0) r = truncate_vnode(vp, 0); } oflags |= O_APPEND; /* force append mode */ filp->filp_flags = oflags; } if (r == OK) { r = pipe_open(vp, bits, oflags); } if (r != ENXIO) { /* See if someone else is doing a rd or wt on * the FIFO. If so, use its filp entry so the * file position will be automatically shared. */ b = (bits & R_BIT ? R_BIT : W_BIT); 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[scratch(fp).file.fd_nr] = filp2; filp2->filp_count++; filp2->filp_vno = vp; filp2->filp_flags = oflags; /* v_count was incremented after the vnode * has been found. i_count was incremented * incorrectly in FS, not knowing that we * were going to use an existing filp * entry. Correct this error. */ unlock_vnode(vp); put_vnode(vp); } else { /* Nobody else found. Restore filp. */ filp->filp_count = 1; } } break; } } } unlock_filp(filp); /* If error, release inode. */ if (r != OK) { if (r != SUSPEND) { 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 = scratch(fp).file.fd_nr; } return(r); }
/*===========================================================================* * mount_fs * *===========================================================================*/ int mount_fs( dev_t dev, char mountpoint[PATH_MAX], endpoint_t fs_e, int rdonly, char mount_label[LABEL_MAX] ) { int i, r = OK, found, isroot, mount_root, con_reqs, slot; struct fproc *tfp, *rfp; struct dmap *dp; struct vnode *root_node, *vp = NULL; struct vmnt *new_vmp, *parent_vmp; char *label; struct node_details res; struct lookup resolve; /* Look up block device driver label when dev is not a pseudo-device */ label = ""; if (!is_nonedev(dev)) { /* Get driver process' endpoint */ dp = &dmap[major(dev)]; if (dp->dmap_driver == NONE) { printf("VFS: no driver for dev %d\n", dev); return(EINVAL); } label = dp->dmap_label; assert(strlen(label) > 0); } /* Scan vmnt table to see if dev already mounted. If not, find a free slot.*/ found = FALSE; for (i = 0; i < NR_MNTS; ++i) { if (vmnt[i].m_dev == dev) found = TRUE; } if (found) { return(EBUSY); } else if ((new_vmp = get_free_vmnt()) == NULL) { return(ENOMEM); } if ((r = lock_vmnt(new_vmp, VMNT_EXCL)) != OK) return(r); isroot = (strcmp(mountpoint, "/") == 0); mount_root = (isroot && have_root < 2); /* Root can be mounted twice: * 1: ramdisk * 2: boot disk (e.g., harddisk) */ if (!mount_root) { /* Get vnode of mountpoint */ lookup_init(&resolve, mountpoint, PATH_NOFLAGS, &parent_vmp, &vp); resolve.l_vmnt_lock = VMNT_EXCL; resolve.l_vnode_lock = VNODE_WRITE; if ((vp = eat_path(&resolve, fp)) == NULL) r = err_code; else if (vp->v_ref_count == 1) { /*Tell FS on which vnode it is mounted (glue into mount tree)*/ r = req_mountpoint(vp->v_fs_e, vp->v_inode_nr); } else r = EBUSY; if (vp != NULL) { /* Quickly unlock to allow back calls (from e.g. FUSE) to * relock */ unlock_vmnt(parent_vmp); } if (r != OK) { if (vp != NULL) { unlock_vnode(vp); put_vnode(vp); } unlock_vmnt(new_vmp); return(r); } } /* We'll need a vnode for the root inode */ if ((root_node = get_free_vnode()) == NULL) { if (vp != NULL) { unlock_vnode(vp); put_vnode(vp); } unlock_vmnt(new_vmp); return(err_code); } lock_vnode(root_node, VNODE_OPCL); /* Record process as a system process */ if (isokendpt(fs_e, &slot) != OK) { if (vp != NULL) { unlock_vnode(vp); put_vnode(vp); } unlock_vnode(root_node); unlock_vmnt(new_vmp); return(EINVAL); } rfp = &fproc[slot]; rfp->fp_flags |= FP_SRV_PROC; /* File Servers are also services */ /* Store some essential vmnt data first */ new_vmp->m_fs_e = fs_e; new_vmp->m_dev = dev; if (rdonly) new_vmp->m_flags |= VMNT_READONLY; else new_vmp->m_flags &= ~VMNT_READONLY; /* Tell FS which device to mount */ new_vmp->m_flags |= VMNT_MOUNTING; r = req_readsuper(fs_e, label, dev, rdonly, isroot, &res, &con_reqs); new_vmp->m_flags &= ~VMNT_MOUNTING; if (r != OK) { mark_vmnt_free(new_vmp); unlock_vnode(root_node); if (vp != NULL) { unlock_vnode(vp); put_vnode(vp); } unlock_vmnt(new_vmp); return(r); } lock_bsf(); /* Fill in root node's fields */ root_node->v_fs_e = res.fs_e; root_node->v_inode_nr = res.inode_nr; root_node->v_mode = res.fmode; root_node->v_uid = res.uid; root_node->v_gid = res.gid; root_node->v_size = res.fsize; root_node->v_sdev = NO_DEV; root_node->v_fs_count = 1; root_node->v_ref_count = 1; /* Root node is indeed on the partition */ root_node->v_vmnt = new_vmp; root_node->v_dev = new_vmp->m_dev; if (con_reqs == 0) new_vmp->m_comm.c_max_reqs = 1; /* Default if FS doesn't tell us */ else new_vmp->m_comm.c_max_reqs = con_reqs; new_vmp->m_comm.c_cur_reqs = 0; if (mount_root) { /* Superblock and root node already read. * Nothing else can go wrong. Perform the mount. */ new_vmp->m_root_node = root_node; new_vmp->m_mounted_on = NULL; strlcpy(new_vmp->m_label, mount_label, LABEL_MAX); if (is_nonedev(dev)) alloc_nonedev(dev); update_bspec(dev, fs_e, 0 /* Don't send new driver endpoint */); ROOT_DEV = dev; ROOT_FS_E = fs_e; /* Replace all root and working directories */ for (i = 0, tfp = fproc; i < NR_PROCS; i++, tfp++) { if (tfp->fp_pid == PID_FREE) continue; #define MAKEROOT(what) { \ if (what) put_vnode(what); \ dup_vnode(root_node); \ what = root_node; \ } MAKEROOT(tfp->fp_rd); MAKEROOT(tfp->fp_wd); } unlock_vnode(root_node); unlock_vmnt(new_vmp); have_root++; /* We have a (new) root */ unlock_bsf(); return(OK); } /* File types may not conflict. */ if (!S_ISDIR(vp->v_mode) && S_ISDIR(root_node->v_mode)) r = EISDIR; /* If error, return the super block and both inodes; release the vmnt. */ if (r != OK) { unlock_vnode(vp); unlock_vnode(root_node); mark_vmnt_free(new_vmp); unlock_vmnt(new_vmp); put_vnode(vp); put_vnode(root_node); unlock_bsf(); return(r); } /* Nothing else can go wrong. Perform the mount. */ new_vmp->m_mounted_on = vp; new_vmp->m_root_node = root_node; strlcpy(new_vmp->m_label, mount_label, LABEL_MAX); /* Allocate the pseudo device that was found, if not using a real device. */ if (is_nonedev(dev)) alloc_nonedev(dev); /* The new FS will handle block I/O requests for its device now. */ if (!(new_vmp->m_flags & VMNT_FORCEROOTBSF)) update_bspec(dev, fs_e, 0 /* Don't send new driver endpoint */); unlock_vnode(vp); unlock_vnode(root_node); unlock_vmnt(new_vmp); unlock_bsf(); return(OK); }
/*===========================================================================* * unmount * *===========================================================================*/ int unmount( dev_t dev, /* block-special device */ char label[LABEL_MAX] /* buffer to retrieve label, or NULL */ ) { struct vnode *vp; struct vmnt *vmp_i = NULL, *vmp = NULL; int count, locks, r; /* Find vmnt that is to be unmounted */ for (vmp_i = &vmnt[0]; vmp_i < &vmnt[NR_MNTS]; ++vmp_i) { if (vmp_i->m_dev == dev) { if(vmp) panic("device mounted more than once: %d", dev); vmp = vmp_i; } } /* Did we find the vmnt (i.e., was dev a mounted device)? */ if(!vmp) return(EINVAL); if ((r = lock_vmnt(vmp, VMNT_EXCL)) != OK) return(r); /* See if the mounted device is busy. Only 1 vnode using it should be * open -- the root vnode -- and that inode only 1 time. */ locks = count = 0; for (vp = &vnode[0]; vp < &vnode[NR_VNODES]; vp++) if (vp->v_ref_count > 0 && vp->v_dev == dev) { count += vp->v_ref_count; if (is_vnode_locked(vp)) locks++; } if (count > 1 || locks > 1 || tll_haspendinglock(&vmp->m_lock)) { unlock_vmnt(vmp); return(EBUSY); /* can't umount a busy file system */ } /* Tell FS to drop all inode references for root inode except 1. */ vnode_clean_refs(vmp->m_root_node); if (vmp->m_mounted_on) { put_vnode(vmp->m_mounted_on); vmp->m_mounted_on = NULL; } vmp->m_comm.c_max_reqs = 1; /* Force max concurrent reqs to just one, so * we won't send any messages after the * unmount request */ /* Tell FS to unmount */ if ((r = req_unmount(vmp->m_fs_e)) != OK) /* Not recoverable. */ printf("VFS: ignoring failed umount attempt FS endpoint: %d (%d)\n", vmp->m_fs_e, r); if (is_nonedev(vmp->m_dev)) free_nonedev(vmp->m_dev); if (label != NULL) strlcpy(label, vmp->m_label, LABEL_MAX); if (vmp->m_root_node) { /* PFS lacks a root node */ vmp->m_root_node->v_ref_count = 0; vmp->m_root_node->v_fs_count = 0; vmp->m_root_node->v_sdev = NO_DEV; vmp->m_root_node = NULL; } mark_vmnt_free(vmp); unlock_vmnt(vmp); /* The root FS will handle block I/O requests for this device now. */ lock_bsf(); update_bspec(dev, ROOT_FS_E, 1 /* send new driver endpoint */); unlock_bsf(); return(OK); }
/*===========================================================================* * read_write * *===========================================================================*/ int read_write(struct fproc *rfp, int rw_flag, struct filp *f, vir_bytes buf, size_t size, endpoint_t for_e) { register struct vnode *vp; off_t position, res_pos; unsigned int cum_io, cum_io_incr, res_cum_io; int op, r; dev_t dev; position = f->filp_pos; vp = f->filp_vno; r = OK; cum_io = 0; assert(rw_flag == READING || rw_flag == WRITING || rw_flag == PEEKING); if (size > SSIZE_MAX) return(EINVAL); op = (rw_flag == READING ? CDEV_READ : CDEV_WRITE); if (S_ISFIFO(vp->v_mode)) { /* Pipes */ if (rfp->fp_cum_io_partial != 0) { panic("VFS: read_write: fp_cum_io_partial not clear"); } if(rw_flag == PEEKING) { printf("read_write: peek on pipe makes no sense\n"); return EINVAL; } r = rw_pipe(rw_flag, for_e, f, buf, size); } else if (S_ISCHR(vp->v_mode)) { /* Character special files. */ if(rw_flag == PEEKING) { printf("read_write: peek on char device makes no sense\n"); return EINVAL; } if (vp->v_sdev == NO_DEV) panic("VFS: read_write tries to access char dev NO_DEV"); dev = vp->v_sdev; r = cdev_io(op, dev, for_e, buf, position, size, f->filp_flags); if (r >= 0) { /* This should no longer happen: all calls are asynchronous. */ printf("VFS: I/O to device %llx succeeded immediately!?\n", dev); cum_io = r; position += r; r = OK; } else if (r == SUSPEND) { /* FIXME: multiple read/write operations on a single filp * should be serialized. They currently aren't; in order to * achieve a similar effect, we optimistically advance the file * position here. This works under the following assumptions: * - character drivers that use the seek position at all, * expose a view of a statically-sized range of bytes, i.e., * they are basically byte-granular block devices; * - if short I/O or an error is returned, all subsequent calls * will return (respectively) EOF and an error; * - the application never checks its own file seek position, * or does not care that it may end up having seeked beyond * the number of bytes it has actually read; * - communication to the character driver is FIFO (this one * is actually true! whew). * Many improvements are possible here, but in the end, * anything short of queuing concurrent operations will be * suboptimal - so we settle for this hack for now. */ position += size; } } else if (S_ISBLK(vp->v_mode)) { /* Block special files. */ if (vp->v_sdev == NO_DEV) panic("VFS: read_write tries to access block dev NO_DEV"); lock_bsf(); if(rw_flag == PEEKING) { r = req_bpeek(vp->v_bfs_e, vp->v_sdev, position, size); } else { r = req_breadwrite(vp->v_bfs_e, for_e, vp->v_sdev, position, size, buf, rw_flag, &res_pos, &res_cum_io); if (r == OK) { position = res_pos; cum_io += res_cum_io; } } unlock_bsf(); } else { /* Regular files */ if (rw_flag == WRITING) { /* Check for O_APPEND flag. */ if (f->filp_flags & O_APPEND) position = vp->v_size; } /* Issue request */ if(rw_flag == PEEKING) { r = req_peek(vp->v_fs_e, vp->v_inode_nr, position, size); } else { off_t new_pos; r = req_readwrite(vp->v_fs_e, vp->v_inode_nr, position, rw_flag, for_e, buf, size, &new_pos, &cum_io_incr); if (r >= 0) { position = new_pos; cum_io += cum_io_incr; } } } /* On write, update file size and access time. */ if (rw_flag == WRITING) { if (S_ISREG(vp->v_mode) || S_ISDIR(vp->v_mode)) { if (position > vp->v_size) { vp->v_size = position; } } } f->filp_pos = position; if (r == EPIPE && rw_flag == WRITING) { /* Process is writing, but there is no reader. Tell the kernel to * generate s SIGPIPE signal. */ if (!(f->filp_flags & O_NOSIGPIPE)) { sys_kill(rfp->fp_endpoint, SIGPIPE); } } if (r == OK) { return(cum_io); } return(r); }