/*===========================================================================* * do_chdir * *===========================================================================*/ PUBLIC int do_chdir() { /* Change directory. This function is also called by MM to simulate a chdir * in order to do EXEC, etc. It also changes the root directory, the uids and * gids, and the umask. */ int r; register struct fproc *rfp; if (who == MM_PROC_NR) { rfp = &fproc[slot1]; put_inode(fp->fp_rootdir); dup_inode(fp->fp_rootdir = rfp->fp_rootdir); put_inode(fp->fp_workdir); dup_inode(fp->fp_workdir = rfp->fp_workdir); /* MM uses access() to check permissions. To make this work, pretend * that the user's real ids are the same as the user's effective ids. * FS calls other than access() do not use the real ids, so are not * affected. */ fp->fp_realuid = fp->fp_effuid = rfp->fp_effuid; fp->fp_realgid = fp->fp_effgid = rfp->fp_effgid; fp->fp_umask = rfp->fp_umask; return(OK); } /* Perform the chdir(name) system call. */ r = change(&fp->fp_workdir, name, name_length); return(r); }
/*===========================================================================* * do_fchdir * *===========================================================================*/ PUBLIC int do_fchdir() { /* Change directory on already-opened fd. */ struct filp *rfilp; /* Is the file descriptor valid? */ if ( (rfilp = get_filp(m_in.fd)) == NIL_FILP) return(err_code); dup_inode(rfilp->filp_ino); return change_into(&fp->fp_workdir, rfilp->filp_ino); }
/*===========================================================================* * do_pipe * *===========================================================================*/ PUBLIC int do_pipe() { /* Perform the pipe(fil_des) system call. */ register struct fproc *rfp; register struct inode *rip; int r; struct filp *fil_ptr0, *fil_ptr1; int fil_des[2]; /* reply goes here */ /* Acquire two file descriptors. */ rfp = fp; if ( (r = get_fd(0, R_BIT, &fil_des[0], &fil_ptr0)) != OK) return(r); rfp->fp_filp[fil_des[0]] = fil_ptr0; fil_ptr0->filp_count = 1; if ( (r = get_fd(0, W_BIT, &fil_des[1], &fil_ptr1)) != OK) { rfp->fp_filp[fil_des[0]] = NIL_FILP; fil_ptr0->filp_count = 0; return(r); } rfp->fp_filp[fil_des[1]] = fil_ptr1; fil_ptr1->filp_count = 1; /* Make the inode on the pipe device. */ if ( (rip = alloc_inode(root_dev, I_REGULAR) ) == NIL_INODE) { rfp->fp_filp[fil_des[0]] = NIL_FILP; fil_ptr0->filp_count = 0; rfp->fp_filp[fil_des[1]] = NIL_FILP; fil_ptr1->filp_count = 0; return(err_code); } if (read_only(rip) != OK) panic(__FILE__,"pipe device is read only", NO_NUM); rip->i_pipe = I_PIPE; rip->i_mode &= ~I_REGULAR; rip->i_mode |= I_NAMED_PIPE; /* pipes and FIFOs have this bit set */ fil_ptr0->filp_ino = rip; fil_ptr0->filp_flags = O_RDONLY; dup_inode(rip); /* for double usage */ fil_ptr1->filp_ino = rip; fil_ptr1->filp_flags = O_WRONLY; rw_inode(rip, WRITING); /* mark inode as allocated */ m_out.reply_i1 = fil_des[0]; m_out.reply_i2 = fil_des[1]; rip->i_update = ATIME | CTIME | MTIME; return(OK); }
struct inode* get_inode(ino_t i) { struct inode *i_node; struct dir_extent *extent; if (i == 0) return NULL; /* Try to get inode from cache. */ i_node = find_inode(i); if (i_node != NULL) { dup_inode(i_node); return i_node; } /* * Inode wasn't in cache, try to load it. * FIXME: a fake extent of one logical block is created for * read_inode(). Reading a inode this way could be problematic if * additional extents are stored behind the block boundary. */ i_node = alloc_inode(); extent = alloc_extent(); extent->location = i / v_pri.logical_block_size_l; extent->length = 1; if (read_inode(i_node, extent, i % v_pri.logical_block_size_l, NULL) != OK) { free_extent(extent); put_inode(i_node); return NULL; } free_extent(extent); return i_node; }
/*===========================================================================* * do_rename * *===========================================================================*/ PUBLIC int do_rename() { /* Perform the rename(name1, name2) system call. */ struct inode *old_dirp, *old_ip; /* ptrs to old dir, file inodes */ struct inode *new_dirp, *new_ip; /* ptrs to new dir, file inodes */ struct inode *new_superdirp, *next_new_superdirp; int r = OK; /* error flag; initially no error */ int odir, ndir; /* TRUE iff {old|new} file is dir */ int same_pdir; /* TRUE iff parent dirs are the same */ char old_name[NAME_MAX], new_name[NAME_MAX]; ino_t numb; int r1; /* See if 'name1' (existing file) exists. Get dir and file inodes. */ if (fetch_name(m_in.name1, m_in.name1_length, M1) != OK) return(err_code); if ( (old_dirp = last_dir(user_path, old_name))==NIL_INODE) return(err_code); if ( (old_ip = advance(old_dirp, old_name)) == NIL_INODE) r = err_code; /* See if 'name2' (new name) exists. Get dir and file inodes. */ if (fetch_name(m_in.name2, m_in.name2_length, M1) != OK) r = err_code; if ( (new_dirp = last_dir(user_path, new_name)) == NIL_INODE) r = err_code; new_ip = advance(new_dirp, new_name); /* not required to exist */ if (old_ip != NIL_INODE) odir = ((old_ip->i_mode & I_TYPE) == I_DIRECTORY); /* TRUE iff dir */ /* If it is ok, check for a variety of possible errors. */ if (r == OK) { same_pdir = (old_dirp == new_dirp); /* The old inode must not be a superdirectory of the new last dir. */ if (odir && !same_pdir) { dup_inode(new_superdirp = new_dirp); while (TRUE) { /* may hang in a file system loop */ if (new_superdirp == old_ip) { r = EINVAL; break; } next_new_superdirp = advance(new_superdirp, dot2); put_inode(new_superdirp); if (next_new_superdirp == new_superdirp) break; /* back at system root directory */ new_superdirp = next_new_superdirp; if (new_superdirp == NIL_INODE) { /* Missing ".." entry. Assume the worst. */ r = EINVAL; break; } } put_inode(new_superdirp); } /* The old or new name must not be . or .. */ if (strcmp(old_name, ".")==0 || strcmp(old_name, "..")==0 || strcmp(new_name, ".")==0 || strcmp(new_name, "..")==0) r = EINVAL; /* Both parent directories must be on the same device. */ if (old_dirp->i_dev != new_dirp->i_dev) r = EXDEV; /* Parent dirs must be writable, searchable and on a writable device */ if ((r1 = forbidden(old_dirp, W_BIT | X_BIT)) != OK || (r1 = forbidden(new_dirp, W_BIT | X_BIT)) != OK) r = r1; /* Some tests apply only if the new path exists. */ if (new_ip == NIL_INODE) { /* don't rename a file with a file system mounted on it. */ if (old_ip->i_dev != old_dirp->i_dev) r = EXDEV; if (odir && new_dirp->i_nlinks >= (new_dirp->i_sp->s_version == V1 ? CHAR_MAX : SHRT_MAX) && !same_pdir && r == OK) r = EMLINK; } else { if (old_ip == new_ip) r = SAME; /* old=new */ /* has the old file or new file a file system mounted on it? */ if (old_ip->i_dev != new_ip->i_dev) r = EXDEV; ndir = ((new_ip->i_mode & I_TYPE) == I_DIRECTORY); /* dir ? */ if (odir == TRUE && ndir == FALSE) r = ENOTDIR; if (odir == FALSE && ndir == TRUE) r = EISDIR; } } /* If a process has another root directory than the system root, we might * "accidently" be moving it's working directory to a place where it's * root directory isn't a super directory of it anymore. This can make * the function chroot useless. If chroot will be used often we should * probably check for it here. */ /* The rename will probably work. Only two things can go wrong now: * 1. being unable to remove the new file. (when new file already exists) * 2. being unable to make the new directory entry. (new file doesn't exists) * [directory has to grow by one block and cannot because the disk * is completely full]. */ if (r == OK) { if (new_ip != NIL_INODE) { /* There is already an entry for 'new'. Try to remove it. */ if (odir) r = remove_dir(new_dirp, new_ip, new_name); else r = unlink_file(new_dirp, new_ip, new_name); } /* if r is OK, the rename will succeed, while there is now an * unused entry in the new parent directory. */ } if (r == OK) { /* If the new name will be in the same parent directory as the old one, * first remove the old name to free an entry for the new name, * otherwise first try to create the new name entry to make sure * the rename will succeed. */ numb = old_ip->i_num; /* inode number of old file */ if (same_pdir) { r = search_dir(old_dirp, old_name, (ino_t *) 0, DELETE); /* shouldn't go wrong. */ if (r==OK) (void) search_dir(old_dirp, new_name, &numb, ENTER); } else { r = search_dir(new_dirp, new_name, &numb, ENTER); if (r == OK) (void) search_dir(old_dirp, old_name, (ino_t *) 0, DELETE); } } /* If r is OK, the ctime and mtime of old_dirp and new_dirp have been marked * for update in search_dir. */ if (r == OK && odir && !same_pdir) { /* Update the .. entry in the directory (still points to old_dirp). */ numb = new_dirp->i_num; (void) unlink_file(old_ip, NIL_INODE, dot2); if (search_dir(old_ip, dot2, &numb, ENTER) == OK) { /* New link created. */ new_dirp->i_nlinks++; new_dirp->i_dirt = DIRTY; } } /* Release the inodes. */ put_inode(old_dirp); put_inode(old_ip); put_inode(new_dirp); put_inode(new_ip); return(r == SAME ? OK : r); }
/*===========================================================================* * do_mount * *===========================================================================*/ PUBLIC int do_mount() { /* Perform the mount(name, mfile, rd_only) system call. */ register struct inode *rip, *root_ip; struct super_block *xp, *sp; dev_t dev; mode_t bits; int rdir, mdir; /* TRUE iff {root|mount} file is dir */ int i, r, found; struct fproc *tfp; /* Only the super-user may do MOUNT. */ if (!super_user) return(EPERM); /* If 'name' is not for a block special file, return error. */ if (fetch_name(m_in.name1, m_in.name1_length, M1) != OK) return(err_code); if ( (dev = name_to_dev(user_path)) == NO_DEV) return(err_code); /* Scan super block table to see if dev already mounted & find a free slot.*/ sp = NIL_SUPER; found = FALSE; for (xp = &super_block[0]; xp < &super_block[NR_SUPERS]; xp++) { if (xp->s_dev == dev) { /* is it mounted already? */ found = TRUE; sp= xp; break; } if (xp->s_dev == NO_DEV) sp = xp; /* record free slot */ } if (found) { printf( "do_mount: s_imount = 0x%x (%x, %d), s_isup = 0x%x (%x, %d), fp_rootdir = 0x%x\n", xp->s_imount, xp->s_imount->i_dev, xp->s_imount->i_num, xp->s_isup, xp->s_isup->i_dev, xp->s_isup->i_num, fproc[FS_PROC_NR].fp_rootdir); /* It is possible that we have an old root lying around that * needs to be remounted. */ if (xp->s_imount != xp->s_isup || xp->s_isup == fproc[FS_PROC_NR].fp_rootdir) { /* Normally, s_imount refers to the mount point. For a root * filesystem, s_imount is equal to the root inode. We assume * that the root of FS is always the real root. If the two * inodes are different or if the root of FS is equal two the * root of the filesystem we found, we found a filesystem that * is in use. */ return(EBUSY); /* already mounted */ } if (root_dev == xp->s_dev) { panic("fs", "inconsistency remounting old root", NO_NUM); } /* Now get the inode of the file to be mounted on. */ if (fetch_name(m_in.name2, m_in.name2_length, M1) != OK) { return(err_code); } if ( (rip = eat_path(user_path)) == NIL_INODE) { return(err_code); } r = OK; /* It may not be special. */ bits = rip->i_mode & I_TYPE; if (bits == I_BLOCK_SPECIAL || bits == I_CHAR_SPECIAL) r = ENOTDIR; /* Get the root inode of the mounted file system. */ root_ip= sp->s_isup; /* File types of 'rip' and 'root_ip' may not conflict. */ if (r == OK) { mdir = ((rip->i_mode & I_TYPE) == I_DIRECTORY); /* TRUE iff dir */ rdir = ((root_ip->i_mode & I_TYPE) == I_DIRECTORY); if (!mdir && rdir) r = EISDIR; } /* If error, return the mount point. */ if (r != OK) { put_inode(rip); return(r); } /* Nothing else can go wrong. Perform the mount. */ rip->i_mount = I_MOUNT; /* this bit says the inode is * mounted on */ put_inode(sp->s_imount); sp->s_imount = rip; sp->s_rd_only = m_in.rd_only; allow_newroot= 0; /* The root is now fixed */ return(OK); } if (sp == NIL_SUPER) return(ENFILE); /* no super block available */ /* Open the device the file system lives on. */ if (dev_open(dev, who_e, m_in.rd_only ? R_BIT : (R_BIT|W_BIT)) != OK) return(EINVAL); /* Make the cache forget about blocks it has open on the filesystem */ (void) do_sync(); invalidate(dev); /* Fill in the super block. */ sp->s_dev = dev; /* read_super() needs to know which dev */ r = read_super(sp); /* Is it recognized as a Minix filesystem? */ if (r != OK) { dev_close(dev); sp->s_dev = NO_DEV; return(r); } /* Now get the inode of the file to be mounted on. */ if (fetch_name(m_in.name2, m_in.name2_length, M1) != OK) { dev_close(dev); sp->s_dev = NO_DEV; return(err_code); } if (strcmp(user_path, "/") == 0 && allow_newroot) { printf("Replacing root\n"); /* Get the root inode of the mounted file system. */ if ( (root_ip = get_inode(dev, ROOT_INODE)) == NIL_INODE) r = err_code; if (root_ip != NIL_INODE && root_ip->i_mode == 0) { r = EINVAL; } /* If error, return the super block and both inodes; release the * maps. */ if (r != OK) { put_inode(root_ip); (void) do_sync(); invalidate(dev); dev_close(dev); sp->s_dev = NO_DEV; return(r); } /* Nothing else can go wrong. Perform the mount. */ sp->s_imount = root_ip; dup_inode(root_ip); sp->s_isup = root_ip; sp->s_rd_only = m_in.rd_only; root_dev= dev; /* Replace all root and working directories */ for (i= 0, tfp= fproc; i<NR_PROCS; i++, tfp++) { if (tfp->fp_pid == PID_FREE) continue; if (tfp->fp_rootdir == NULL) panic("fs", "do_mount: null rootdir", i); put_inode(tfp->fp_rootdir); dup_inode(root_ip); tfp->fp_rootdir= root_ip; if (tfp->fp_workdir == NULL) panic("fs", "do_mount: null workdir", i); put_inode(tfp->fp_workdir); dup_inode(root_ip); tfp->fp_workdir= root_ip; } /* Leave the old filesystem lying around. */ return(OK); } if ( (rip = eat_path(user_path)) == NIL_INODE) { dev_close(dev); sp->s_dev = NO_DEV; return(err_code); } /* It may not be busy. */ r = OK; if (rip->i_count > 1) r = EBUSY; /* It may not be special. */ bits = rip->i_mode & I_TYPE; if (bits == I_BLOCK_SPECIAL || bits == I_CHAR_SPECIAL) r = ENOTDIR; /* Get the root inode of the mounted file system. */ root_ip = NIL_INODE; /* if 'r' not OK, make sure this is defined */ if (r == OK) { if ( (root_ip = get_inode(dev, ROOT_INODE)) == NIL_INODE) r = err_code; } if (root_ip != NIL_INODE && root_ip->i_mode == 0) { r = EINVAL; } /* File types of 'rip' and 'root_ip' may not conflict. */ if (r == OK) { mdir = ((rip->i_mode & I_TYPE) == I_DIRECTORY); /* TRUE iff dir */ rdir = ((root_ip->i_mode & I_TYPE) == I_DIRECTORY); if (!mdir && rdir) r = EISDIR; } /* If error, return the super block and both inodes; release the maps. */ if (r != OK) { put_inode(rip); put_inode(root_ip); (void) do_sync(); invalidate(dev); dev_close(dev); sp->s_dev = NO_DEV; return(r); } /* Nothing else can go wrong. Perform the mount. */ rip->i_mount = I_MOUNT; /* this bit says the inode is mounted on */ sp->s_imount = rip; sp->s_isup = root_ip; sp->s_rd_only = m_in.rd_only; allow_newroot= 0; /* The root is now fixed */ return(OK); }