/* $Header: /ker/coh.386/RCS/sys5.c,v 2.6 93/10/29 00:55:50 nigel Exp Locker: nigel $ */ /* * System calls introduced when going from COH 286 to COH 386. * $Log: sys5.c,v $ * Revision 2.6 93/10/29 00:55:50 nigel * R98 (aka 4.2 Beta) prior to removing System Global memory * * Revision 2.5 93/08/19 03:26:55 nigel * Nigel's r83 (Stylistic cleanup) */ #include <kernel/proc_lib.h> #include <sys/errno.h> #include <sys/stat.h> #include <sys/file.h> #include <ulimit.h> #include <dirent.h> #include <fcntl.h> #include <stddef.h> #include <limits.h> #define _KERNEL 1 #include <kernel/alloc.h> #include <kernel/trace.h> #include <kernel/reg.h> #include <sys/uproc.h> #include <sys/proc.h> #include <sys/mmu.h> #include <sys/buf.h> #include <canon.h> #include <sys/con.h> #include <sys/fd.h> #include <sys/filsys.h> #include <sys/ino.h> #include <sys/inode.h> #include <sys/io.h> #include <sys/mount.h> #include <sys/utsname.h> #include <sys/mount.h> #include <ustat.h> #include <sys/statfs.h> #include <sys/sysi86.h> time_t utime () { return timer.t_time; } /* * Return an unique number. */ int usysi86 (f, arg1) int f; int arg1; { MOUNT *mp; struct filsys *fsp; extern void (*ndpEmFn)(); int fpval; extern short ndpType; switch (f) { case SYI86UNEEK: if ((mp = getment (rootdev, 1)) == NULL) return -1; fsp = & mp->m_super; fsp->s_fmod = 1; return ++ fsp->s_unique; case SI86FPHW: /* * 2's bit: floating point ndp is present (80287/80387/80486dx) * 1's bit (when 2's bit = 1): 80387/486dx is present */ if (! useracc (arg1, sizeof (int), 1)) { SET_U_ERROR (EFAULT, "sysi386:SI86FPHW"); return -1; } if (ndpType <= 1) { /* no ndp */ fpval = (ndpEmFn) ? FP_SW : FP_NO; } else fpval = (ndpType > 2) ? FP_387 : FP_287; putuwd (arg1, fpval); return 0; } set_user_error (EINVAL); return -1; } /* * sys_unknown - write name unknown to utsname struct */ #if __USE_PROTO__ __LOCAL__ int sys_unknown (struct utsname * name) #else __LOCAL__ int sys_unknown (name) struct utsname *name; #endif { static char unknown[] = "UNKNOWN"; if (! kucopy (unknown, name->sysname, sizeof (unknown))) return -1; if (! kucopy (unknown, name->nodename, sizeof (unknown))) return -1; return 0; } /* * uname - get name of the current operating system. */ extern char version[]; /* Defined in main.c */ extern char release[]; /* Defined in main.c */ int uname(name) struct utsname *name; { register char *rcp; /**/ register int i; /* Counter, loop index */ BUF *bp; /* Read buffer */ char namebuf[SYS_NMLN]; /* System name */ int fl; /* File length*/ /* Check if *name is an available user area */ if (! useracc ((char *) name, sizeof (struct utsname), 1)) { set_user_error (EFAULT); return -1; } /* Find the size of the version number */ for (rcp = version, i = 0; *rcp != 0 && i < SYS_NMLN; i++, rcp++) /* DO NOTHING */ ; /* Write version number to user area */ if (! kucopy (version, name->version, i)) return -1; /* Find the size of the release number */ for (rcp = release, i = 0 ; * rcp != 0 && i < SYS_NMLN ; i ++, rcp ++) /* DO NOTHING */ ; /* Write release number to user area */ if (! kucopy (release, name->release, i)) return -1; /* Write "machine" to user area */ if (! kucopy ("i386", name->machine, 4)) return -1; /* * We supposed that system name and nodename are in /etc/uucpname * NIGEL: Set the global io segment to IOSYS so that ftoi () will use * the right method of getting at the argument passed to it. */ { struct direct dir; if (ftoi ("/etc/uucpname", 'r', IOSYS, NULL, & dir, SELF->p_credp) != 0) return sys_unknown (name); } if ((fl = u.u_cdiri->i_size) == 0) { idetach (u.u_cdiri); return sys_unknown (name); } if (iaccess (u.u_cdiri, IPR, SELF->p_credp) == 0 || (bp = vread (u.u_cdiri, (daddr_t) 0)) == NULL) { idetach (u.u_cdiri); return -1; } /* namebuf should be not more than SYS_NMLN - 1 characters long */ fl = fl > SYS_NMLN ? SYS_NMLN : fl; memcpy (namebuf, bp->b_vaddr, fl); brelease (bp); idetach (u.u_cdiri); if (fl == 1 && namebuf [0] == '\n') return sys_unknown (name); for (rcp = namebuf, i = 0 ; i < fl ; rcp ++) { i ++; if (* rcp == '\n') { * rcp = 0; break; } } namebuf[i - 1] = 0; /* Write system name to user area */ if (! kucopy (namebuf, name->sysname, i)) return -1; /* Write system name to user area */ if (! kucopy (namebuf, name->nodename, i)) return -1; return 0; } /* * u_ustat - get file system statistics. (Name ustat in use for stat s.c.) */ int u_ustat(dev, buf) dev_t dev; struct ustat *buf; { MOUNT *mp; /* Check if buf is an available user area. */ /* B_READ | B_WRITE is not implemented yet. */ if (! useracc ((char *) buf, sizeof (struct ustat), 1)) { set_user_error (EFAULT); return -1; } /* Take mount filesystem, check if dev is mounted device */ for (mp = mountp ; mp != NULL ; mp = mp->m_next) if (mp->m_dev == dev) break; if (mp == NULL) { set_user_error (EINVAL); return -1; } /* Pickup information from superblock */ /* Number of free blocks */ if (! kucopy (& mp->m_super.s_tfree, & buf->f_tfree, sizeof (mp->m_super.s_tfree))) return -1; /* Number of free inodes */ if (! kucopy (& mp->m_super.s_tinode, & buf->f_tinode, sizeof (mp->m_super.s_tinode))) return -1; /* File system name */ if (! kucopy (mp->m_super.s_fname, buf->f_fname, sizeof (mp->m_super.s_fname))) return -1; /* File system pack name */ if (! kucopy (mp->m_super.s_fpack, buf->f_fpack, sizeof (mp->m_super.s_fpack))) return -1; return 0; } /* * uname and ustat system calls. * * int uname(struct utsname *name) * Before lcall instruction 4(%esp) contains name, 8(%esp) contains * an unspecified value, and 12(%esp) contains the value 0. * * int ustat(int dev, struct ustat *buf) * Before lcall instruction 4(%esp) contains buf (REVERSE order of argument) * 8(%esp) contains dev, and 12(%esp) contains the value 2. */ int uutssys (arg1, arg2, func) caddr_t arg1; caddr_t arg2; int func; { switch (func) { case 0: return uname ((struct utsname *) arg1); case 2: return u_ustat (arg2, arg1); default: set_user_error (EINVAL); return -1; } } /* Don't tell user process about last remaining 64k of RAM */ #define BRK_CUSHION 16 unsigned long uulimit (cmd, newlimit) int cmd; unsigned long newlimit; { int freeClicks; switch (cmd) { case UL_GETFSIZE: /* Get max # of 512-byte blocks per file. */ return u.u_bpfmax; break; case UL_SETFSIZE: /* Set max # of 512-byte blocks per file. */ /* (only superuser may increase this) */ if (newlimit <= u.u_bpfmax || super ()) { u.u_bpfmax = newlimit; return 0; } /* else super() will have set_user_error () to EPERM */ break; case UL_GMEMLIM: /* Get max break value. */ /* return (current brk value) + (amount of free space) */ /* Don't report all free clicks - leave a cushion. */ freeClicks = allocno () - BRK_CUSHION; if (freeClicks < 0) freeClicks = 0; return (unsigned long) (SELF->p_segl [SIPDATA].sr_base + SELF->p_segl [SIPDATA].sr_segp->s_size + NBPC * freeClicks); case UL_GDESLIM: /* Return configured number of open files per process. */ return NOFILE; break; default: set_user_error (EINVAL); } return -1UL; } /* * Change the size of a file. */ int uchsize (fd, size) int fd; long size; { __fd_t * fdp; INODE * ip; if (size < 0) { set_user_error (EINVAL); return -1; } if ((fdp = fd_get (fd)) == NULL || (fdp->f_flag & IPW) == 0) { set_user_error (EBADF); return -1; } ip = fdp->f_ip; switch (ip->i_mode & IFMT) { case IFREG: if (size > (long) u.u_bpfmax * BSIZE) { set_user_error (EFBIG); return -1; } if (size == ip->i_size) break; ilock (ip, "uchsize () IFREG"); if (size < ip->i_size) blclear (ip, __DIVIDE_ROUNDUP (ip->i_size, BSIZE)); ip->i_size = size; imodcreat (ip); iunlock (ip); break; case IFPIPE: if (size > PIPE_MAX) { set_user_error (EFBIG); return -1; } ilock (ip, "uchsize () IFPIPE"); if (! ip->i_par && ! ip->i_psr) { set_user_error (EPIPE); sendsig (SIGPIPE, SELF); iunlock (ip); return -1; } ip->i_pwx += (size - ip->i_pnc); if (size > ip->i_pnc) { if (ip->i_pwx >= PIPE_MAX) ip->i_pwx -= PIPE_MAX; } else if (size < ip->i_pnc) { if (ip->i_pwx < 0) ip->i_pwx += PIPE_MAX; } ip->i_pnc = size; imodcreat (ip); if (size > 0) pwake (ip, 2); /* 2==IFWFW, see pipe.c */ if (size < PIPE_MAX) pwake (ip, 1); /* 1==IFWFR, see pipe.c */ iunlock (ip); break; default: set_user_error (EBADF); return -1; } return 0; } /* * This is a copy of iucheck. The only one difference is that that allows * to remove a directory to a regular user. */ int diucheck (dev, ino) o_dev_t dev; o_ino_t ino; { if (inode_find (dev, ino, NULL) == NULL) { struct inode *ip; struct inode inode; ip = & inode; ip->i_dev = dev; ip->i_ino = ino; if (icopydm (ip) == 0) return 0; } return 1; } /* * Unlink the given directory. */ void dunlink (np) char *np; { INODE * ip; dev_t dev; IO io; struct direct dir; if (file_to_inode (np, 'u', 1, IOSYS, & io, & dir, SELF->p_credp) != 0) return; if (iaccess (u.u_pdiri, IPW, SELF->p_credp) == 0) { idetach (u.u_pdiri); return; } dev = u.u_pdiri->i_dev; if (diucheck (dev, u.u_cdirn) == 0) { idetach (u.u_pdiri); return; } idirent (0, & io, & dir); idetach (u.u_pdiri); if ((ip = iattach (dev, u.u_cdirn)) == NULL) return; if (ip->i_nlink > 0) -- ip->i_nlink; icreated (ip); /* unlink - ctime */ idetach (ip); return; } /* * Remove a directory. * path is a pointer to user area. */ void removedir(path, iPathLen) char *path; /* Directory name */ int iPathLen; /* Path length */ { char *buf; char *cpbuf, /* internal file_name buffer */ *cppath; /* user file_name buffer */ /* * Allocate kernel buffer. We need extra space for '/', '.', '..' * and 0 */ if ((buf = kalloc (iPathLen + 4)) == NULL) { SET_U_ERROR (ENOSPC, "rmdir: out of kernel space"); return; } cpbuf = buf; cppath = path; /* Copy path to the kernel buffer. */ while ((* cpbuf = getubd (cppath)) != 0) { cppath ++; cpbuf ++; } * cpbuf++ = '/'; * cpbuf++ = '.'; * cpbuf = 0; dunlink (buf); if (get_user_error ()) { kfree (buf); return; } * cpbuf ++ = '.'; * cpbuf = 0; dunlink (buf); if (get_user_error ()) { /* We have to link '.' back here. */ kfree (buf); return; } buf [iPathLen] = 0; dunlink (buf); if (get_user_error ()) { /* We have to link '.' and '..' back here. */ kfree (buf); return; } kfree (buf); return; } /* * Check if directory is empty. */ int isdirempty (ip) struct inode *ip; { char *cp; int count; BUF *bp; for (count = 0 ; count < ip->i_size ; count += BSIZE) { if ((bp = vread (ip, count)) == NULL) break; for (cp = (char *) bp->b_vaddr ; cp < (char *) bp->b_vaddr + BSIZE ; cp += 16) { if (* cp == 0 && cp [1] == 0) continue; if (cp [2] != '.') goto bad; if (cp [3] == 0) continue; if (cp [3] != '.' || cp [4] != 0) goto bad; } brelease (bp); } return 1; bad: brelease (bp); return 0; } /* * Remove a directory. */ int urmdir (path) char * path; { int iPathLen; /* Size of the string */ extern int strUserAcc (); struct direct dir; /* Check if path points to a valid user buffer.*/ if ((iPathLen = strUserAcc (path, 0)) < 0) { set_user_error (EFAULT); return -1; } if (ftoi (path, 'r', IOUSR, NULL, & dir, SELF->p_credp) != 0) return -1; /* Check if path is a directory */ if ((u.u_cdiri->i_mode & IFMT) != IFDIR) { SET_U_ERROR (ENOTDIR, "rmdir: no such directory"); idetach (u.u_cdiri); return -1; } /* We have to check if directory is empty */ if (! isdirempty (u.u_cdiri)) { SET_U_ERROR (EEXIST, "rmdir: directory is not empty"); idetach (u.u_cdiri); return -1; } idetach (u.u_cdiri); removedir (path, iPathLen); return 0; } /* * SysV compatible mkdir() system call. * * Create a directory of the given "path" and "mode", if possible. * Creating the directory is straight forward. Trying to clean * up in case we run out of inodes or freee blocks in the process * is not trivial. * This system call was implemented in very press time. * Vlad 6-04-92 */ int umkdir (path, mode) char *path; int mode; { INODE *dmknod(); /* make directory node */ INODE *pip; /* parent inode pointer */ char *cp_path, *cpb_path, *cp_parent; char *bufpath, *bufparent, *mark; /* Check if path points to a valid user buffer.*/ if (strUserAcc (path, 0) < 0) { set_user_error (EFAULT); return -1; } /* * Create a local copies of "path" which we can use to build up * the required directory links: * path/. -- bufdot * path/..-- bufdotdot * Verify that the given path is not too long. */ cp_path = path; cpb_path = bufpath = kalloc ((PATH_MAX + 5) * sizeof (char)); if (cpb_path == NULL) { set_user_error (EAGAIN); return -1; } cp_parent = bufparent = kalloc ((PATH_MAX + 5) * sizeof (char)); if (cp_parent == NULL) { kfree (cpb_path); set_user_error (EAGAIN); return -1; } while ((* cpb_path = getubd (cp_path)) != 0) { * cp_parent ++ = * cpb_path; ++ cp_path; if (++ cpb_path >= & bufpath [PATH_MAX - 3]) { SET_U_ERROR (ENOENT, "sys5: mkdir: path too long"); kfree (bufpath); kfree (bufparent); return -1; } } if ((pip = dmknod (path, mode, IOUSR)) == NULL) { kfree (bufpath); kfree (bufparent); return -1; } /* * Temporarily change parent to the 'dot' directory */ mark = cp_parent; * cp_parent ++ = '/'; * cp_parent ++ = '.'; * cp_parent ++ = 0; do_link (bufpath, bufparent, IOSYS, 0); /* * Get rid of dot entry. */ * (cp_parent = mark) = 0; if (get_user_error ()) { set_user_error (do_unlink (bufpath, IOSYS)); kfree (bufparent); kfree (bufpath); return -1; } /* * Now really find the parent */ while (-- cp_parent >= bufparent) { if (* cp_parent == '/') { * ++ cp_parent = 0; break; } } if (cp_parent < bufparent) { * ++ cp_parent = '.'; * ++ cp_parent = 0; } /* * We can hack into the pathname now. */ mark = cpb_path; * cpb_path ++ = '/'; * cpb_path ++ = '.'; * cpb_path ++ = '.'; * cpb_path ++ = 0; do_link (bufparent, bufpath, IOSYS, 0); * (cpb_path = mark) = 0; if (get_user_error ()) { /* * Temporarily change parent to the 'dot' directory */ mark = cp_parent; *cp_parent++ = '/'; *cp_parent++ = '.'; *cp_parent++ = 0; set_user_error (do_unlink (bufparent, IOSYS)); set_user_error (do_unlink (bufpath, IOSYS)); } kfree (bufpath); kfree (bufparent); return 0; } /* * Create a directory. * * We cannot use original ulink because it makes the directories only * for superuser. */ INODE * dmknod (np, mode, space) char *np; /* Direcotory name */ int mode; int space; { INODE *ip; IO io; struct direct dir; /* If ftoi returns nonzero, get_user_error () has been set. */ if (ftoi (np, 'c', space, & io, & dir, SELF->p_credp) != 0) return NULL; if (u.u_cdiri != NULL) { SET_U_ERROR (EEXIST, "dmknod: path already exists"); idetach (u.u_cdiri); return NULL; } if ((ip = imake (mode | S_IFDIR, (o_dev_t) 0, & io, & dir, SELF->p_credp)) != NULL) idetach (ip); return u.u_pdiri; /* grab ptr to parent inode */ } /* * Read `n' bytes from the directory `fd' using the buffer `bp'. */ int dirio(fd, bp, n, offset) int fd; struct direct *bp; unsigned n; off_t *offset; { __fd_t * fdp; IO io; /* Check file descriptor */ if ((fdp = fd_get (fd)) == NULL || (fdp->f_flag & IPR) == 0 || (fdp->f_ip->i_mode & IFMT) != IFDIR) { set_user_error (EBADF); return 0; } ilock (fdp->f_ip, "dirio ()"); io.io_seg = IOSYS; io.io_seek = fdp->f_seek; io.io.vbase = (caddr_t) bp; io.io_ioc = n; io.io_flag = 0; if ((fdp->f_flag & IPNDLY) != 0) io.io_flag |= IONDLY; if ((fdp->f_flag & IPNONBLOCK) != 0) io.io_flag |= IONONBLOCK; iread (fdp->f_ip, & io); iaccessed (fdp->f_ip); /* read - atime */ n -= io.io_ioc; * offset = fdp->f_seek; fdp->f_seek += n; iunlock (fdp->f_ip); return n; } /* * Get directory entry in file system independent format. */ int ugetdents(fd, bp, n) int fd; /* File descriptor to an open directory */ char *bp; /* Buffer where entries should be read */ unsigned n; /* Number of bytes to be read */ { struct direct r_dir; unsigned bytes; /* Number of bytes */ struct dirent sd; ino_t inode; /* Inode number */ unsigned short ofnm; /* Offset to file name in dirent */ char *cw, *cr; int minbuf; /* Minimum possible size of the bp */ int i, mod; int entry; char ends[3] = ""; int total = 0; cw = bp; ofnm = sizeof (sd.d_ino) + sizeof (sd.d_off) + sizeof (sd.d_reclen); /* * Find minimum possible size of bp. It should be enough to contain * the header of dirent, file name + 0, and be on a sizeof(long) * boundary. */ entry = ofnm + DIRSIZ + 1; mod = entry % sizeof(long); minbuf = entry + (mod ? sizeof(long) - mod : 0); /* Is user buffer available? */ if (! useracc (bp, n, 1) || n < minbuf) { set_user_error (EFAULT); return -1; } while(n - (cw - bp) >= minbuf) { /* * Read next entry from the directory. * inode == 0 for rm(ed) entries */ do { if ((bytes = dirio (fd, & r_dir, sizeof (struct direct), & sd.d_off)) == 0) return total; inode = r_dir.d_ino; } while (! inode); /* Find the size of file name */ for (cr = r_dir.d_name, i = 0 ; * cr != 0 && i < DIRSIZ ; i ++, cr ++) /* DO NOTHING */ ; /* Copy file name */ if (! kucopy (r_dir.d_name, cw + ofnm, i)) return -1; /* Write 0 */ putubd (cw + ofnm + i ++, 0); /* Round up to long boundary */ if ((mod = (ofnm + i) % sizeof(long)) != 0) if (! kucopy (ends, cw + ofnm + i, sizeof (long) - mod)) return -1; sd.d_ino = r_dir.d_ino; sd.d_reclen = ofnm + i; /* Size of directory entry */ if (mod) sd.d_reclen += sizeof (long) - mod; if (! kucopy(& sd, cw, ofnm)) return -1; total += sd.d_reclen; cw += sd.d_reclen; } return total; } /* * Get file system information by file name. */ int ustatfs(path, stfs, len, fstyp) char *path; /* File name */ struct statfs *stfs; /* Pointer to a user structure */ int len; /* Size of the structure */ int fstyp; /* File system type */ { struct filsys *statmount(); /* Get mp for mounted device */ struct filsys *statunmount(); /* Get mp for unmounted device */ struct filsys *sb; /* Pointer to superblock */ int count = 0; /* Number of copied bytes */ short systype = 1; /* System type */ long bsize = BSIZE; /* Block size */ long frsize = 0; /* Fragment size */ /* Check if stfs is an available user area. */ if (! useracc ((char *) stfs, len, 1)) { SET_U_ERROR (EFAULT, "ustatfs 0"); return -1; } /* Filesystem type is 1 for 512 bytes blocks. */ count += sizeof (systype); if (count > len) { SET_U_ERROR (EFAULT, "ustatfs 1"); return -1; } if (! kucopy (& systype, & stfs->f_fstyp, sizeof (systype))) return -1; /* Block size */ count += sizeof (bsize); if (count > len) { SET_U_ERROR (EFAULT, "ustatfs 2"); return -1; } if (! kucopy (& bsize, & stfs->f_bsize, sizeof (bsize))) return -1; /* Fragment size. */ count += sizeof (int); if (count > len) { SET_U_ERROR (EFAULT, "ustatfs 3"); return -1; } if (! kucopy (& frsize, & stfs->f_frsize, sizeof (frsize))) return -1; if (! fstyp) { if ((sb = statmount (-1, path)) == NULL) return -1; devinfo (sb, stfs, len, & count); } else { if ((sb = statunmount (-1, path)) == NULL) return -1; devinfo (sb, stfs, len, & count); kfree (sb); } return 0; } /* * statmount - get superblock for mounted file system. * fd - file descriptor or -1, path - file name or NULL. */ struct filsys * statmount(fd, path) int fd; char *path; { MOUNT *mp; /* Pointer to device */ dev_t device; /* Mounted device */ /* Find the device */ if (path) { /* Find ip by file name */ struct direct dir; if (ftoi (path, 'r', IOUSR, NULL, & dir, SELF->p_credp)) return NULL; device = u.u_cdiri->i_dev; idetach (u.u_cdiri); } else { /* Find ip by file descriptor */ __fd_t * fdp; if ((fdp = fd_get (fd)) == NULL || (fdp->f_flag & IPR) == 0) { SET_U_ERROR (EBADF, "statmount 1"); return NULL; } device = fdp->f_ip->i_dev; } /* Take mount filesystem, check if dev is mounted device */ for (mp = mountp; mp != NULL; mp = mp->m_next) if (mp->m_dev == device) break; if (mp == NULL) { set_user_error (EINVAL); return NULL; } return & mp->m_super; } /* * devinfo() write system information to user area */ int devinfo (sb, stfs, len, count) struct filsys *sb; /* File name */ struct statfs *stfs; /* Pointer to a user structure */ int len; /* Size of the structure */ int *count; { long inode; /* Total number of blocks */ * count += sizeof (sb->s_fsize); if (* count > len) return -1; if (! kucopy (& sb->s_fsize, & stfs->f_blocks, sizeof (sb->s_fsize))) return -1; /* Count of free blocks */ * count += sizeof (sb->s_tfree); if (* count > len) return -1; if (! kucopy (& sb->s_tfree, & stfs->f_bfree, sizeof (sb->s_tfree))) return -1; /* Total number of file inodes */ * count += sizeof (inode); if (* count > len) return -1; inode = (long) (sb->s_isize - INODEI) * INOPB; if (! kucopy (& inode, & stfs->f_files, sizeof (inode))) return -1; /* Number of free inodes */ * count += sizeof(inode); if (* count > len) return -1; inode = sb->s_tinode; if (! kucopy (& inode, & stfs->f_ffree, sizeof (inode))) return -1; /* Volume name */ * count += sizeof (sb->s_fname); if (* count > len) return -1; if (! kucopy (sb->s_fname, stfs->f_fname, sizeof (sb->s_fpack))) return -1; /* Pack name */ * count += sizeof(sb->s_fpack); if (* count > len) return -1; if (! kucopy (sb->s_fpack, stfs->f_fpack, sizeof (sb->s_fpack))) return -1; return 0; } /* * statunmount - get superblock for unmounted file system. * fd - file descriptor or -1, path - file name or NULL. */ struct filsys * statunmount(fd, path) int fd; /* File descriptor */ char *path; /* File name */ { MOUNT *mp; dev_t rdev; int mode; BUF *bp; struct filsys *sb; /* Find the device */ if (path) { /* Find ip by file name */ struct direct dir; if (ftoi (path, 'r', IOUSR, NULL, & dir, SELF->p_credp)) return NULL; mode = u.u_cdiri->i_mode; rdev = u.u_cdiri->i_rdev; idetach (u.u_cdiri); } else { /* Find ip by file descriptor */ __fd_t *fdp; if ((fdp = fd_get (fd)) == NULL || (fdp->f_flag & IPR) == 0) { set_user_error (EBADF); return NULL; } ilock (fdp->f_ip, "statunmount ()"); mode = fdp->f_ip->i_mode; rdev = fdp->f_ip->i_rdev; iunlock (fdp->f_ip); } /* Check for block special device */ if ((mode & IFMT) != IFBLK) { set_user_error (ENOTBLK); return NULL; } /* Check if device is mounted device */ for (mp = mountp; mp != NULL; mp = mp->m_next) { if (mp->m_dev == rdev) { set_user_error (EBUSY); return NULL; } } (void) dopen (rdev, IPR, DFBLK, NULL); if (get_user_error ()) return NULL; /* * NIGEL: Modified for new dclose (). */ bp = bread (rdev, (daddr_t) SUPERI, BUF_SYNC); dclose (rdev, IPR, DFBLK, NULL); if (bp == NULL) return NULL; if ((sb = kalloc (sizeof (struct filsys))) == NULL) return NULL; memcpy (sb, bp->b_vaddr, sizeof (struct filsys)); /* * We must invalidate the buffer or the next try will * use this buffer even if the disk has changed (e.g., * for a floppy disk). */ buf_invalidate(bp); brelease (bp); cansuper (sb); /* canonicalize superblock */ if (tstf (sb) == 0) { /* check for consistency */ kfree (sb); set_user_error (EINVAL); return NULL; } return sb; } /* * Get file system information by file descriptor */ int ufstatfs (fildes, stfs, len, fstyp) int fildes; /* File descriptor */ struct statfs *stfs; /* Pointer to a user structure */ int len; /* Size of the structure */ int fstyp; /* File system type */ { struct filsys *statmount(); /* Get mp for mounted device */ struct filsys *statunmount(); /* Get mp for unmounted device */ struct filsys *sb; /* Pointer to superblock */ int count = 0; /* Number of copied bytes */ short systype = 1; /* System type */ long bsize = BSIZE; /* Block size */ long frsize = 0; /* Fragment size */ /* Check if stfs is an available user area. */ if (! useracc ((char *) stfs, len)) { set_user_error (EFAULT); return -1; } /* Filesystem type is 1 for 512 bytes blocks. */ count += sizeof (systype); if (count > len) { SET_U_ERROR (EFAULT, "ufstatfs 0"); return -1; } if (! kucopy (& systype, & stfs->f_fstyp, sizeof (systype))) return -1; /* Block size */ count += sizeof (bsize); if (count > len) { SET_U_ERROR (EFAULT, "ufstatfs 1"); return -1; } if (! kucopy (& bsize, & stfs->f_bsize, sizeof (bsize))) return -1; /* Fragment size. */ count += sizeof (int); if (count > len) { SET_U_ERROR (EFAULT, "ufstatfs 2"); return -1; } if (! kucopy (& frsize, & stfs->f_frsize, sizeof (frsize))) return -1; if (! fstyp) { if ((sb = statmount (fildes, NULL)) == NULL) return -1; if (devinfo (sb, stfs, len, & count) != 0) return -1; } else { if ((sb = statunmount (fildes, NULL)) == NULL) return -1; if (devinfo (sb, stfs, len, & count) != 0) { kfree (sb); return -1; } kfree (sb); } return 0; } /* * Check superblock for consistency. */ int tstf(fp) struct filsys *fp; { daddr_t *dp; ino_t *ip; ino_t maxinode; maxinode = (fp->s_isize - INODEI) * INOPB + 1; if (fp->s_isize >= fp->s_fsize) return 0; if (fp->s_tfree < fp->s_nfree || fp->s_tfree >= fp->s_fsize - fp->s_isize + 1) return 0; if (fp->s_tinode < fp->s_ninode || fp->s_tinode >= maxinode - 1) return 0; for (dp = fp->s_free ; dp < fp->s_free + fp->s_nfree ; dp += 1) if (* dp < fp->s_isize || * dp >= fp->s_fsize) return 0; for (ip = fp->s_inode ; ip < fp->s_inode + fp->s_ninode ; ip += 1) if (* ip < 1 || * ip > maxinode) return 0; return 1; } /* the following calls are not in the BCS */ int uadmin() { set_user_error (EINVAL); return -1; }