BOOK.T
上传用户:jnzhq888
上传日期:2007-01-18
资源大小:51694k
文件大小:987k
- 24308 * pipe_check: check to see that a read or write on a pipe is feasible now
- 24309 * suspend: suspend a process that cannot do a requested read or write
- 24310 * release: check to see if a suspended process can be released and do it
- 24311 * revive: mark a suspended process as able to run again
- 24312 * do_unpause: a signal has been sent to a process; see if it suspended
- 24313 */
- 24314
- 24315 #include "fs.h"
- 24316 #include <fcntl.h>
- 24317 #include <signal.h>
- 24318 #include <minix/boot.h>
- 24319 #include <minix/callnr.h>
- 24320 #include <minix/com.h>
- 24321 #include "dev.h"
- 24322 #include "file.h"
- 24323 #include "fproc.h"
- 24324 #include "inode.h"
- 24325 #include "param.h"
- 24326
- 24327 PRIVATE message mess;
- 24328
- 24329 /*===========================================================================*
- 24330 * do_pipe *
- 24331 *===========================================================================*/
- 24332 PUBLIC int do_pipe()
- 24333 {
- 24334 /* Perform the pipe(fil_des) system call. */
- 24335
- 24336 register struct fproc *rfp;
- 24337 register struct inode *rip;
- 24338 int r;
- 24339 struct filp *fil_ptr0, *fil_ptr1;
- 24340 int fil_des[2]; /* reply goes here */
- 24341
- 24342 /* Acquire two file descriptors. */
- 24343 rfp = fp;
- 24344 if ( (r = get_fd(0, R_BIT, &fil_des[0], &fil_ptr0)) != OK) return(r);
- 24345 rfp->fp_filp[fil_des[0]] = fil_ptr0;
- 24346 fil_ptr0->filp_count = 1;
- 24347 if ( (r = get_fd(0, W_BIT, &fil_des[1], &fil_ptr1)) != OK) {
- 24348 rfp->fp_filp[fil_des[0]] = NIL_FILP;
- 24349 fil_ptr0->filp_count = 0;
- .Op 335 src/fs/pipe.c
- 24350 return(r);
- 24351 }
- 24352 rfp->fp_filp[fil_des[1]] = fil_ptr1;
- 24353 fil_ptr1->filp_count = 1;
- 24354
- 24355 /* Make the inode on the pipe device. */
- 24356 if ( (rip = alloc_inode(PIPE_DEV, I_REGULAR) ) == NIL_INODE) {
- 24357 rfp->fp_filp[fil_des[0]] = NIL_FILP;
- 24358 fil_ptr0->filp_count = 0;
- 24359 rfp->fp_filp[fil_des[1]] = NIL_FILP;
- 24360 fil_ptr1->filp_count = 0;
- 24361 return(err_code);
- 24362 }
- 24363
- 24364 if (read_only(rip) != OK) panic("pipe device is read only", NO_NUM);
- 24365
- 24366 rip->i_pipe = I_PIPE;
- 24367 rip->i_mode &= ~I_REGULAR;
- 24368 rip->i_mode |= I_NAMED_PIPE; /* pipes and FIFOs have this bit set */
- 24369 fil_ptr0->filp_ino = rip;
- 24370 fil_ptr0->filp_flags = O_RDONLY;
- 24371 dup_inode(rip); /* for double usage */
- 24372 fil_ptr1->filp_ino = rip;
- 24373 fil_ptr1->filp_flags = O_WRONLY;
- 24374 rw_inode(rip, WRITING); /* mark inode as allocated */
- 24375 reply_i1 = fil_des[0];
- 24376 reply_i2 = fil_des[1];
- 24377 rip->i_update = ATIME | CTIME | MTIME;
- 24378 return(OK);
- 24379 }
-
-
- 24382 /*===========================================================================*
- 24383 * pipe_check *
- 24384 *===========================================================================*/
- 24385 PUBLIC int pipe_check(rip, rw_flag, oflags, bytes, position, canwrite)
- 24386 register struct inode *rip; /* the inode of the pipe */
- 24387 int rw_flag; /* READING or WRITING */
- 24388 int oflags; /* flags set by open or fcntl */
- 24389 register int bytes; /* bytes to be read or written (all chunks) */
- 24390 register off_t position; /* current file position */
- 24391 int *canwrite; /* return: number of bytes we can write */
- 24392 {
- 24393 /* Pipes are a little different. If a process reads from an empty pipe for
- 24394 * which a writer still exists, suspend the reader. If the pipe is empty
- 24395 * and there is no writer, return 0 bytes. If a process is writing to a
- 24396 * pipe and no one is reading from it, give a broken pipe error.
- 24397 */
- 24398
- 24399 int r = 0;
- 24400
- 24401 /* If reading, check for empty pipe. */
- 24402 if (rw_flag == READING) {
- 24403 if (position >= rip->i_size) {
- 24404 /* Process is reading from an empty pipe. */
- 24405 if (find_filp(rip, W_BIT) != NIL_FILP) {
- 24406 /* Writer exists */
- 24407 if (oflags & O_NONBLOCK)
- 24408 r = EAGAIN;
- 24409 else
- .Ep 336 src/fs/pipe.c
- 24410 suspend(XPIPE); /* block reader */
- 24411
- 24412 /* If need be, activate sleeping writers. */
- 24413 if (susp_count > 0) release(rip, WRITE, susp_count);
- 24414 }
- 24415 return(r);
- 24416 }
- 24417 } else {
- 24418 /* Process is writing to a pipe. */
- 24419 /* if (bytes > PIPE_SIZE) return(EFBIG); */
- 24420 if (find_filp(rip, R_BIT) == NIL_FILP) {
- 24421 /* Tell kernel to generate a SIGPIPE signal. */
- 24422 sys_kill((int)(fp - fproc), SIGPIPE);
- 24423 return(EPIPE);
- 24424 }
- 24425
- 24426 if (position + bytes > PIPE_SIZE) {
- 24427 if ((oflags & O_NONBLOCK) && bytes < PIPE_SIZE)
- 24428 return(EAGAIN);
- 24429 else if ((oflags & O_NONBLOCK) && bytes > PIPE_SIZE) {
- 24430 if ( (*canwrite = (PIPE_SIZE - position)) > 0) {
- 24431 /* Do a partial write. Need to wakeup reader */
- 24432 release(rip, READ, susp_count);
- 24433 return(1);
- 24434 } else {
- 24435 return(EAGAIN);
- 24436 }
- 24437 }
- 24438 if (bytes > PIPE_SIZE) {
- 24439 if ((*canwrite = PIPE_SIZE - position) > 0) {
- 24440 /* Do a partial write. Need to wakeup reader
- 24441 * since we'll suspend ourself in read_write()
- 24442 */
- 24443 release(rip, READ, susp_count);
- 24444 return(1);
- 24445 }
- 24446 }
- 24447 suspend(XPIPE); /* stop writer -- pipe full */
- 24448 return(0);
- 24449 }
- 24450
- 24451 /* Writing to an empty pipe. Search for suspended reader. */
- 24452 if (position == 0) release(rip, READ, susp_count);
- 24453 }
- 24454
- 24455 *canwrite = 0;
- 24456 return(1);
- 24457 }
-
-
- 24460 /*===========================================================================*
- 24461 * suspend *
- 24462 *===========================================================================*/
- 24463 PUBLIC void suspend(task)
- 24464 int task; /* who is proc waiting for? (PIPE = pipe) */
- 24465 {
- 24466 /* Take measures to suspend the processing of the present system call.
- 24467 * Store the parameters to be used upon resuming in the process table.
- 24468 * (Actually they are not used when a process is waiting for an I/O device,
- 24469 * but they are needed for pipes, and it is not worth making the distinction.)
- .Op 337 src/fs/pipe.c
- 24470 */
- 24471
- 24472 if (task == XPIPE || task == XPOPEN) susp_count++;/* #procs susp'ed on pipe*/
- 24473 fp->fp_suspended = SUSPENDED;
- 24474 fp->fp_fd = fd << 8 | fs_call;
- 24475 fp->fp_task = -task;
- 24476 if (task == XLOCK) {
- 24477 fp->fp_buffer = (char *) name1; /* third arg to fcntl() */
- 24478 fp->fp_nbytes =request; /* second arg to fcntl() */
- 24479 } else {
- 24480 fp->fp_buffer = buffer; /* for reads and writes */
- 24481 fp->fp_nbytes = nbytes;
- 24482 }
- 24483 dont_reply = TRUE; /* do not send caller a reply message now */
- 24484 }
-
-
- 24487 /*===========================================================================*
- 24488 * release *
- 24489 *===========================================================================*/
- 24490 PUBLIC void release(ip, call_nr, count)
- 24491 register struct inode *ip; /* inode of pipe */
- 24492 int call_nr; /* READ, WRITE, OPEN or CREAT */
- 24493 int count; /* max number of processes to release */
- 24494 {
- 24495 /* Check to see if any process is hanging on the pipe whose inode is in 'ip'.
- 24496 * If one is, and it was trying to perform the call indicated by 'call_nr',
- 24497 * release it.
- 24498 */
- 24499
- 24500 register struct fproc *rp;
- 24501
- 24502 /* Search the proc table. */
- 24503 for (rp = &fproc[0]; rp < &fproc[NR_PROCS]; rp++) {
- 24504 if (rp->fp_suspended == SUSPENDED &&
- 24505 rp->fp_revived == NOT_REVIVING &&
- 24506 (rp->fp_fd & BYTE) == call_nr &&
- 24507 rp->fp_filp[rp->fp_fd>>8]->filp_ino == ip) {
- 24508 revive((int)(rp - fproc), 0);
- 24509 susp_count--; /* keep track of who is suspended */
- 24510 if (--count == 0) return;
- 24511 }
- 24512 }
- 24513 }
-
-
- 24516 /*===========================================================================*
- 24517 * revive *
- 24518 *===========================================================================*/
- 24519 PUBLIC void revive(proc_nr, bytes)
- 24520 int proc_nr; /* process to revive */
- 24521 int bytes; /* if hanging on task, how many bytes read */
- 24522 {
- 24523 /* Revive a previously blocked process. When a process hangs on tty, this
- 24524 * is the way it is eventually released.
- 24525 */
- 24526
- 24527 register struct fproc *rfp;
- 24528 register int task;
- 24529
- .Ep 338 src/fs/pipe.c
- 24530 if (proc_nr < 0 || proc_nr >= NR_PROCS) panic("revive err", proc_nr);
- 24531 rfp = &fproc[proc_nr];
- 24532 if (rfp->fp_suspended == NOT_SUSPENDED || rfp->fp_revived == REVIVING)return;
- 24533
- 24534 /* The 'reviving' flag only applies to pipes. Processes waiting for TTY get
- 24535 * a message right away. The revival process is different for TTY and pipes.
- 24536 * For TTY revival, the work is already done, for pipes it is not: the proc
- 24537 * must be restarted so it can try again.
- 24538 */
- 24539 task = -rfp->fp_task;
- 24540 if (task == XPIPE || task == XLOCK) {
- 24541 /* Revive a process suspended on a pipe or lock. */
- 24542 rfp->fp_revived = REVIVING;
- 24543 reviving++; /* process was waiting on pipe or lock */
- 24544 } else {
- 24545 rfp->fp_suspended = NOT_SUSPENDED;
- 24546 if (task == XPOPEN) /* process blocked in open or create */
- 24547 reply(proc_nr, rfp->fp_fd>>8);
- 24548 else {
- 24549 /* Revive a process suspended on TTY or other device. */
- 24550 rfp->fp_nbytes = bytes; /*pretend it wants only what there is*/
- 24551 reply(proc_nr, bytes); /* unblock the process */
- 24552 }
- 24553 }
- 24554 }
-
-
- 24557 /*===========================================================================*
- 24558 * do_unpause *
- 24559 *===========================================================================*/
- 24560 PUBLIC int do_unpause()
- 24561 {
- 24562 /* A signal has been sent to a user who is paused on the file system.
- 24563 * Abort the system call with the EINTR error message.
- 24564 */
- 24565
- 24566 register struct fproc *rfp;
- 24567 int proc_nr, task, fild;
- 24568 struct filp *f;
- 24569 dev_t dev;
- 24570
- 24571 if (who > MM_PROC_NR) return(EPERM);
- 24572 proc_nr = pro;
- 24573 if (proc_nr < 0 || proc_nr >= NR_PROCS) panic("unpause err 1", proc_nr);
- 24574 rfp = &fproc[proc_nr];
- 24575 if (rfp->fp_suspended == NOT_SUSPENDED) return(OK);
- 24576 task = -rfp->fp_task;
- 24577
- 24578 switch(task) {
- 24579 case XPIPE: /* process trying to read or write a pipe */
- 24580 break;
- 24581
- 24582 case XOPEN: /* process trying to open a special file */
- 24583 panic ("fs/do_unpause called with XOPENn", NO_NUM);
- 24584
- 24585 case XLOCK: /* process trying to set a lock with FCNTL */
- 24586 break;
- 24587
- 24588 case XPOPEN: /* process trying to open a fifo */
- 24589 break;
- .Op 339 src/fs/pipe.c
- 24590
- 24591 default: /* process trying to do device I/O (e.g. tty)*/
- 24592 fild = (rfp->fp_fd >> 8) & BYTE;/* extract file descriptor */
- 24593 if (fild < 0 || fild >= OPEN_MAX)panic("unpause err 2",NO_NUM);
- 24594 f = rfp->fp_filp[fild];
- 24595 dev = (dev_t) f->filp_ino->i_zone[0]; /* device hung on */
- 24596 mess.TTY_LINE = (dev >> MINOR) & BYTE;
- 24597 mess.PROC_NR = proc_nr;
- 24598
- 24599 /* Tell kernel R or W. Mode is from current call, not open. */
- 24600 mess.COUNT = (rfp->fp_fd & BYTE) == READ ? R_BIT : W_BIT;
- 24601 mess.m_type = CANCEL;
- 24602 fp = rfp; /* hack - call_ctty uses fp */
- 24603 (*dmap[(dev >> MAJOR) & BYTE].dmap_rw)(task, &mess);
- 24604 }
- 24605
- 24606 rfp->fp_suspended = NOT_SUSPENDED;
- 24607 reply(proc_nr, EINTR); /* signal interrupted call */
- 24608 return(OK);
- 24609 }
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- src/fs/path.c
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- 24700 /* This file contains the procedures that look up path names in the directory
- 24701 * system and determine the inode number that goes with a given path name.
- 24702 *
- 24703 * The entry points into this file are
- 24704 * eat_path: the 'main' routine of the path-to-inode conversion mechanism
- 24705 * last_dir: find the final directory on a given path
- 24706 * advance: parse one component of a path name
- 24707 * search_dir: search a directory for a string and return its inode number
- 24708 */
- 24709
- 24710 #include "fs.h"
- 24711 #include <string.h>
- 24712 #include <minix/callnr.h>
- 24713 #include "buf.h"
- 24714 #include "file.h"
- 24715 #include "fproc.h"
- 24716 #include "inode.h"
- 24717 #include "super.h"
- 24718
- 24719 PUBLIC char dot1[2] = "."; /* used for search_dir to bypass the access */
- 24720 PUBLIC char dot2[3] = ".."; /* permissions for . and .. */
- 24721
- 24722 FORWARD _PROTOTYPE( char *get_name, (char *old_name, char string [NAME_MAX]) );
- 24723
- 24724 /*===========================================================================*
- 24725 * eat_path *
- 24726 *===========================================================================*/
- 24727 PUBLIC struct inode *eat_path(path)
- 24728 char *path; /* the path name to be parsed */
- 24729 {
- .Ep 340 src/fs/path.c
- 24730 /* Parse the path 'path' and put its inode in the inode table. If not possible,
- 24731 * return NIL_INODE as function value and an error code in 'err_code'.
- 24732 */
- 24733
- 24734 register struct inode *ldip, *rip;
- 24735 char string[NAME_MAX]; /* hold 1 path component name here */
- 24736
- 24737 /* First open the path down to the final directory. */
- 24738 if ( (ldip = last_dir(path, string)) == NIL_INODE)
- 24739 return(NIL_INODE); /* we couldn't open final directory */
- 24740
- 24741 /* The path consisting only of "/" is a special case, check for it. */
- 24742 if (string[0] == ' ') return(ldip);
- 24743
- 24744 /* Get final component of the path. */
- 24745 rip = advance(ldip, string);
- 24746 put_inode(ldip);
- 24747 return(rip);
- 24748 }
-
-
- 24751 /*===========================================================================*
- 24752 * last_dir *
- 24753 *===========================================================================*/
- 24754 PUBLIC struct inode *last_dir(path, string)
- 24755 char *path; /* the path name to be parsed */
- 24756 char string[NAME_MAX]; /* the final component is returned here */
- 24757 {
- 24758 /* Given a path, 'path', located in the fs address space, parse it as
- 24759 * far as the last directory, fetch the inode for the last directory into
- 24760 * the inode table, and return a pointer to the inode. In
- 24761 * addition, return the final component of the path in 'string'.
- 24762 * If the last directory can't be opened, return NIL_INODE and
- 24763 * the reason for failure in 'err_code'.
- 24764 */
- 24765
- 24766 register struct inode *rip;
- 24767 register char *new_name;
- 24768 register struct inode *new_ip;
- 24769
- 24770 /* Is the path absolute or relative? Initialize 'rip' accordingly. */
- 24771 rip = (*path == '/' ? fp->fp_rootdir : fp->fp_workdir);
- 24772
- 24773 /* If dir has been removed or path is empty, return ENOENT. */
- 24774 if (rip->i_nlinks == 0 || *path == ' ') {
- 24775 err_code = ENOENT;
- 24776 return(NIL_INODE);
- 24777 }
- 24778
- 24779 dup_inode(rip); /* inode will be returned with put_inode */
- 24780
- 24781 /* Scan the path component by component. */
- 24782 while (TRUE) {
- 24783 /* Extract one component. */
- 24784 if ( (new_name = get_name(path, string)) == (char*) 0) {
- 24785 put_inode(rip); /* bad path in user space */
- 24786 return(NIL_INODE);
- 24787 }
- 24788 if (*new_name == ' ')
- 24789 if ( (rip->i_mode & I_TYPE) == I_DIRECTORY)
- .Op 341 src/fs/path.c
- 24790 return(rip); /* normal exit */
- 24791 else {
- 24792 /* last file of path prefix is not a directory */
- 24793 put_inode(rip);
- 24794 err_code = ENOTDIR;
- 24795 return(NIL_INODE);
- 24796 }
- 24797
- 24798 /* There is more path. Keep parsing. */
- 24799 new_ip = advance(rip, string);
- 24800 put_inode(rip); /* rip either obsolete or irrelevant */
- 24801 if (new_ip == NIL_INODE) return(NIL_INODE);
- 24802
- 24803 /* The call to advance() succeeded. Fetch next component. */
- 24804 path = new_name;
- 24805 rip = new_ip;
- 24806 }
- 24807 }
-
-
- 24810 /*===========================================================================*
- 24811 * get_name *
- 24812 *===========================================================================*/
- 24813 PRIVATE char *get_name(old_name, string)
- 24814 char *old_name; /* path name to parse */
- 24815 char string[NAME_MAX]; /* component extracted from 'old_name' */
- 24816 {
- 24817 /* Given a pointer to a path name in fs space, 'old_name', copy the next
- 24818 * component to 'string' and pad with zeros. A pointer to that part of
- 24819 * the name as yet unparsed is returned. Roughly speaking,
- 24820 * 'get_name' = 'old_name' - 'string'.
- 24821 *
- 24822 * This routine follows the standard convention that /usr/ast, /usr//ast,
- 24823 * //usr///ast and /usr/ast/ are all equivalent.
- 24824 */
- 24825
- 24826 register int c;
- 24827 register char *np, *rnp;
- 24828
- 24829 np = string; /* 'np' points to current position */
- 24830 rnp = old_name; /* 'rnp' points to unparsed string */
- 24831 while ( (c = *rnp) == '/') rnp++; /* skip leading slashes */
- 24832
- 24833 /* Copy the unparsed path, 'old_name', to the array, 'string'. */
- 24834 while ( rnp < &old_name[PATH_MAX] && c != '/' && c != ' ') {
- 24835 if (np < &string[NAME_MAX]) *np++ = c;
- 24836 c = *++rnp; /* advance to next character */
- 24837 }
- 24838
- 24839 /* To make /usr/ast/ equivalent to /usr/ast, skip trailing slashes. */
- 24840 while (c == '/' && rnp < &old_name[PATH_MAX]) c = *++rnp;
- 24841
- 24842 if (np < &string[NAME_MAX]) *np = ' '; /* Terminate string */
- 24843
- 24844 if (rnp >= &old_name[PATH_MAX]) {
- 24845 err_code = ENAMETOOLONG;
- 24846 return((char *) 0);
- 24847 }
- 24848 return(rnp);
- 24849 }
- .Ep 342 src/fs/path.c
-
-
- 24852 /*===========================================================================*
- 24853 * advance *
- 24854 *===========================================================================*/
- 24855 PUBLIC struct inode *advance(dirp, string)
- 24856 struct inode *dirp; /* inode for directory to be searched */
- 24857 char string[NAME_MAX]; /* component name to look for */
- 24858 {
- 24859 /* Given a directory and a component of a path, look up the component in
- 24860 * the directory, find the inode, open it, and return a pointer to its inode
- 24861 * slot. If it can't be done, return NIL_INODE.
- 24862 */
- 24863
- 24864 register struct inode *rip;
- 24865 struct inode *rip2;
- 24866 register struct super_block *sp;
- 24867 int r, inumb;
- 24868 dev_t mnt_dev;
- 24869 ino_t numb;
- 24870
- 24871 /* If 'string' is empty, yield same inode straight away. */
- 24872 if (string[0] == ' ') return(get_inode(dirp->i_dev, (int) dirp->i_num));
- 24873
- 24874 /* Check for NIL_INODE. */
- 24875 if (dirp == NIL_INODE) return(NIL_INODE);
- 24876
- 24877 /* If 'string' is not present in the directory, signal error. */
- 24878 if ( (r = search_dir(dirp, string, &numb, LOOK_UP)) != OK) {
- 24879 err_code = r;
- 24880 return(NIL_INODE);
- 24881 }
- 24882
- 24883 /* Don't go beyond the current root directory, unless the string is dot2. */
- 24884 if (dirp == fp->fp_rootdir && strcmp(string, "..") == 0 && string != dot2)
- 24885 return(get_inode(dirp->i_dev, (int) dirp->i_num));
- 24886
- 24887 /* The component has been found in the directory. Get inode. */
- 24888 if ( (rip = get_inode(dirp->i_dev, (int) numb)) == NIL_INODE)
- 24889 return(NIL_INODE);
- 24890
- 24891 if (rip->i_num == ROOT_INODE)
- 24892 if (dirp->i_num == ROOT_INODE) {
- 24893 if (string[1] == '.') {
- 24894 for (sp = &super_block[1]; sp < &super_block[NR_SUPERS]; sp++){
- 24895 if (sp->s_dev == rip->i_dev) {
- 24896 /* Release the root inode. Replace by the
- 24897 * inode mounted on.
- 24898 */
- 24899 put_inode(rip);
- 24900 mnt_dev = sp->s_imount->i_dev;
- 24901 inumb = (int) sp->s_imount->i_num;
- 24902 rip2 = get_inode(mnt_dev, inumb);
- 24903 rip = advance(rip2, string);
- 24904 put_inode(rip2);
- 24905 break;
- 24906 }
- 24907 }
- 24908 }
- 24909 }
- .Op 343 src/fs/path.c
- 24910 if (rip == NIL_INODE) return(NIL_INODE);
- 24911
- 24912 /* See if the inode is mounted on. If so, switch to root directory of the
- 24913 * mounted file system. The super_block provides the linkage between the
- 24914 * inode mounted on and the root directory of the mounted file system.
- 24915 */
- 24916 while (rip != NIL_INODE && rip->i_mount == I_MOUNT) {
- 24917 /* The inode is indeed mounted on. */
- 24918 for (sp = &super_block[0]; sp < &super_block[NR_SUPERS]; sp++) {
- 24919 if (sp->s_imount == rip) {
- 24920 /* Release the inode mounted on. Replace by the
- 24921 * inode of the root inode of the mounted device.
- 24922 */
- 24923 put_inode(rip);
- 24924 rip = get_inode(sp->s_dev, ROOT_INODE);
- 24925 break;
- 24926 }
- 24927 }
- 24928 }
- 24929 return(rip); /* return pointer to inode's component */
- 24930 }
-
-
- 24933 /*===========================================================================*
- 24934 * search_dir *
- 24935 *===========================================================================*/
- 24936 PUBLIC int search_dir(ldir_ptr, string, numb, flag)
- 24937 register struct inode *ldir_ptr; /* ptr to inode for dir to search */
- 24938 char string[NAME_MAX]; /* component to search for */
- 24939 ino_t *numb; /* pointer to inode number */
- 24940 int flag; /* LOOK_UP, ENTER, DELETE or IS_EMPTY */
- 24941 {
- 24942 /* This function searches the directory whose inode is pointed to by 'ldip':
- 24943 * if (flag == ENTER) enter 'string' in the directory with inode # '*numb';
- 24944 * if (flag == DELETE) delete 'string' from the directory;
- 24945 * if (flag == LOOK_UP) search for 'string' and return inode # in 'numb';
- 24946 * if (flag == IS_EMPTY) return OK if only . and .. in dir else ENOTEMPTY;
- 24947 *
- 24948 * if 'string' is dot1 or dot2, no access permissions are checked.
- 24949 */
- 24950
- 24951 register struct direct *dp;
- 24952 register struct buf *bp;
- 24953 int i, r, e_hit, t, match;
- 24954 mode_t bits;
- 24955 off_t pos;
- 24956 unsigned new_slots, old_slots;
- 24957 block_t b;
- 24958 struct super_block *sp;
- 24959 int extended = 0;
- 24960
- 24961 /* If 'ldir_ptr' is not a pointer to a dir inode, error. */
- 24962 if ( (ldir_ptr->i_mode & I_TYPE) != I_DIRECTORY) return(ENOTDIR);
- 24963
- 24964 r = OK;
- 24965
- 24966 if (flag != IS_EMPTY) {
- 24967 bits = (flag == LOOK_UP ? X_BIT : W_BIT | X_BIT);
- 24968
- 24969 if (string == dot1 || string == dot2) {
- .Ep 344 src/fs/path.c
- 24970 if (flag != LOOK_UP) r = read_only(ldir_ptr);
- 24971 /* only a writable device is required. */
- 24972 }
- 24973 else r = forbidden(ldir_ptr, bits); /* check access permissions */
- 24974 }
- 24975 if (r != OK) return(r);
- 24976
- 24977 /* Step through the directory one block at a time. */
- 24978 old_slots = (unsigned) (ldir_ptr->i_size/DIR_ENTRY_SIZE);
- 24979 new_slots = 0;
- 24980 e_hit = FALSE;
- 24981 match = 0; /* set when a string match occurs */
- 24982
- 24983 for (pos = 0; pos < ldir_ptr->i_size; pos += BLOCK_SIZE) {
- 24984 b = read_map(ldir_ptr, pos); /* get block number */
- 24985
- 24986 /* Since directories don't have holes, 'b' cannot be NO_BLOCK. */
- 24987 bp = get_block(ldir_ptr->i_dev, b, NORMAL); /* get a dir block */
- 24988
- 24989 /* Search a directory block. */
- 24990 for (dp = &bp->b_dir[0]; dp < &bp->b_dir[NR_DIR_ENTRIES]; dp++) {
- 24991 if (++new_slots > old_slots) { /* not found, but room left */
- 24992 if (flag == ENTER) e_hit = TRUE;
- 24993 break;
- 24994 }
- 24995
- 24996 /* Match occurs if string found. */
- 24997 if (flag != ENTER && dp->d_ino != 0) {
- 24998 if (flag == IS_EMPTY) {
- 24999 /* If this test succeeds, dir is not empty. */
- 25000 if (strcmp(dp->d_name, "." ) != 0 &&
- 25001 strcmp(dp->d_name, "..") != 0) match = 1;
- 25002 } else {
- 25003 if (strncmp(dp->d_name, string, NAME_MAX) == 0)
- 25004 match = 1;
- 25005 }
- 25006 }
- 25007
- 25008 if (match) {
- 25009 /* LOOK_UP or DELETE found what it wanted. */
- 25010 r = OK;
- 25011 if (flag == IS_EMPTY) r = ENOTEMPTY;
- 25012 else if (flag == DELETE) {
- 25013 /* Save d_ino for recovery. */
- 25014 t = NAME_MAX - sizeof(ino_t);
- 25015 *((ino_t *) &dp->d_name[t]) = dp->d_ino;
- 25016 dp->d_ino = 0; /* erase entry */
- 25017 bp->b_dirt = DIRTY;
- 25018 ldir_ptr->i_update |= CTIME | MTIME;
- 25019 ldir_ptr->i_dirt = DIRTY;
- 25020 } else {
- 25021 sp = ldir_ptr->i_sp; /* 'flag' is LOOK_UP */
- 25022 *numb = conv2(sp->s_native, (int) dp->d_ino);
- 25023 }
- 25024 put_block(bp, DIRECTORY_BLOCK);
- 25025 return(r);
- 25026 }
- 25027
- 25028
- 25029 /* Check for free slot for the benefit of ENTER. */
- .Op 345 src/fs/path.c
- 25030 if (flag == ENTER && dp->d_ino == 0) {
- 25031 e_hit = TRUE; /* we found a free slot */
- 25032 break;
- 25033 }
- 25034 }
- 25035
- 25036 /* The whole block has been searched or ENTER has a free slot. */
- 25037 if (e_hit) break; /* e_hit set if ENTER can be performed now */
- 25038 put_block(bp, DIRECTORY_BLOCK); /* otherwise, continue searching dir */
- 25039 }
- 25040
- 25041 /* The whole directory has now been searched. */
- 25042 if (flag != ENTER) return(flag == IS_EMPTY ? OK : ENOENT);
- 25043
- 25044 /* This call is for ENTER. If no free slot has been found so far, try to
- 25045 * extend directory.
- 25046 */
- 25047 if (e_hit == FALSE) { /* directory is full and no room left in last block */
- 25048 new_slots++; /* increase directory size by 1 entry */
- 25049 if (new_slots == 0) return(EFBIG); /* dir size limited by slot count */
- 25050 if ( (bp = new_block(ldir_ptr, ldir_ptr->i_size)) == NIL_BUF)
- 25051 return(err_code);
- 25052 dp = &bp->b_dir[0];
- 25053 extended = 1;
- 25054 }
- 25055
- 25056 /* 'bp' now points to a directory block with space. 'dp' points to slot. */
- 25057 (void) memset(dp->d_name, 0, (size_t) NAME_MAX); /* clear entry */
- 25058 for (i = 0; string[i] && i < NAME_MAX; i++) dp->d_name[i] = string[i];
- 25059 sp = ldir_ptr->i_sp;
- 25060 dp->d_ino = conv2(sp->s_native, (int) *numb);
- 25061 bp->b_dirt = DIRTY;
- 25062 put_block(bp, DIRECTORY_BLOCK);
- 25063 ldir_ptr->i_update |= CTIME | MTIME; /* mark mtime for update later */
- 25064 ldir_ptr->i_dirt = DIRTY;
- 25065 if (new_slots > old_slots) {
- 25066 ldir_ptr->i_size = (off_t) new_slots * DIR_ENTRY_SIZE;
- 25067 /* Send the change to disk if the directory is extended. */
- 25068 if (extended) rw_inode(ldir_ptr, WRITING);
- 25069 }
- 25070 return(OK);
- 25071 }
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- src/fs/mount.c
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- 25100 /* This file performs the MOUNT and UMOUNT system calls.
- 25101 *
- 25102 * The entry points into this file are
- 25103 * do_mount: perform the MOUNT system call
- 25104 * do_umount: perform the UMOUNT system call
- 25105 */
- 25106
- 25107 #include "fs.h"
- 25108 #include <fcntl.h>
- 25109 #include <minix/com.h>
- .Ep 346 src/fs/mount.c
- 25110 #include <sys/stat.h>
- 25111 #include "buf.h"
- 25112 #include "dev.h"
- 25113 #include "file.h"
- 25114 #include "fproc.h"
- 25115 #include "inode.h"
- 25116 #include "param.h"
- 25117 #include "super.h"
- 25118
- 25119 PRIVATE message dev_mess;
- 25120
- 25121 FORWARD _PROTOTYPE( dev_t name_to_dev, (char *path) );
- 25122
- 25123 /*===========================================================================*
- 25124 * do_mount *
- 25125 *===========================================================================*/
- 25126 PUBLIC int do_mount()
- 25127 {
- 25128 /* Perform the mount(name, mfile, rd_only) system call. */
- 25129
- 25130 register struct inode *rip, *root_ip;
- 25131 struct super_block *xp, *sp;
- 25132 dev_t dev;
- 25133 mode_t bits;
- 25134 int rdir, mdir; /* TRUE iff {root|mount} file is dir */
- 25135 int r, found, major, task;
- 25136
- 25137 /* Only the super-user may do MOUNT. */
- 25138 if (!super_user) return(EPERM);
- 25139
- 25140 /* If 'name' is not for a block special file, return error. */
- 25141 if (fetch_name(name1, name1_length, M1) != OK) return(err_code);
- 25142 if ( (dev = name_to_dev(user_path)) == NO_DEV) return(err_code);
- 25143
- 25144 /* Scan super block table to see if dev already mounted & find a free slot.*/
- 25145 sp = NIL_SUPER;
- 25146 found = FALSE;
- 25147 for (xp = &super_block[0]; xp < &super_block[NR_SUPERS]; xp++) {
- 25148 if (xp->s_dev == dev) found = TRUE; /* is it mounted already? */
- 25149 if (xp->s_dev == NO_DEV) sp = xp; /* record free slot */
- 25150 }
- 25151 if (found) return(EBUSY); /* already mounted */
- 25152 if (sp == NIL_SUPER) return(ENFILE); /* no super block available */
- 25153
- 25154 dev_mess.m_type = DEV_OPEN; /* distinguish from close */
- 25155 dev_mess.DEVICE = dev; /* Touch the device. */
- 25156 if (rd_only) dev_mess.COUNT = R_BIT;
- 25157 else dev_mess.COUNT = R_BIT|W_BIT;
- 25158
- 25159 major = (dev >> MAJOR) & BYTE;
- 25160 if (major <= 0 || major >= max_major) return(ENODEV);
- 25161 task = dmap[major].dmap_task; /* device task nr */
- 25162 (*dmap[major].dmap_open)(task, &dev_mess);
- 25163 if (dev_mess.REP_STATUS != OK) return(EINVAL);
- 25164
- 25165 /* Fill in the super block. */
- 25166 sp->s_dev = dev; /* read_super() needs to know which dev */
- 25167 r = read_super(sp);
- 25168
- 25169 /* Is it recognized as a Minix filesystem? */
- .Op 347 src/fs/mount.c
- 25170 if (r != OK) {
- 25171 dev_mess.m_type = DEV_CLOSE;
- 25172 dev_mess.DEVICE = dev;
- 25173 (*dmap[major].dmap_close)(task, &dev_mess);
- 25174 return(r);
- 25175 }
- 25176
- 25177 /* Now get the inode of the file to be mounted on. */
- 25178 if (fetch_name(name2, name2_length, M1) != OK) {
- 25179 sp->s_dev = NO_DEV;
- 25180 dev_mess.m_type = DEV_CLOSE;
- 25181 dev_mess.DEVICE = dev;
- 25182 (*dmap[major].dmap_close)(task, &dev_mess);
- 25183 return(err_code);
- 25184 }
- 25185 if ( (rip = eat_path(user_path)) == NIL_INODE) {
- 25186 sp->s_dev = NO_DEV;
- 25187 dev_mess.m_type = DEV_CLOSE;
- 25188 dev_mess.DEVICE = dev;
- 25189 (*dmap[major].dmap_close)(task, &dev_mess);
- 25190 return(err_code);
- 25191 }
- 25192
- 25193 /* It may not be busy. */
- 25194 r = OK;
- 25195 if (rip->i_count > 1) r = EBUSY;
- 25196
- 25197 /* It may not be special. */
- 25198 bits = rip->i_mode & I_TYPE;
- 25199 if (bits == I_BLOCK_SPECIAL || bits == I_CHAR_SPECIAL) r = ENOTDIR;
- 25200
- 25201 /* Get the root inode of the mounted file system. */
- 25202 root_ip = NIL_INODE; /* if 'r' not OK, make sure this is defined */
- 25203 if (r == OK) {
- 25204 if ( (root_ip = get_inode(dev, ROOT_INODE)) == NIL_INODE) r = err_code;
- 25205 }
- 25206 if (root_ip != NIL_INODE && root_ip->i_mode == 0) r = EINVAL;
- 25207
- 25208 /* File types of 'rip' and 'root_ip' may not conflict. */
- 25209 if (r == OK) {
- 25210 mdir = ((rip->i_mode & I_TYPE) == I_DIRECTORY); /* TRUE iff dir */
- 25211 rdir = ((root_ip->i_mode & I_TYPE) == I_DIRECTORY);
- 25212 if (!mdir && rdir) r = EISDIR;
- 25213 }
- 25214
- 25215 /* If error, return the super block and both inodes; release the maps. */
- 25216 if (r != OK) {
- 25217 put_inode(rip);
- 25218 put_inode(root_ip);
- 25219 (void) do_sync();
- 25220 invalidate(dev);
- 25221
- 25222 sp->s_dev = NO_DEV;
- 25223 dev_mess.m_type = DEV_CLOSE;
- 25224 dev_mess.DEVICE = dev;
- 25225 (*dmap[major].dmap_close)(task, &dev_mess);
- 25226 return(r);
- 25227 }
- 25228
- 25229 /* Nothing else can go wrong. Perform the mount. */
- .Ep 348 src/fs/mount.c
- 25230 rip->i_mount = I_MOUNT; /* this bit says the inode is mounted on */
- 25231 sp->s_imount = rip;
- 25232 sp->s_isup = root_ip;
- 25233 sp->s_rd_only = rd_only;
- 25234 return(OK);
- 25235 }
-
-
- 25238 /*===========================================================================*
- 25239 * do_umount *
- 25240 *===========================================================================*/
- 25241 PUBLIC int do_umount()
- 25242 {
- 25243 /* Perform the umount(name) system call. */
- 25244
- 25245 register struct inode *rip;
- 25246 struct super_block *sp, *sp1;
- 25247 dev_t dev;
- 25248 int count;
- 25249 int major, task;
- 25250
- 25251 /* Only the super-user may do UMOUNT. */
- 25252 if (!super_user) return(EPERM);
- 25253
- 25254 /* If 'name' is not for a block special file, return error. */
- 25255 if (fetch_name(name, name_length, M3) != OK) return(err_code);
- 25256 if ( (dev = name_to_dev(user_path)) == NO_DEV) return(err_code);
- 25257
- 25258 /* See if the mounted device is busy. Only 1 inode using it should be
- 25259 * open -- the root inode -- and that inode only 1 time.
- 25260 */
- 25261 count = 0;
- 25262 for (rip = &inode[0]; rip< &inode[NR_INODES]; rip++)
- 25263 if (rip->i_count > 0 && rip->i_dev == dev) count += rip->i_count;
- 25264 if (count > 1) return(EBUSY); /* can't umount a busy file system */
- 25265
- 25266 /* Find the super block. */
- 25267 sp = NIL_SUPER;
- 25268 for (sp1 = &super_block[0]; sp1 < &super_block[NR_SUPERS]; sp1++) {
- 25269 if (sp1->s_dev == dev) {
- 25270 sp = sp1;
- 25271 break;
- 25272 }
- 25273 }
- 25274
- 25275 /* Sync the disk, and invalidate cache. */
- 25276 (void) do_sync(); /* force any cached blocks out of memory */
- 25277 invalidate(dev); /* invalidate cache entries for this dev */
- 25278 if (sp == NIL_SUPER) return(EINVAL);
- 25279
- 25280 major = (dev >> MAJOR) & BYTE; /* major device nr */
- 25281 task = dmap[major].dmap_task; /* device task nr */
- 25282 dev_mess.m_type = DEV_CLOSE; /* distinguish from open */
- 25283 dev_mess.DEVICE = dev;
- 25284 (*dmap[major].dmap_close)(task, &dev_mess);
- 25285
- 25286 /* Finish off the unmount. */
- 25287 sp->s_imount->i_mount = NO_MOUNT; /* inode returns to normal */
- 25288 put_inode(sp->s_imount); /* release the inode mounted on */
- 25289 put_inode(sp->s_isup); /* release the root inode of the mounted fs */
- .Op 349 src/fs/mount.c
- 25290 sp->s_imount = NIL_INODE;
- 25291 sp->s_dev = NO_DEV;
- 25292 return(OK);
- 25293 }
-
-
- 25296 /*===========================================================================*
- 25297 * name_to_dev *
- 25298 *===========================================================================*/
- 25299 PRIVATE dev_t name_to_dev(path)
- 25300 char *path; /* pointer to path name */
- 25301 {
- 25302 /* Convert the block special file 'path' to a device number. If 'path'
- 25303 * is not a block special file, return error code in 'err_code'.
- 25304 */
- 25305
- 25306 register struct inode *rip;
- 25307 register dev_t dev;
- 25308
- 25309 /* If 'path' can't be opened, give up immediately. */
- 25310 if ( (rip = eat_path(path)) == NIL_INODE) return(NO_DEV);
- 25311
- 25312 /* If 'path' is not a block special file, return error. */
- 25313 if ( (rip->i_mode & I_TYPE) != I_BLOCK_SPECIAL) {
- 25314 err_code = ENOTBLK;
- 25315 put_inode(rip);
- 25316 return(NO_DEV);
- 25317 }
- 25318
- 25319 /* Extract the device number. */
- 25320 dev = (dev_t) rip->i_zone[0];
- 25321 put_inode(rip);
- 25322 return(dev);
- 25323 }
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- src/fs/link.c
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- 25400 /* This file handles the LINK and UNLINK system calls. It also deals with
- 25401 * deallocating the storage used by a file when the last UNLINK is done to a
- 25402 * file and the blocks must be returned to the free block pool.
- 25403 *
- 25404 * The entry points into this file are
- 25405 * do_link: perform the LINK system call
- 25406 * do_unlink: perform the UNLINK and RMDIR system calls
- 25407 * do_rename: perform the RENAME system call
- 25408 * truncate: release all the blocks associated with an inode
- 25409 */
- 25410
- 25411 #include "fs.h"
- 25412 #include <sys/stat.h>
- 25413 #include <string.h>
- 25414 #include <minix/callnr.h>
- 25415 #include "buf.h"
- 25416 #include "file.h"
- 25417 #include "fproc.h"
- 25418 #include "inode.h"
- 25419 #include "param.h"
- .Ep 350 src/fs/link.c
- 25420 #include "super.h"
- 25421
- 25422 #define SAME 1000
- 25423
- 25424 FORWARD _PROTOTYPE( int remove_dir, (struct inode *rldirp, struct inode *rip,
- 25425 char dir_name[NAME_MAX]) );
- 25426
- 25427 FORWARD _PROTOTYPE( int unlink_file, (struct inode *dirp, struct inode *rip,
- 25428 char file_name[NAME_MAX]) );
- 25429
- 25430
- 25431 /*===========================================================================*
- 25432 * do_link *
- 25433 *===========================================================================*/
- 25434 PUBLIC int do_link()
- 25435 {
- 25436 /* Perform the link(name1, name2) system call. */
- 25437
- 25438 register struct inode *ip, *rip;
- 25439 register int r;
- 25440 char string[NAME_MAX];
- 25441 struct inode *new_ip;
- 25442
- 25443 /* See if 'name' (file to be linked) exists. */
- 25444 if (fetch_name(name1, name1_length, M1) != OK) return(err_code);
- 25445 if ( (rip = eat_path(user_path)) == NIL_INODE) return(err_code);
- 25446
- 25447 /* Check to see if the file has maximum number of links already. */
- 25448 r = OK;
- 25449 if ( (rip->i_nlinks & BYTE) >= LINK_MAX) r = EMLINK;
- 25450
- 25451 /* Only super_user may link to directories. */
- 25452 if (r == OK)
- 25453 if ( (rip->i_mode & I_TYPE) == I_DIRECTORY && !super_user) r = EPERM;
- 25454
- 25455 /* If error with 'name', return the inode. */
- 25456 if (r != OK) {
- 25457 put_inode(rip);
- 25458 return(r);
- 25459 }
- 25460
- 25461 /* Does the final directory of 'name2' exist? */
- 25462 if (fetch_name(name2, name2_length, M1) != OK) {
- 25463 put_inode(rip);
- 25464 return(err_code);
- 25465 }
- 25466 if ( (ip = last_dir(user_path, string)) == NIL_INODE) r = err_code;
- 25467
- 25468 /* If 'name2' exists in full (even if no space) set 'r' to error. */
- 25469 if (r == OK) {
- 25470 if ( (new_ip = advance(ip, string)) == NIL_INODE) {
- 25471 r = err_code;
- 25472 if (r == ENOENT) r = OK;
- 25473 } else {
- 25474 put_inode(new_ip);
- 25475 r = EEXIST;
- 25476 }
- 25477 }
- 25478
- 25479 /* Check for links across devices. */
- .Op 351 src/fs/link.c
- 25480 if (r == OK)
- 25481 if (rip->i_dev != ip->i_dev) r = EXDEV;
- 25482
- 25483 /* Try to link. */
- 25484 if (r == OK)
- 25485 r = search_dir(ip, string, &rip->i_num, ENTER);
- 25486
- 25487 /* If success, register the linking. */
- 25488 if (r == OK) {
- 25489 rip->i_nlinks++;
- 25490 rip->i_update |= CTIME;
- 25491 rip->i_dirt = DIRTY;
- 25492 }
- 25493
- 25494 /* Done. Release both inodes. */
- 25495 put_inode(rip);
- 25496 put_inode(ip);
- 25497 return(r);
- 25498 }
-
-
- 25501 /*===========================================================================*
- 25502 * do_unlink *
- 25503 *===========================================================================*/
- 25504 PUBLIC int do_unlink()
- 25505 {
- 25506 /* Perform the unlink(name) or rmdir(name) system call. The code for these two
- 25507 * is almost the same. They differ only in some condition testing. Unlink()
- 25508 * may be used by the superuser to do dangerous things; rmdir() may not.
- 25509 */
- 25510
- 25511 register struct inode *rip;
- 25512 struct inode *rldirp;
- 25513 int r;
- 25514 char string[NAME_MAX];
- 25515
- 25516 /* Get the last directory in the path. */
- 25517 if (fetch_name(name, name_length, M3) != OK) return(err_code);
- 25518 if ( (rldirp = last_dir(user_path, string)) == NIL_INODE)
- 25519 return(err_code);
- 25520
- 25521 /* The last directory exists. Does the file also exist? */
- 25522 r = OK;
- 25523 if ( (rip = advance(rldirp, string)) == NIL_INODE) r = err_code;
- 25524
- 25525 /* If error, return inode. */
- 25526 if (r != OK) {
- 25527 put_inode(rldirp);
- 25528 return(r);
- 25529 }
- 25530
- 25531 /* Do not remove a mount point. */
- 25532 if (rip->i_num == ROOT_INODE) {
- 25533 put_inode(rldirp);
- 25534 put_inode(rip);
- 25535 return(EBUSY);
- 25536 }
- 25537
- 25538 /* Now test if the call is allowed, separately for unlink() and rmdir(). */
- 25539 if (fs_call == UNLINK) {
- .Ep 352 src/fs/link.c
- 25540 /* Only the su may unlink directories, but the su can unlink any dir.*/
- 25541 if ( (rip->i_mode & I_TYPE) == I_DIRECTORY && !super_user) r = EPERM;
- 25542
- 25543 /* Don't unlink a file if it is the root of a mounted file system. */
- 25544 if (rip->i_num == ROOT_INODE) r = EBUSY;
- 25545
- 25546 /* Actually try to unlink the file; fails if parent is mode 0 etc. */
- 25547 if (r == OK) r = unlink_file(rldirp, rip, string);
- 25548
- 25549 } else {
- 25550 r = remove_dir(rldirp, rip, string); /* call is RMDIR */
- 25551 }
- 25552
- 25553 /* If unlink was possible, it has been done, otherwise it has not. */
- 25554 put_inode(rip);
- 25555 put_inode(rldirp);
- 25556 return(r);
- 25557 }
-
-
- 25560 /*===========================================================================*
- 25561 * do_rename *
- 25562 *===========================================================================*/
- 25563 PUBLIC int do_rename()
- 25564 {
- 25565 /* Perform the rename(name1, name2) system call. */
- 25566
- 25567 struct inode *old_dirp, *old_ip; /* ptrs to old dir, file inodes */
- 25568 struct inode *new_dirp, *new_ip; /* ptrs to new dir, file inodes */
- 25569 struct inode *new_superdirp, *next_new_superdirp;
- 25570 int r = OK; /* error flag; initially no error */
- 25571 int odir, ndir; /* TRUE iff {old|new} file is dir */
- 25572 int same_pdir; /* TRUE iff parent dirs are the same */
- 25573 char old_name[NAME_MAX], new_name[NAME_MAX];
- 25574 ino_t numb;
- 25575 int r1;
- 25576
- 25577 /* See if 'name1' (existing file) exists. Get dir and file inodes. */
- 25578 if (fetch_name(name1, name1_length, M1) != OK) return(err_code);
- 25579 if ( (old_dirp = last_dir(user_path, old_name))==NIL_INODE) return(err_code);
- 25580
- 25581 if ( (old_ip = advance(old_dirp, old_name)) == NIL_INODE) r = err_code;
- 25582
- 25583 /* See if 'name2' (new name) exists. Get dir and file inodes. */
- 25584 if (fetch_name(name2, name2_length, M1) != OK) r = err_code;
- 25585 if ( (new_dirp = last_dir(user_path, new_name)) == NIL_INODE) r = err_code;
- 25586 new_ip = advance(new_dirp, new_name); /* not required to exist */
- 25587
- 25588 if (old_ip != NIL_INODE)
- 25589 odir = ((old_ip->i_mode & I_TYPE) == I_DIRECTORY); /* TRUE iff dir */
- 25590
- 25591 /* If it is ok, check for a variety of possible errors. */
- 25592 if (r == OK) {
- 25593 same_pdir = (old_dirp == new_dirp);
- 25594
- 25595 /* The old inode must not be a superdirectory of the new last dir. */
- 25596 if (odir && !same_pdir) {
- 25597 dup_inode(new_superdirp = new_dirp);
- 25598 while (TRUE) { /* may hang in a file system loop */
- 25599 if (new_superdirp == old_ip) {
- .Op 353 src/fs/link.c
- 25600 r = EINVAL;
- 25601 break;
- 25602 }
- 25603 next_new_superdirp = advance(new_superdirp, dot2);
- 25604 put_inode(new_superdirp);
- 25605 if (next_new_superdirp == new_superdirp)
- 25606 break; /* back at system root directory */
- 25607 new_superdirp = next_new_superdirp;
- 25608 if (new_superdirp == NIL_INODE) {
- 25609 /* Missing ".." entry. Assume the worst. */
- 25610 r = EINVAL;
- 25611 break;
- 25612 }
- 25613 }
- 25614 put_inode(new_superdirp);
- 25615 }
- 25616
- 25617 /* The old or new name must not be . or .. */
- 25618 if (strcmp(old_name, ".")==0 || strcmp(old_name, "..")==0 ||
- 25619 strcmp(new_name, ".")==0 || strcmp(new_name, "..")==0) r = EINVAL;
- 25620
- 25621 /* Both parent directories must be on the same device. */
- 25622 if (old_dirp->i_dev != new_dirp->i_dev) r = EXDEV;
- 25623
- 25624 /* Parent dirs must be writable, searchable and on a writable device */
- 25625 if ((r1 = forbidden(old_dirp, W_BIT | X_BIT)) != OK ||
- 25626 (r1 = forbidden(new_dirp, W_BIT | X_BIT)) != OK) r = r1;
- 25627
- 25628 /* Some tests apply only if the new path exists. */
- 25629 if (new_ip == NIL_INODE) {
- 25630 /* don't rename a file with a file system mounted on it. */
- 25631 if (old_ip->i_dev != old_dirp->i_dev) r = EXDEV;
- 25632 if (odir && (new_dirp->i_nlinks & BYTE) >= LINK_MAX &&
- 25633 !same_pdir && r == OK) r = EMLINK;
- 25634 } else {
- 25635 if (old_ip == new_ip) r = SAME; /* old=new */
- 25636
- 25637 /* has the old file or new file a file system mounted on it? */
- 25638 if (old_ip->i_dev != new_ip->i_dev) r = EXDEV;
- 25639
- 25640 ndir = ((new_ip->i_mode & I_TYPE) == I_DIRECTORY); /* dir ? */
- 25641 if (odir == TRUE && ndir == FALSE) r = ENOTDIR;
- 25642 if (odir == FALSE && ndir == TRUE) r = EISDIR;
- 25643 }
- 25644 }
- 25645
- 25646 /* If a process has another root directory than the system root, we might
- 25647 * "accidently" be moving it's working directory to a place where it's
- 25648 * root directory isn't a super directory of it anymore. This can make
- 25649 * the function chroot useless. If chroot will be used often we should
- 25650 * probably check for it here.
- 25651 */
- 25652
- 25653 /* The rename will probably work. Only two things can go wrong now:
- 25654 * 1. being unable to remove the new file. (when new file already exists)
- 25655 * 2. being unable to make the new directory entry. (new file doesn't exists)
- 25656 * [directory has to grow by one block and cannot because the disk
- 25657 * is completely full].
- 25658 */
- 25659 if (r == OK) {
- .Ep 354 src/fs/link.c
- 25660 if (new_ip != NIL_INODE) {
- 25661 /* There is already an entry for 'new'. Try to remove it. */
- 25662 if (odir)
- 25663 r = remove_dir(new_dirp, new_ip, new_name);
- 25664 else
- 25665 r = unlink_file(new_dirp, new_ip, new_name);
- 25666 }
- 25667 /* if r is OK, the rename will succeed, while there is now an
- 25668 * unused entry in the new parent directory.
- 25669 */
- 25670 }
- 25671
- 25672 if (r == OK) {
- 25673 /* If the new name will be in the same parent directory as the old one,
- 25674 * first remove the old name to free an entry for the new name,
- 25675 * otherwise first try to create the new name entry to make sure
- 25676 * the rename will succeed.
- 25677 */
- 25678 numb = old_ip->i_num; /* inode number of old file */
- 25679
- 25680 if (same_pdir) {
- 25681 r = search_dir(old_dirp, old_name, (ino_t *) 0, DELETE);
- 25682 /* shouldn't go wrong. */
- 25683 if (r==OK) (void) search_dir(old_dirp, new_name, &numb, ENTER);
- 25684 } else {
- 25685 r = search_dir(new_dirp, new_name, &numb, ENTER);
- 25686 if (r == OK)
- 25687 (void) search_dir(old_dirp, old_name, (ino_t *) 0, DELETE);
- 25688 }
- 25689 }
- 25690 /* If r is OK, the ctime and mtime of old_dirp and new_dirp have been marked
- 25691 * for update in search_dir.
- 25692 */
- 25693
- 25694 if (r == OK && odir && !same_pdir) {
- 25695 /* Update the .. entry in the directory (still points to old_dirp). */
- 25696 numb = new_dirp->i_num;
- 25697 (void) unlink_file(old_ip, NIL_INODE, dot2);
- 25698 if (search_dir(old_ip, dot2, &numb, ENTER) == OK) {
- 25699 /* New link created. */
- 25700 new_dirp->i_nlinks++;
- 25701 new_dirp->i_dirt = DIRTY;
- 25702 }
- 25703 }
- 25704
- 25705 /* Release the inodes. */
- 25706 put_inode(old_dirp);
- 25707 put_inode(old_ip);
- 25708 put_inode(new_dirp);
- 25709 put_inode(new_ip);
- 25710 return(r == SAME ? OK : r);
- 25711 }
-
-
- 25714 /*===========================================================================*
- 25715 * truncate *
- 25716 *===========================================================================*/
- 25717 PUBLIC void truncate(rip)
- 25718 register struct inode *rip; /* pointer to inode to be truncated */
- 25719 {
- .Op 355 src/fs/link.c
- 25720 /* Remove all the zones from the inode 'rip' and mark it dirty. */
- 25721
- 25722 register block_t b;
- 25723 zone_t z, zone_size, z1;
- 25724 off_t position;
- 25725 int i, scale, file_type, waspipe, single, nr_indirects;
- 25726 struct buf *bp;
- 25727 dev_t dev;
- 25728
- 25729 file_type = rip->i_mode & I_TYPE; /* check to see if file is special */
- 25730 if (file_type == I_CHAR_SPECIAL || file_type == I_BLOCK_SPECIAL) return;
- 25731 dev = rip->i_dev; /* device on which inode resides */
- 25732 scale = rip->i_sp->s_log_zone_size;
- 25733 zone_size = (zone_t) BLOCK_SIZE << scale;
- 25734 nr_indirects = rip->i_nindirs;
- 25735
- 25736 /* Pipes can shrink, so adjust size to make sure all zones are removed. */
- 25737 waspipe = rip->i_pipe == I_PIPE; /* TRUE is this was a pipe */
- 25738 if (waspipe) rip->i_size = PIPE_SIZE;
- 25739
- 25740 /* Step through the file a zone at a time, finding and freeing the zones. */
- 25741 for (position = 0; position < rip->i_size; position += zone_size) {
- 25742 if ( (b = read_map(rip, position)) != NO_BLOCK) {
- 25743 z = (zone_t) b >> scale;
- 25744 free_zone(dev, z);
- 25745 }
- 25746 }
- 25747
- 25748 /* All the data zones have been freed. Now free the indirect zones. */
- 25749 rip->i_dirt = DIRTY;
- 25750 if (waspipe) {
- 25751 wipe_inode(rip); /* clear out inode for pipes */
- 25752 return; /* indirect slots contain file positions */
- 25753 }
- 25754 single = rip->i_ndzones;
- 25755 free_zone(dev, rip->i_zone[single]); /* single indirect zone */
- 25756 if ( (z = rip->i_zone[single+1]) != NO_ZONE) {
- 25757 /* Free all the single indirect zones pointed to by the double. */
- 25758 b = (block_t) z << scale;
- 25759 bp = get_block(dev, b, NORMAL); /* get double indirect zone */
- 25760 for (i = 0; i < nr_indirects; i++) {
- 25761 z1 = rd_indir(bp, i);
- 25762 free_zone(dev, z1);
- 25763 }
- 25764
- 25765 /* Now free the double indirect zone itself. */
- 25766 put_block(bp, INDIRECT_BLOCK);
- 25767 free_zone(dev, z);
- 25768 }
- 25769
- 25770 /* Leave zone numbers for de(1) to recover file after an unlink(2). */
- 25771 }
-
-
- 25774 /*===========================================================================*
- 25775 * remove_dir *
- 25776 *===========================================================================*/
- 25777 PRIVATE int remove_dir(rldirp, rip, dir_name)
- 25778 struct inode *rldirp; /* parent directory */
- 25779 struct inode *rip; /* directory to be removed */
- .Ep 356 src/fs/link.c
- 25780 char dir_name[NAME_MAX]; /* name of directory to be removed */
- 25781 {
- 25782 /* A directory file has to be removed. Five conditions have to met:
- 25783 * - The file must be a directory
- 25784 * - The directory must be empty (except for . and ..)
- 25785 * - The final component of the path must not be . or ..
- 25786 * - The directory must not be the root of a mounted file system
- 25787 * - The directory must not be anybody's root/working directory
- 25788 */
- 25789
- 25790 int r;
- 25791 register struct fproc *rfp;
- 25792
- 25793 /* search_dir checks that rip is a directory too. */
- 25794 if ((r = search_dir(rip, "", (ino_t *) 0, IS_EMPTY)) != OK) return r;
- 25795
- 25796 if (strcmp(dir_name, ".") == 0 || strcmp(dir_name, "..") == 0)return(EINVAL);
- 25797 if (rip->i_num == ROOT_INODE) return(EBUSY); /* can't remove 'root' */
- 25798
- 25799 for (rfp = &fproc[INIT_PROC_NR + 1]; rfp < &fproc[NR_PROCS]; rfp++)
- 25800 if (rfp->fp_workdir == rip || rfp->fp_rootdir == rip) return(EBUSY);
- 25801 /* can't remove anybody's working dir */
- 25802
- 25803 /* Actually try to unlink the file; fails if parent is mode 0 etc. */
- 25804 if ((r = unlink_file(rldirp, rip, dir_name)) != OK) return r;
- 25805
- 25806 /* Unlink . and .. from the dir. The super user can link and unlink any dir,
- 25807 * so don't make too many assumptions about them.
- 25808 */
- 25809 (void) unlink_file(rip, NIL_INODE, dot1);
- 25810 (void) unlink_file(rip, NIL_INODE, dot2);
- 25811 return(OK);
- 25812 }
-
-
- 25815 /*===========================================================================*
- 25816 * unlink_file *
- 25817 *===========================================================================*/
- 25818 PRIVATE int unlink_file(dirp, rip, file_name)
- 25819 struct inode *dirp; /* parent directory of file */
- 25820 struct inode *rip; /* inode of file, may be NIL_INODE too. */
- 25821 char file_name[NAME_MAX]; /* name of file to be removed */
- 25822 {
- 25823 /* Unlink 'file_name'; rip must be the inode of 'file_name' or NIL_INODE. */
- 25824
- 25825 ino_t numb; /* inode number */
- 25826 int r;
- 25827
- 25828 /* If rip is not NIL_INODE, it is used to get faster access to the inode. */
- 25829 if (rip == NIL_INODE) {
- 25830 /* Search for file in directory and try to get its inode. */
- 25831 err_code = search_dir(dirp, file_name, &numb, LOOK_UP);
- 25832 if (err_code == OK) rip = get_inode(dirp->i_dev, (int) numb);
- 25833 if (err_code != OK || rip == NIL_INODE) return(err_code);
- 25834 } else {
- 25835 dup_inode(rip); /* inode will be returned with put_inode */
- 25836 }
- 25837
- 25838 r = search_dir(dirp, file_name, (ino_t *) 0, DELETE);
- 25839
- .Op 357 src/fs/link.c
- 25840 if (r == OK) {
- 25841 rip->i_nlinks--; /* entry deleted from parent's dir */
- 25842 rip->i_update |= CTIME;
- 25843 rip->i_dirt = DIRTY;
- 25844 }
- 25845
- 25846 put_inode(rip);
- 25847 return(r);
- 25848 }
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- src/fs/stadir.c
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- 25900 /* This file contains the code for performing four system calls relating to
- 25901 * status and directories.
- 25902 *
- 25903 * The entry points into this file are
- 25904 * do_chdir: perform the CHDIR system call
- 25905 * do_chroot: perform the CHROOT system call
- 25906 * do_stat: perform the STAT system call
- 25907 * do_fstat: perform the FSTAT system call
- 25908 */
- 25909
- 25910 #include "fs.h"
- 25911 #include <sys/stat.h>
- 25912 #include "file.h"
- 25913 #include "fproc.h"
- 25914 #include "inode.h"
- 25915 #include "param.h"
- 25916
- 25917 FORWARD _PROTOTYPE( int change, (struct inode **iip, char *name_ptr, int len));
- 25918 FORWARD _PROTOTYPE( int stat_inode, (struct inode *rip, struct filp *fil_ptr,
- 25919 char *user_addr) );
- 25920
- 25921 /*===========================================================================*
- 25922 * do_chdir *
- 25923 *===========================================================================*/
- 25924 PUBLIC int do_chdir()
- 25925 {
- 25926 /* Change directory. This function is also called by MM to simulate a chdir
- 25927 * in order to do EXEC, etc. It also changes the root directory, the uids and
- 25928 * gids, and the umask.
- 25929 */
- 25930
- 25931 int r;
- 25932 register struct fproc *rfp;
- 25933
- 25934 if (who == MM_PROC_NR) {
- 25935 rfp = &fproc[slot1];
- 25936 put_inode(fp->fp_rootdir);
- 25937 dup_inode(fp->fp_rootdir = rfp->fp_rootdir);
- 25938 put_inode(fp->fp_workdir);
- 25939 dup_inode(fp->fp_workdir = rfp->fp_workdir);
- 25940
- 25941 /* MM uses access() to check permissions. To make this work, pretend
- 25942 * that the user's real ids are the same as the user's effective ids.
- 25943 * FS calls other than access() do not use the real ids, so are not
- 25944 * affected.
- .Ep 358 src/fs/stadir.c
- 25945 */
- 25946 fp->fp_realuid =
- 25947 fp->fp_effuid = rfp->fp_effuid;
- 25948 fp->fp_realgid =
- 25949 fp->fp_effgid = rfp->fp_effgid;
- 25950 fp->fp_umask = rfp->fp_umask;
- 25951 return(OK);
- 25952 }
- 25953
- 25954 /* Perform the chdir(name) system call. */
- 25955 r = change(&fp->fp_workdir, name, name_length);
- 25956 return(r);
- 25957 }
-
-
- 25960 /*===========================================================================*
- 25961 * do_chroot *
- 25962 *===========================================================================*/
- 25963 PUBLIC int do_chroot()
- 25964 {
- 25965 /* Perform the chroot(name) system call. */
- 25966
- 25967 register int r;
- 25968
- 25969 if (!super_user) return(EPERM); /* only su may chroot() */
- 25970 r = change(&fp->fp_rootdir, name, name_length);
- 25971 return(r);
- 25972 }
-
-
- 25975 /*===========================================================================*
- 25976 * change *
- 25977 *===========================================================================*/
- 25978 PRIVATE int change(iip, name_ptr, len)
- 25979 struct inode **iip; /* pointer to the inode pointer for the dir */
- 25980 char *name_ptr; /* pointer to the directory name to change to */
- 25981 int len; /* length of the directory name string */
- 25982 {
- 25983 /* Do the actual work for chdir() and chroot(). */
- 25984
- 25985 struct inode *rip;
- 25986 register int r;
- 25987
- 25988 /* Try to open the new directory. */
- 25989 if (fetch_name(name_ptr, len, M3) != OK) return(err_code);
- 25990 if ( (rip = eat_path(user_path)) == NIL_INODE) return(err_code);
- 25991
- 25992 /* It must be a directory and also be searchable. */
- 25993 if ( (rip->i_mode & I_TYPE) != I_DIRECTORY)
- 25994 r = ENOTDIR;
- 25995 else
- 25996 r = forbidden(rip, X_BIT); /* check if dir is searchable */
- 25997
- 25998 /* If error, return inode. */
- 25999 if (r != OK) {
- 26000 put_inode(rip);
- 26001 return(r);
- 26002 }
- 26003
- 26004 /* Everything is OK. Make the change. */
- .Op 359 src/fs/stadir.c
- 26005 put_inode(*iip); /* release the old directory */
- 26006 *iip = rip; /* acquire the new one */
- 26007 return(OK);
- 26008 }
-
-
- 26011 /*===========================================================================*
- 26012 * do_stat *
- 26013 *===========================================================================*/
- 26014 PUBLIC int do_stat()
- 26015 {
- 26016 /* Perform the stat(name, buf) system call. */
- 26017
- 26018 register struct inode *rip;
- 26019 register int r;
- 26020
- 26021 /* Both stat() and fstat() use the same routine to do the real work. That
- 26022 * routine expects an inode, so acquire it temporarily.
- 26023 */
- 26024 if (fetch_name(name1, name1_length, M1) != OK) return(err_code);
- 26025 if ( (rip = eat_path(user_path)) == NIL_INODE) return(err_code);
- 26026 r = stat_inode(rip, NIL_FILP, name2); /* actually do the work.*/
- 26027 put_inode(rip); /* release the inode */
- 26028 return(r);
- 26029 }
-
-
- 26032 /*===========================================================================*
- 26033 * do_fstat *
- 26034 *===========================================================================*/
- 26035 PUBLIC int do_fstat()
- 26036 {
- 26037 /* Perform the fstat(fd, buf) system call. */
- 26038
- 26039 register struct filp *rfilp;
- 26040
- 26041 /* Is the file descriptor valid? */
- 26042 if ( (rfilp = get_filp(fd)) == NIL_FILP) return(err_code);
- 26043
- 26044 return(stat_inode(rfilp->filp_ino, rfilp, buffer));
- 26045 }
-
-
- 26048 /*===========================================================================*
- 26049 * stat_inode *
- 26050 *===========================================================================*/
- 26051 PRIVATE int stat_inode(rip, fil_ptr, user_addr)
- 26052 register struct inode *rip; /* pointer to inode to stat */
- 26053 struct filp *fil_ptr; /* filp pointer, supplied by 'fstat' */
- 26054 char *user_addr; /* user space address where stat buf goes */
- 26055 {
- 26056 /* Common code for stat and fstat system calls. */
- 26057
- 26058 struct stat statbuf;
- 26059 mode_t mo;
- 26060 int r, s;
- 26061
- 26062 /* Update the atime, ctime, and mtime fields in the inode, if need be. */
- 26063 if (rip->i_update) update_times(rip);
- 26064
- .Ep 360 src/fs/stadir.c
- 26065 /* Fill in the statbuf struct. */
- 26066 mo = rip->i_mode & I_TYPE;
- 26067 s = (mo == I_CHAR_SPECIAL || mo == I_BLOCK_SPECIAL); /* true iff special */
- 26068 statbuf.st_dev = rip->i_dev;
- 26069 statbuf.st_ino = rip->i_num;
- 26070 statbuf.st_mode = rip->i_mode;
- 26071 statbuf.st_nlink = rip->i_nlinks & BYTE;
- 26072 statbuf.st_uid = rip->i_uid;
- 26073 statbuf.st_gid = rip->i_gid & BYTE;
- 26074 statbuf.st_rdev = (dev_t) (s ? rip->i_zone[0] : NO_DEV);
- 26075 statbuf.st_size = rip->i_size;
- 26076
- 26077 if (rip->i_pipe == I_PIPE) {
- 26078 statbuf.st_mode &= ~I_REGULAR; /* wipe out I_REGULAR bit for pipes */
- 26079 if (fil_ptr != NIL_FILP && fil_ptr->filp_mode & R_BIT)
- 26080 statbuf.st_size -= fil_ptr->filp_pos;
- 26081 }
- 26082
- 26083 statbuf.st_atime = rip->i_atime;
- 26084 statbuf.st_mtime = rip->i_mtime;
- 26085 statbuf.st_ctime = rip->i_ctime;
- 26086
- 26087 /* Copy the struct to user space. */
- 26088 r = sys_copy(FS_PROC_NR, D, (phys_bytes) &statbuf,
- 26089 who, D, (phys_bytes) user_addr, (phys_bytes) sizeof(statbuf));
- 26090 return(r);
- 26091 }
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- src/fs/protect.c
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- 26100 /* This file deals with protection in the file system. It contains the code
- 26101 * for four system calls that relate to protection.
- 26102 *
- 26103 * The entry points into this file are
- 26104 * do_chmod: perform the CHMOD system call
- 26105 * do_chown: perform the CHOWN system call
- 26106 * do_umask: perform the UMASK system call
- 26107 * do_access: perform the ACCESS system call
- 26108 * forbidden: check to see if a given access is allowed on a given inode
- 26109 */
- 26110
- 26111 #include "fs.h"
- 26112 #include <unistd.h>
- 26113 #include <minix/callnr.h>
- 26114 #include "buf.h"
- 26115 #include "file.h"
- 26116 #include "fproc.h"
- 26117 #include "inode.h"
- 26118 #include "param.h"
- 26119 #include "super.h"
- 26120
- 26121 /*===========================================================================*
- 26122 * do_chmod *
- 26123 *===========================================================================*/
- 26124 PUBLIC int do_chmod()
- .Op 361 src/fs/protect.c
- 26125 {
- 26126 /* Perform the chmod(name, mode) system call. */
- 26127
- 26128 register struct inode *rip;
- 26129 register int r;
- 26130
- 26131 /* Temporarily open the file. */
- 26132 if (fetch_name(name, name_length, M3) != OK) return(err_code);
- 26133 if ( (rip = eat_path(user_path)) == NIL_INODE) return(err_code);
- 26134
- 26135 /* Only the owner or the super_user may change the mode of a file.
- 26136 * No one may change the mode of a file on a read-only file system.
- 26137 */
- 26138 if (rip->i_uid != fp->fp_effuid && !super_user)
- 26139 r = EPERM;
- 26140 else
- 26141 r = read_only(rip);
- 26142
- 26143 /* If error, return inode. */
- 26144 if (r != OK) {
- 26145 put_inode(rip);
- 26146 return(r);
- 26147 }
- 26148
- 26149 /* Now make the change. Clear setgid bit if file is not in caller's grp */
- 26150 rip->i_mode = (rip->i_mode & ~ALL_MODES) | (mode & ALL_MODES);
- 26151 if (!super_user && rip->i_gid != fp->fp_effgid)rip->i_mode &= ~I_SET_GID_BIT;
- 26152 rip->i_update |= CTIME;
- 26153 rip->i_dirt = DIRTY;
- 26154
- 26155 put_inode(rip);
- 26156 return(OK);
- 26157 }
-
-
- 26160 /*===========================================================================*
- 26161 * do_chown *
- 26162 *===========================================================================*/
- 26163 PUBLIC int do_chown()
- 26164 {
- 26165 /* Perform the chown(name, owner, group) system call. */
- 26166
- 26167 register struct inode *rip;
- 26168 register int r;
- 26169
- 26170 /* Temporarily open the file. */
- 26171 if (fetch_name(name1, name1_length, M1) != OK) return(err_code);
- 26172 if ( (rip = eat_path(user_path)) == NIL_INODE) return(err_code);
- 26173
- 26174 /* Not permitted to change the owner of a file on a read-only file sys. */
- 26175 r = read_only(rip);
- 26176 if (r == OK) {
- 26177 /* FS is R/W. Whether call is allowed depends on ownership, etc. */
- 26178 if (super_user) {
- 26179 /* The super user can do anything. */
- 26180 rip->i_uid = owner; /* others later */
- 26181 } else {
- 26182 /* Regular users can only change groups of their own files. */
- 26183 if (rip->i_uid != fp->fp_effuid) r = EPERM;
- 26184 if (rip->i_uid != owner) r = EPERM; /* no giving away */
- .Ep 362 src/fs/protect.c
- 26185 if (fp->fp_effgid != group) r = EPERM;
- 26186 }
- 26187 }
- 26188 if (r == OK) {
- 26189 rip->i_gid = group;
- 26190 rip->i_mode &= ~(I_SET_UID_BIT | I_SET_GID_BIT);
- 26191 rip->i_update |= CTIME;
- 26192 rip->i_dirt = DIRTY;
- 26193 }
- 26194
- 26195 put_inode(rip);
- 26196 return(r);
- 26197 }
-
-
- 26200 /*===========================================================================*
- 26201 * do_umask *
- 26202 *===========================================================================*/
- 26203 PUBLIC int do_umask()
- 26204 {
- 26205 /* Perform the umask(co_mode) system call. */
- 26206 register mode_t r;
- 26207
- 26208 r = ~fp->fp_umask; /* set 'r' to complement of old mask */
- 26209 fp->fp_umask = ~(co_mode & RWX_MODES);
- 26210 return(r); /* return complement of old mask */
- 26211 }
-
-
- 26214 /*===========================================================================*
- 26215 * do_access *
- 26216 *===========================================================================*/
- 26217 PUBLIC int do_access()
- 26218 {
- 26219 /* Perform the access(name, mode) system call. */
- 26220
- 26221 struct inode *rip;
- 26222 register int r;
- 26223
- 26224 /* First check to see if the mode is correct. */
- 26225 if ( (mode & ~(R_OK | W_OK | X_OK)) != 0 && mode != F_OK)
- 26226 return(EINVAL);
- 26227
- 26228 /* Temporarily open the file whose access is to be checked. */
- 26229 if (fetch_name(name, name_length, M3) != OK) return(err_code);
- 26230 if ( (rip = eat_path(user_path)) == NIL_INODE) return(err_code);
- 26231
- 26232 /* Now check the permissions. */
- 26233 r = forbidden(rip, (mode_t) mode);
- 26234 put_inode(rip);
- 26235 return(r);
- 26236 }
-
-
- 26239 /*===========================================================================*
- 26240 * forbidden *
- 26241 *===========================================================================*/
- 26242 PUBLIC int forbidden(rip, access_desired)
- 26243 register struct inode *rip; /* pointer to inode to be checked */
- 26244 mode_t access_desired; /* RWX bits */
- .Op 363 src/fs/protect.c
- 26245 {
- 26246 /* Given a pointer to an inode, 'rip', and the access desired, determine
- 26247 * if the access is allowed, and if not why not. The routine looks up the
- 26248 * caller's uid in the 'fproc' table. If access is allowed, OK is returned
- 26249 * if it is forbidden, EACCES is returned.
- 26250 */
- 26251
- 26252 register struct inode *old_rip = rip;
- 26253 register struct super_block *sp;
- 26254 register mode_t bits, perm_bits;
- 26255 int r, shift, test_uid, test_gid;
- 26256
- 26257 if (rip->i_mount == I_MOUNT) /* The inode is mounted on. */
- 26258 for (sp = &super_block[1]; sp < &super_block[NR_SUPERS]; sp++)
- 26259 if (sp->s_imount == rip) {
- 26260 rip = get_inode(sp->s_dev, ROOT_INODE);
- 26261 break;
- 26262 } /* if */
- 26263
- 26264 /* Isolate the relevant rwx bits from the mode. */
- 26265 bits = rip->i_mode;
- 26266 test_uid = (fs_call == ACCESS ? fp->fp_realuid : fp->fp_effuid);
- 26267 test_gid = (fs_call == ACCESS ? fp->fp_realgid : fp->fp_effgid);
- 26268 if (test_uid == SU_UID) {
- 26269 /* Grant read and write permission. Grant search permission for
- 26270 * directories. Grant execute permission (for non-directories) if
- 26271 * and only if one of the 'X' bits is set.
- 26272 */
- 26273 if ( (bits & I_TYPE) == I_DIRECTORY ||
- 26274 bits & ((X_BIT << 6) | (X_BIT << 3) | X_BIT))
- 26275 perm_bits = R_BIT | W_BIT | X_BIT;
- 26276 else
- 26277 perm_bits = R_BIT | W_BIT;
- 26278 } else {
- 26279 if (test_uid == rip->i_uid) shift = 6; /* owner */
- 26280 else if (test_gid == rip->i_gid ) shift = 3; /* group */
- 26281 else shift = 0; /* other */
- 26282 perm_bits = (bits >> shift) & (R_BIT | W_BIT | X_BIT);
- 26283 }
- 26284
- 26285 /* If access desired is not a subset of what is allowed, it is refused. */
- 26286 r = OK;
- 26287 if ((perm_bits | access_desired) != perm_bits) r = EACCES;
- 26288
- 26289 /* Check to see if someone is trying to write on a file system that is
- 26290 * mounted read-only.
- 26291 */
- 26292 if (r == OK)
- 26293 if (access_desired & W_BIT) r = read_only(rip);
- 26294
- 26295 if (rip != old_rip) put_inode(rip);
- 26296
- 26297 return(r);
- 26298 }
-
-
- 26301 /*===========================================================================*
- 26302 * read_only *
- 26303 *===========================================================================*/
- 26304 PUBLIC int read_only(ip)
- .Ep 364 src/fs/protect.c
- 26305 struct inode *ip; /* ptr to inode whose file sys is to be cked */
- 26306 {
- 26307 /* Check to see if the file system on which the inode 'ip' resides is mounted
- 26308 * read only. If so, return EROFS, else return OK.
- 26309 */
- 26310
- 26311 register struct super_block *sp;
- 26312
- 26313 sp = ip->i_sp;
- 26314 return(sp->s_rd_only ? EROFS : OK);
- 26315 }
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- src/fs/time.c
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- 26400 /* This file takes care of those system calls that deal with time.
- 26401 *
- 26402 * The entry points into this file are
- 26403 * do_utime: perform the UTIME system call
- 26404 * do_time: perform the TIME system call
- 26405 * do_stime: perform the STIME system call
- 26406 * do_tims: perform the TIMES system call
- 26407 */
- 26408
- 26409 #include "fs.h"
- 26410 #include <minix/callnr.h>
- 26411 #include <minix/com.h>
- 26412 #include "file.h"
- 26413 #include "fproc.h"
- 26414 #include "inode.h"
- 26415 #include "param.h"
- 26416
- 26417 PRIVATE message clock_mess;
- 26418
- 26419 /*===========================================================================*
- 26420 * do_utime *
- 26421 *===========================================================================*/
- 26422 PUBLIC int do_utime()
- 26423 {
- 26424 /* Perform the utime(name, timep) system call. */
- 26425
- 26426 register struct inode *rip;
- 26427 register int len, r;
- 26428
- 26429 /* Adjust for case of NULL 'timep'. */
- 26430 len = utime_length;
- 26431 if (len == 0) len = m.m2_i2;
- 26432
- 26433 /* Temporarily open the file. */
- 26434 if (fetch_name(utime_file, len, M1) != OK) return(err_code);
- 26435 if ( (rip = eat_path(user_path)) == NIL_INODE) return(err_code);
- 26436
- 26437 /* Only the owner of a file or the super_user can change its time. */
- 26438 r = OK;
- 26439 if (rip->i_uid != fp->fp_effuid && !super_user) r = EPERM;
- .Op 365 src/fs/time.c
- 26440 if (utime_length == 0 && r != OK) r = forbidden(rip, W_BIT);
- 26441 if (read_only(rip) != OK) r = EROFS; /* not even su can touch if R/O */
- 26442 if (r == OK) {
- 26443 if (utime_length == 0) {
- 26444 rip->i_atime = clock_time();
- 26445 rip->i_mtime = rip->i_atime;
- 26446 } else {
- 26447 rip->i_atime = utime_actime;
- 26448 rip->i_mtime = utime_modtime;
- 26449 }
- 26450 rip->i_update = CTIME; /* discard any stale ATIME and MTIME flags */
- 26451 rip->i_dirt = DIRTY;
- 26452 }
- 26453
- 26454 put_inode(rip);
- 26455 return(r);
- 26456 }
-
-
- 26459 /*===========================================================================*
- 26460 * do_time *
- 26461 *===========================================================================*/
- 26462 PUBLIC int do_time()
- 26463
- 26464 {
- 26465 /* Perform the time(tp) system call. */
- 26466
- 26467 reply_l1 = clock_time(); /* return time in seconds */
- 26468 return(OK);
- 26469 }
-
-
- 26472 /*===========================================================================*
- 26473 * do_stime *
- 26474 *===========================================================================*/
- 26475 PUBLIC int do_stime()
- 26476 {
- 26477 /* Perform the stime(tp) system call. */
- 26478
- 26479 register int k;
- 26480
- 26481 if (!super_user) return(EPERM);
- 26482 clock_mess.m_type = SET_TIME;
- 26483 clock_mess.NEW_TIME = (long) tp;
- 26484 if ( (k = sendrec(CLOCK, &clock_mess)) != OK) panic("do_stime error", k);
- 26485 return(OK);
- 26486 }
-
-
- 26489 /*===========================================================================*
- 26490 * do_tims *
- 26491 *===========================================================================*/
- 26492 PUBLIC int do_tims()
- 26493 {
- 26494 /* Perform the times(buffer) system call. */
- 26495
- 26496 clock_t t[5];
- 26497
- 26498 sys_times(who, t);
- 26499 reply_t1 = t[0];
- .Ep 366 src/fs/time.c
- 26500 reply_t2 = t[1];
- 26501 reply_t3 = t[2];
- 26502 reply_t4 = t[3];
- 26503 reply_t5 = t[4];
- 26504 return(OK);
- 26505 }
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- src/fs/misc.c
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- 26600 /* This file contains a collection of miscellaneous procedures. Some of them
- 26601 * perform simple system calls. Some others do a little part of system calls
- 26602 * that are mostly performed by the Memory Manager.
- 26603 *
- 26604 * The entry points into this file are
- 26605 * do_dup: perform the DUP system call
- 26606 * do_fcntl: perform the FCNTL system call
- 26607 * do_sync: perform the SYNC system call
- 26608 * do_fork: adjust the tables after MM has performed a FORK system call
- 26609 * do_exec: handle files with FD_CLOEXEC on after MM has done an EXEC
- 26610 * do_exit: a process has exited; note that in the tables
- 26611 * do_set: set uid or gid for some process
- 26612 * do_revive: revive a process that was waiting for something (e.g. TTY)
- 26613 */
- 26614
- 26615 #include "fs.h"
- 26616 #include <fcntl.h>
- 26617 #include <unistd.h> /* cc runs out of memory with unistd.h :-( */
- 26618 #include <minix/callnr.h>
- 26619 #include <minix/com.h>
- 26620 #include <minix/boot.h>
- 26621 #include "buf.h"
- 26622 #include "file.h"
- 26623 #include "fproc.h"
- 26624 #include "inode.h"
- 26625 #include "dev.h"
- 26626 #include "param.h"
- 26627
- 26628
- 26629 /*===========================================================================*
- 26630 * do_dup *
- 26631 *===========================================================================*/
- 26632 PUBLIC int do_dup()
- 26633 {
- 26634 /* Perform the dup(fd) or dup2(fd,fd2) system call. These system calls are
- 26635 * obsolete. In fact, it is not even possible to invoke them using the
- 26636 * current library because the library routines call fcntl(). They are
- 26637 * provided to permit old binary programs to continue to run.
- 26638 */
- 26639
- 26640 register int rfd;
- 26641 register struct filp *f;
- 26642 struct filp *dummy;
- 26643 int r;
- 26644
- .Op 367 src/fs/misc.c
- 26645 /* Is the file descriptor valid? */
- 26646 rfd = fd & ~DUP_MASK; /* kill off dup2 bit, if on */
- 26647 if ((f = get_filp(rfd)) == NIL_FILP) return(err_code);
- 26648
- 26649 /* Distinguish between dup and dup2. */
- 26650 if (fd == rfd) { /* bit not on */
- 26651 /* dup(fd) */
- 26652 if ( (r = get_fd(0, 0, &fd2, &dummy)) != OK) return(r);
- 26653 } else {
- 26654 /* dup2(fd, fd2) */
- 26655 if (fd2 < 0 || fd2 >= OPEN_MAX) return(EBADF);
- 26656 if (rfd == fd2) return(fd2); /* ignore the call: dup2(x, x) */
- 26657 fd = fd2; /* prepare to close fd2 */
- 26658 (void) do_close(); /* cannot fail */
- 26659 }
- 26660
- 26661 /* Success. Set up new file descriptors. */
- 26662 f->filp_count++;
- 26663 fp->fp_filp[fd2] = f;
- 26664 return(fd2);
- 26665 }
-
- 26667 /*===========================================================================*
- 26668 * do_fcntl *
- 26669 *===========================================================================*/
- 26670 PUBLIC int do_fcntl()
- 26671 {
- 26672 /* Perform the fcntl(fd, request, ...) system call. */
- 26673
- 26674 register struct filp *f;
- 26675 int new_fd, r, fl;
- 26676 long cloexec_mask; /* bit map for the FD_CLOEXEC flag */
- 26677 long clo_value; /* FD_CLOEXEC flag in proper position */
- 26678 struct filp *dummy;
- 26679
- 26680 /* Is the file descriptor valid? */
- 26681 if ((f = get_filp(fd)) == NIL_FILP) return(err_code);
- 26682
- 26683 switch (request) {
- 26684 case F_DUPFD:
- 26685 /* This replaces the old dup() system call. */
- 26686 if (addr < 0 || addr >= OPEN_MAX) return(EINVAL);
- 26687 if ((r = get_fd(addr, 0, &new_fd, &dummy)) != OK) return(r);
- 26688 f->filp_count++;
- 26689 fp->fp_filp[new_fd] = f;
- 26690 return(new_fd);
- 26691
- 26692 case F_GETFD:
- 26693 /* Get close-on-exec flag (FD_CLOEXEC in POSIX Table 6-2). */
- 26694 return( ((fp->fp_cloexec >> fd) & 01) ? FD_CLOEXEC : 0);
- 26695
- 26696 case F_SETFD:
- 26697 /* Set close-on-exec flag (FD_CLOEXEC in POSIX Table 6-2). */
- 26698 cloexec_mask = 1L << fd; /* singleton set position ok */
- 26699 clo_value = (addr & FD_CLOEXEC ? cloexec_mask : 0L);
- 26700 fp->fp_cloexec = (fp->fp_cloexec & ~cloexec_mask) | clo_value;
- 26701 return(OK);
- 26702
- 26703 case F_GETFL:
- 26704 /* Get file status flags (O_NONBLOCK and O_APPEND). */
- .Ep 368 src/fs/misc.c
- 26705 fl = f->filp_flags & (O_NONBLOCK | O_APPEND | O_ACCMODE);
- 26706 return(fl);
- 26707
- 26708 case F_SETFL:
- 26709 /* Set file status flags (O_NONBLOCK and O_APPEND). */
- 26710 fl = O_NONBLOCK | O_APPEND;
- 26711 f->filp_flags = (f->filp_flags & ~fl) | (addr & fl);
- 26712 return(OK);
- 26713
- 26714 case F_GETLK:
- 26715 case F_SETLK:
- 26716 case F_SETLKW:
- 26717 /* Set or clear a file lock. */
- 26718 r = lock_op(f, request);
- 26719 return(r);
- 26720
- 26721 default:
- 26722 return(EINVAL);
- 26723 }
- 26724 }
-
-
- 26727 /*===========================================================================*
- 26728 * do_sync *
- 26729 *===========================================================================*/
- 26730 PUBLIC int do_sync()
- 26731 {
- 26732 /* Perform the sync() system call. Flush all the tables. */
- 26733
- 26734 register struct inode *rip;
- 26735 register struct buf *bp;
- 26736
- 26737 /* The order in which the various tables are flushed is critical. The
- 26738 * blocks must be flushed last, since rw_inode() leaves its results in
- 26739 * the block cache.
- 26740 */
- 26741
- 26742 /* Write all the dirty inodes to the disk. */
- 26743 for (rip = &inode[0]; rip < &inode[NR_INODES]; rip++)
- 26744 if (rip->i_count > 0 && rip->i_dirt == DIRTY) rw_inode(rip, WRITING);
- 26745
- 26746 /* Write all the dirty blocks to the disk, one drive at a time. */
- 26747 for (bp = &buf[0]; bp < &buf[NR_BUFS]; bp++)
- 26748 if (bp->b_dev != NO_DEV && bp->b_dirt == DIRTY) flushall(bp->b_dev);
- 26749
- 26750 return(OK); /* sync() can't fail */
- 26751 }
-
-
- 26754 /*===========================================================================*
- 26755 * do_fork *
- 26756 *===========================================================================*/
- 26757 PUBLIC int do_fork()
- 26758 {
- 26759 /* Perform those aspects of the fork() system call that relate to files.
- 26760 * In particular, let the child inherit its parent's file descriptors.
- 26761 * The parent and child parameters tell who forked off whom. The file
- 26762 * system uses the same slot numbers as the kernel. Only MM makes this call.
- 26763 */
- 26764
- .Op 369 src/fs/misc.c
- 26765 register struct fproc *cp;
- 26766 int i;
- 26767
- 26768 /* Only MM may make this call directly. */
- 26769 if (who != MM_PROC_NR) return(EGENERIC);
- 26770
- 26771 /* Copy the parent's fproc struct to the child. */
- 26772 fproc[child] = fproc[parent];
- 26773
- 26774 /* Increase the counters in the 'filp' table. */
- 26775 cp = &fproc[child];
- 26776 for (i = 0; i < OPEN_MAX; i++)
- 26777 if (cp->fp_filp[i] != NIL_FILP) cp->fp_filp[i]->filp_count++;
- 26778
- 26779 /* Fill in new process id. */
- 26780 cp->fp_pid = pid;
- 26781
- 26782 /* A child is not a process leader. */
- 26783 cp->fp_sesldr = 0;
- 26784
- 26785 /* Record the fact that both root and working dir have another user. */
- 26786 dup_inode(cp->fp_rootdir);
- 26787 dup_inode(cp->fp_workdir);
- 26788 return(OK);
- 26789 }
-
-
- 26792 /*===========================================================================*
- 26793 * do_exec *
- 26794 *===========================================================================*/
- 26795 PUBLIC int do_exec()
- 26796 {
- 26797 /* Files can be marked with the FD_CLOEXEC bit (in fp->fp_cloexec). When
- 26798 * MM does an EXEC, it calls FS to allow FS to find these files and close them.
- 26799 */
- 26800
- 26801 register int i;
- 26802 long bitmap;
- 26803
- 26804 /* Only MM may make this call directly. */
- 26805 if (who != MM_PROC_NR) return(EGENERIC);
- 26806
- 26807 /* The array of FD_CLOEXEC bits is in the fp_cloexec bit map. */
- 26808 fp = &fproc[slot1]; /* get_filp() needs 'fp' */
- 26809 bitmap = fp->fp_cloexec;
- 26810 if (bitmap == 0) return(OK); /* normal case, no FD_CLOEXECs */
- 26811
- 26812 /* Check the file desriptors one by one for presence of FD_CLOEXEC. */
- 26813 for (i = 0; i < OPEN_MAX; i++) {
- 26814 fd = i;
- 26815 if ( (bitmap >> i) & 01) (void) do_close();
- 26816 }
- 26817
- 26818 return(OK);
- 26819 }
-
-
- 26822 /*===========================================================================*
- 26823 * do_exit *
- 26824 *===========================================================================*/
- .Ep 370 src/fs/misc.c
- 26825 PUBLIC int do_exit()
- 26826 {
- 26827 /* Perform the file system portion of the exit(status) system call. */
- 26828
- 26829 register int i, exitee, task;
- 26830 register struct fproc *rfp;
- 26831 register struct filp *rfilp;
- 26832 register struct inode *rip;
- 26833 int major;
- 26834 dev_t dev;
- 26835 message dev_mess;
- 26836
- 26837 /* Only MM may do the EXIT call directly. */
- 26838 if (who != MM_PROC_NR) return(EGENERIC);
- 26839
- 26840 /* Nevertheless, pretend that the call came from the user. */
- 26841 fp = &fproc[slot1]; /* get_filp() needs 'fp' */
- 26842 exitee = slot1;
- 26843
- 26844 if (fp->fp_suspended == SUSPENDED) {
- 26845 task = -fp->fp_task;
- 26846 if (task == XPIPE || task == XPOPEN) susp_count--;
- 26847 pro = exitee;
- 26848 (void) do_unpause(); /* this always succeeds for MM */
- 26849 fp->fp_suspended = NOT_SUSPENDED;
- 26850 }
- 26851
- 26852 /* Loop on file descriptors, closing any that are open. */
- 26853 for (i = 0; i < OPEN_MAX; i++) {
- 26854 fd = i;
- 26855 (void) do_close();
- 26856 }
- 26857
- 26858 /* Release root and working directories. */
- 26859 put_inode(fp->fp_rootdir);
- 26860 put_inode(fp->fp_workdir);
- 26861 fp->fp_rootdir = NIL_INODE;
- 26862 fp->fp_workdir = NIL_INODE;
- 26863
- 26864 /* If a session leader exits then revoke access to its controlling tty from
- 26865 * all other processes using it.
- 26866 */
- 26867 if (!fp->fp_sesldr) return(OK); /* not a session leader */
- 26868 fp->fp_sesldr = FALSE;
- 26869 if (fp->fp_tty == 0) return(OK); /* no controlling tty */
- 26870 dev = fp->fp_tty;
- 26871
- 26872 for (rfp = &fproc[LOW_USER]; rfp < &fproc[NR_PROCS]; rfp++) {
- 26873 if (rfp->fp_tty == dev) rfp->fp_tty = 0;
- 26874
- 26875 for (i = 0; i < OPEN_MAX; i++) {
- 26876 if ((rfilp = rfp->fp_filp[i]) == NIL_FILP) continue;
- 26877 if (rfilp->filp_mode == FILP_CLOSED) continue;
- 26878 rip = rfilp->filp_ino;
- 26879 if ((rip->i_mode & I_TYPE) != I_CHAR_SPECIAL) continue;
- 26880 if ((dev_t) rip->i_zone[0] != dev) continue;
- 26881 dev_mess.m_type = DEV_CLOSE;
- 26882 dev_mess.DEVICE = dev;
- 26883 major = (dev >> MAJOR) & BYTE; /* major device nr */
- 26884 task = dmap[major].dmap_task; /* device task nr */
- .Op 371 src/fs/misc.c
- 26885 (*dmap[major].dmap_close)(task, &dev_mess);
- 26886 rfilp->filp_mode = FILP_CLOSED;
- 26887 }
- 26888 }
- 26889 return(OK);
- 26890 }
-
-
- 26893 /*===========================================================================*
- 26894 * do_set *
- 26895 *===========================================================================*/
- 26896 PUBLIC int do_set()
- 26897 {
- 26898 /* Set uid_t or gid_t field. */
- 26899
- 26900 register struct fproc *tfp;
- 26901
- 26902 /* Only MM may make this call directly. */
- 26903 if (who != MM_PROC_NR) return(EGENERIC);
- 26904
- 26905 tfp = &fproc[slot1];
- 26906 if (fs_call == SETUID) {
- 26907 tfp->fp_realuid = (uid_t) real_user_id;
- 26908 tfp->fp_effuid = (uid_t) eff_user_id;
- 26909 }
- 26910 if (fs_call == SETGID) {
- 26911 tfp->fp_effgid = (gid_t) eff_grp_id;
- 26912 tfp->fp_realgid = (gid_t) real_grp_id;
- 26913 }
- 26914 return(OK);
- 26915 }
-
-
- 26918 /*===========================================================================*
- 26919 * do_revive *
- 26920 *===========================================================================*/
- 26921 PUBLIC int do_revive()
- 26922 {
- 26923 /* A task, typically TTY, has now gotten the characters that were needed for a
- 26924 * previous read. The process did not get a reply when it made the call.
- 26925 * Instead it was suspended. Now we can send the reply to wake it up. This
- 26926 * business has to be done carefully, since the incoming message is from
- 26927 * a task (to which no reply can be sent), and the reply must go to a process
- 26928 * that blocked earlier. The reply to the caller is inhibited by setting the
- 26929 * 'dont_reply' flag, and the reply to the blocked process is done explicitly
- 26930 * in revive().
- 26931 */
- 26932
- 26933 #if !ALLOW_USER_SEND
- 26934 if (who >= LOW_USER) return(EPERM);
- 26935 #endif
- 26936
- 26937 revive(m.REP_PROC_NR, m.REP_STATUS);
- 26938 dont_reply = TRUE; /* don't reply to the TTY task */
- 26939 return(OK);
- 26940 }
- .Ep 372 src/fs/device.c
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- src/fs/device.c
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- 27000 /* When a needed block is not in the cache, it must be fetched from the disk.
- 27001 * Special character files also require I/O. The routines for these are here.
- 27002 *
- 27003 * The entry points in this file are:
- 27004 * dev_io: perform a read or write on a block or character device
- 27005 * dev_opcl: perform generic device-specific processing for open & close
- 27006 * tty_open: perform tty-specific processing for open
- 27007 * ctty_open: perform controlling-tty-specific processing for open
- 27008 * ctty_close: perform controlling-tty-specific processing for close
- 27009 * do_setsid: perform the SETSID system call (FS side)
- 27010 * do_ioctl: perform the IOCTL system call
- 27011 * call_task: procedure that actually calls the kernel tasks
- 27012 * call_ctty: procedure that actually calls task for /dev/tty
- 27013 */
- 27014
- 27015 #include "fs.h"
- 27016 #include <fcntl.h>
- 27017 #include <minix/callnr.h>
- 27018 #include <minix/com.h>
- 27019 #include "dev.h"
- 27020 #include "file.h"
- 27021 #include "fproc.h"
- 27022 #include "inode.h"
- 27023 #include "param.h"
- 27024
- 27025 PRIVATE message dev_mess;
- 27026 PRIVATE major, minor, task;
- 27027
- 27028 FORWARD _PROTOTYPE( void find_dev, (Dev_t dev) );
- 27029
- 27030 /*===========================================================================*
- 27031 * dev_io *
- 27032 *===========================================================================*/
- 27033 PUBLIC int dev_io(op, nonblock, dev, pos, bytes, proc, buff)
- 27034 int op; /* DEV_READ, DEV_WRITE, DEV_IOCTL, etc. */
- 27035 int nonblock; /* TRUE if nonblocking op */
- 27036 dev_t dev; /* major-minor device number */
- 27037 off_t pos; /* byte position */
- 27038 int bytes; /* how many bytes to transfer */
- 27039 int proc; /* in whose address space is buff? */
- 27040 char *buff; /* virtual address of the buffer */
- 27041 {
- 27042 /* Read or write from a device. The parameter 'dev' tells which one. */
- 27043
- 27044 find_dev(dev); /* load the variables major, minor, and task */
- 27045
- 27046 /* Set up the message passed to task. */
- 27047 dev_mess.m_type = op;
- 27048 dev_mess.DEVICE = (dev >> MINOR) & BYTE;
- 27049 dev_mess.POSITION = pos;
- 27050 dev_mess.PROC_NR = proc;
- 27051 dev_mess.ADDRESS = buff;
- 27052 dev_mess.COUNT = bytes;
- 27053 dev_mess.TTY_FLAGS = nonblock; /* temporary kludge */
- 27054
- .Op 373 src/fs/device.c
- 27055 /* Call the task. */
- 27056 (*dmap[major].dmap_rw)(task, &dev_mess);
- 27057
- 27058 /* Task has completed. See if call completed. */
- 27059 if (dev_mess.REP_STATUS == SUSPEND) {
- 27060 if (op == DEV_OPEN) task = XPOPEN;
- 27061 suspend(task); /* suspend user */
- 27062 }
- 27063
- 27064 return(dev_mess.REP_STATUS);
- 27065 }
-
-
- 27068 /*===========================================================================*
- 27069 * dev_opcl *
- 27070 *===========================================================================*/
- 27071 PUBLIC void dev_opcl(task_nr, mess_ptr)
- 27072 int task_nr; /* which task */
- 27073 message *mess_ptr; /* message pointer */
- 27074 {
- 27075 /* Called from the dmap struct in table.c on opens & closes of special files.*/
- 27076
- 27077 int op;
- 27078
- 27079 op = mess_ptr->m_type; /* save DEV_OPEN or DEV_CLOSE for later */
- 27080 mess_ptr->DEVICE = (mess_ptr->DEVICE >> MINOR) & BYTE;
- 27081 mess_ptr->PROC_NR = fp - fproc;
- 27082
- 27083 call_task(task_nr, mess_ptr);
- 27084
- 27085 /* Task has completed. See if call completed. */
- 27086 if (mess_ptr->REP_STATUS == SUSPEND) {
- 27087 if (op == DEV_OPEN) task_nr = XPOPEN;
- 27088 suspend(task_nr); /* suspend user */
- 27089 }
- 27090 }
-
- 27092 /*===========================================================================*
- 27093 * tty_open *
- 27094 *===========================================================================*/
- 27095 PUBLIC void tty_open(task_nr, mess_ptr)
- 27096 int task_nr;
- 27097 message *mess_ptr;
- 27098 {
- 27099 /* This procedure is called from the dmap struct in table.c on tty opens. */
- 27100
- 27101 int r;
- 27102 dev_t dev;
- 27103 int flags, proc;
- 27104 register struct fproc *rfp;
- 27105
- 27106 dev = (dev_t) mess_ptr->DEVICE;
- 27107 flags = mess_ptr->COUNT;
- 27108 proc = fp - fproc;
- 27109
- 27110 /* Add O_NOCTTY to the flags if this process is not a session leader, or
- 27111 * if it already has a controlling tty, or if it is someone elses
- 27112 * controlling tty.
- 27113 */
- 27114 if (!fp->fp_sesldr || fp->fp_tty != 0) {
- .Ep 374 src/fs/device.c
- 27115 flags |= O_NOCTTY;
- 27116 } else {
- 27117 for (rfp = &fproc[LOW_USER]; rfp < &fproc[NR_PROCS]; rfp++) {
- 27118 if (rfp->fp_tty == dev) flags |= O_NOCTTY;
- 27119 }
- 27120 }
- 27121
- 27122 r = dev_io(DEV_OPEN, mode, dev, (off_t) 0, flags, proc, NIL_PTR);
- 27123
- 27124 if (r == 1) {
- 27125 fp->fp_tty = dev;
- 27126 r = OK;
- 27127 }
- 27128
- 27129 mess_ptr->REP_STATUS = r;
- 27130 }
-
-
- 27133 /*===========================================================================*
- 27134 * ctty_open *
- 27135 *===========================================================================*/
- 27136 PUBLIC void ctty_open(task_nr, mess_ptr)
- 27137 int task_nr;
- 27138 message *mess_ptr;
- 27139 {
- 27140 /* This procedure is called from the dmap struct in table.c on opening
- 27141 * /dev/tty, the magic device that translates to the controlling tty.
- 27142 */
- 27143
- 27144 mess_ptr->REP_STATUS = fp->fp_tty == 0 ? ENXIO : OK;
- 27145 }
-
-
- 27148 /*===========================================================================*
- 27149 * ctty_close *
- 27150 *===========================================================================*/
- 27151 PUBLIC void ctty_close(task_nr, mess_ptr)
- 27152 int task_nr;
- 27153 message *mess_ptr;
- 27154 {
- 27155 /* Close /dev/tty. */
- 27156
- 27157 mess_ptr->REP_STATUS = OK;
- 27158 }
-
-
- 27161 /*===========================================================================*
- 27162 * do_setsid *
- 27163 *===========================================================================*/
- 27164 PUBLIC int do_setsid()
- 27165 {
- 27166 /* Perform the FS side of the SETSID call, i.e. get rid of the controlling
- 27167 * terminal of a process, and make the process a session leader.
- 27168 */
- 27169 register struct fproc *rfp;
- 27170
- 27171 /* Only MM may do the SETSID call directly. */
- 27172 if (who != MM_PROC_NR) return(ENOSYS);
- 27173
- 27174 /* Make the process a session leader with no controlling tty. */
- .Op 375 src/fs/device.c
- 27175 rfp = &fproc[slot1];
- 27176 rfp->fp_sesldr = TRUE;
- 27177 rfp->fp_tty = 0;
- 27178 }
-
-
- 27181 /*===========================================================================*
- 27182 * do_ioctl *
- 27183 *===========================================================================*/
- 27184 PUBLIC int do_ioctl()
- 27185 {
- 27186 /* Perform the ioctl(ls_fd, request, argx) system call (uses m2 fmt). */
- 27187
- 27188 struct filp *f;
- 27189 register struct inode *rip;
- 27190 dev_t dev;
- 27191
- 27192 if ( (f = get_filp(ls_fd)) == NIL_FILP) return(err_code);
- 27193 rip = f->filp_ino; /* get inode pointer */
- 27194 if ( (rip->i_mode & I_TYPE) != I_CHAR_SPECIAL
- 27195 && (rip->i_mode & I_TYPE) != I_BLOCK_SPECIAL) return(ENOTTY);
- 27196 dev = (dev_t) rip->i_zone[0];
- 27197 find_dev(dev);
- 27198
- 27199 dev_mess= m;
- 27200
- 27201 dev_mess.m_type = DEV_IOCTL;
- 27202 dev_mess.PROC_NR = who;
- 27203 dev_mess.TTY_LINE = minor;
- 27204
- 27205 /* Call the task. */
- 27206 (*dmap[major].dmap_rw)(task, &dev_mess);
- 27207
- 27208 /* Task has completed. See if call completed. */
- 27209 if (dev_mess.REP_STATUS == SUSPEND) {
- 27210 if (f->filp_flags & O_NONBLOCK) {
- 27211 /* Not supposed to block. */
- 27212 dev_mess.m_type = CANCEL;
- 27213 dev_mess.PROC_NR = who;
- 27214 dev_mess.TTY_LINE = minor;
- 27215 (*dmap[major].dmap_rw)(task, &dev_mess);
- 27216 if (dev_mess.REP_STATUS == EINTR) dev_mess.REP_STATUS = EAGAIN;
- 27217 } else {
- 27218 suspend(task); /* User must be suspended. */
- 27219 }
- 27220 }
- 27221 return(dev_mess.REP_STATUS);
- 27222 }
-
-
- 27225 /*===========================================================================*
- 27226 * find_dev *
- 27227 *===========================================================================*/
- 27228 PRIVATE void find_dev(dev)
- 27229 dev_t dev; /* device */
- 27230 {
- 27231 /* Extract the major and minor device number from the parameter. */
- 27232
- 27233 major = (dev >> MAJOR) & BYTE; /* major device number */
- 27234 minor = (dev >> MINOR) & BYTE; /* minor device number */
- .Ep 376 src/fs/device.c
- 27235 if (major >= max_major) {
- 27236 major = minor = 0; /* will fail with ENODEV */
- 27237 }
- 27238 task = dmap[major].dmap_task; /* which task services the device */
- 27239 }
-
-
- 27242 /*===========================================================================*
- 27243 * call_task *
- 27244 *===========================================================================*/
- 27245 PUBLIC void call_task(task_nr, mess_ptr)
- 27246 int task_nr; /* which task to call */
- 27247 message *mess_ptr; /* pointer to message for task */
- 27248 {
- 27249 /* All file system I/O ultimately comes down to I/O on major/minor device
- 27250 * pairs. These lead to calls on the following routines via the dmap table.
- 27251 */
- 27252
- 27253 int r, proc_nr;
- 27254 message local_m;
- 27255
- 27256 proc_nr = mess_ptr->PROC_NR;
- 27257
- 27258 while ((r = sendrec(task_nr, mess_ptr)) == ELOCKED) {
- 27259 /* sendrec() failed to avoid deadlock. The task 'task_nr' is
- 27260 * trying to send a REVIVE message for an earlier request.
- 27261 * Handle it and go try again.
- 27262 */
- 27263 if ((r = receive(task_nr, &local_m)) != OK) break;
- 27264
- 27265 /* If we're trying to send a cancel message to a task which has just
- 27266 * sent a completion reply, ignore the reply and abort the cancel
- 27267 * request. The caller will do the revive for the process.
- 27268 */
- 27269 if (mess_ptr->m_type == CANCEL && local_m.REP_PROC_NR == proc_nr)
- 27270 return;
- 27271
- 27272 /* Otherwise it should be a REVIVE. */
- 27273 if (local_m.m_type != REVIVE) {
- 27274 printf(
- 27275 "fs: strange device reply from %d, type = %d, proc = %dn",
- 27276 local_m.m_source,
- 27277 local_m.m_type, local_m.REP_PROC_NR);
- 27278 continue;
- 27279 }
- 27280
- 27281 revive(local_m.REP_PROC_NR, local_m.REP_STATUS);
- 27282 }
- 27283
- 27284 /* The message received may be a reply to this call, or a REVIVE for some
- 27285 * other process.
- 27286 */
- 27287 for (;;) {
- 27288 if (r != OK) panic("call_task: can't send/receive", NO_NUM);
- 27289
- 27290 /* Did the process we did the sendrec() for get a result? */
- 27291 if (mess_ptr->REP_PROC_NR == proc_nr) break;
- 27292
- 27293 /* Otherwise it should be a REVIVE. */
- 27294 if (mess_ptr->m_type != REVIVE) {
- .Op 377 src/fs/device.c
- 27295 printf(
- 27296 "fs: strange device reply from %d, type = %d, proc = %dn",
- 27297 mess_ptr->m_source,
- 27298 mess_ptr->m_type, mess_ptr->REP_PROC_NR);
- 27299 continue;
- 27300 }
- 27301 revive(mess_ptr->REP_PROC_NR, mess_ptr->REP_STATUS);
- 27302
- 27303 r = receive(task_nr, mess_ptr);
- 27304 }
- 27305 }
-
-
- 27308 /*===========================================================================*
- 27309 * call_ctty *
- 27310 *===========================================================================*/
- 27311 PUBLIC void call_ctty(task_nr, mess_ptr)
- 27312 int task_nr; /* not used - for compatibility with dmap_t */
- 27313 message *mess_ptr; /* pointer to message for task */
- 27314 {
- 27315 /* This routine is only called for one device, namely /dev/tty. Its job
- 27316 * is to change the message to use the controlling terminal, instead of the
- 27317 * major/minor pair for /dev/tty itself.
- 27318 */
- 27319
- 27320 int major_device;
- 27321
- 27322 if (fp->fp_tty == 0) {
- 27323 /* No controlling tty present anymore, return an I/O error. */
- 27324 mess_ptr->REP_STATUS = EIO;
- 27325 return;
- 27326 }
- 27327 major_device = (fp->fp_tty >> MAJOR) & BYTE;
- 27328 task_nr = dmap[major_device].dmap_task; /* task for controlling tty */
- 27329 mess_ptr->DEVICE = (fp->fp_tty >> MINOR) & BYTE;
- 27330 call_task(task_nr, mess_ptr);
- 27331 }
-
-
- 27334 /*===========================================================================*
- 27335 * no_dev *
- 27336 *===========================================================================*/
- 27337 PUBLIC void no_dev(task_nr, m_ptr)
- 27338 int task_nr; /* not used - for compatibility with dmap_t */
- 27339 message *m_ptr; /* message pointer */
- 27340 {
- 27341 /* No device there. */
- 27342
- 27343 m_ptr->REP_STATUS = ENODEV;
- 27344 }
- .Ep 378 src/fs/utility.c
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- src/fs/utility.c
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- 27400 /* This file contains a few general purpose utility routines.
- 27401 *
- 27402 * The entry points into this file are
- 27403 * clock_time: ask the clock task for the real time
- 27404 * copy: copy a block of data
- 27405 * fetch_name: go get a path name from user space
- 27406 * no_sys: reject a system call that FS does not handle
- 27407 * panic: something awful has occurred; MINIX cannot continue
- 27408 * conv2: do byte swapping on a 16-bit int
- 27409 * conv4: do byte swapping on a 32-bit long
- 27410 */
- 27411
- 27412 #include "fs.h"
- 27413 #include <minix/com.h>
- 27414 #include <minix/boot.h>
- 27415 #include <unistd.h>
- 27416 #include "buf.h"
- 27417 #include "file.h"
- 27418 #include "fproc.h"
- 27419 #include "inode.h"
- 27420 #include "param.h"
- 27421
- 27422 PRIVATE int panicking; /* inhibits recursive panics during sync */
- 27423 PRIVATE message clock_mess;
- 27424
- 27425 /*===========================================================================*
- 27426 * clock_time *
- 27427 *===========================================================================*/
- 27428 PUBLIC time_t clock_time()
- 27429 {
- 27430 /* This routine returns the time in seconds since 1.1.1970. MINIX is an
- 27431 * astrophysically naive system that assumes the earth rotates at a constant
- 27432 * rate and that such things as leap seconds do not exist.
- 27433 */
- 27434
- 27435 register int k;
- 27436
- 27437 clock_mess.m_type = GET_TIME;
- 27438 if ( (k = sendrec(CLOCK, &clock_mess)) != OK) panic("clock_time err", k);
- 27439
- 27440 return( (time_t) clock_mess.NEW_TIME);
- 27441 }
-
-
- 27444 /*===========================================================================*
- 27445 * fetch_name *
- 27446 *===========================================================================*/
- 27447 PUBLIC int fetch_name(path, len, flag)
- 27448 char *path; /* pointer to the path in user space */
- 27449 int len; /* path length, including 0 byte */
- 27450 int flag; /* M3 means path may be in message */
- 27451 {
- 27452 /* Go get path and put it in 'user_path'.
- 27453 * If 'flag' = M3 and 'len' <= M3_STRING, the path is present in 'message'.
- 27454 * If it is not, go copy it from user space.
- .Op 379 src/fs/utility.c
- 27455 */
- 27456
- 27457 register char *rpu, *rpm;
- 27458 int r;
- 27459
- 27460 /* Check name length for validity. */
- 27461 if (len <= 0) {
- 27462 err_code = EINVAL;
- 27463 return(EGENERIC);
- 27464 }
- 27465
- 27466 if (len > PATH_MAX) {
- 27467 err_code = ENAMETOOLONG;
- 27468 return(EGENERIC);
- 27469 }
- 27470
- 27471 if (flag == M3 && len <= M3_STRING) {
- 27472 /* Just copy the path from the message to 'user_path'. */
- 27473 rpu = &user_path[0];
- 27474 rpm = pathname; /* contained in input message */
- 27475 do { *rpu++ = *rpm++; } while (--len);
- 27476 r = OK;
- 27477 } else {
- 27478 /* String is not contained in the message. Get it from user space. */
- 27479 r = sys_copy(who, D, (phys_bytes) path,
- 27480 FS_PROC_NR, D, (phys_bytes) user_path, (phys_bytes) len);
- 27481 }
- 27482 return(r);
- 27483 }
-
-
- 27486 /*===========================================================================*
- 27487 * no_sys *
- 27488 *===========================================================================*/
- 27489 PUBLIC int no_sys()
- 27490 {
- 27491 /* Somebody has used an illegal system call number */
- 27492
- 27493 return(EINVAL);
- 27494 }
-
-
- 27497 /*===========================================================================*
- 27498 * panic *
- 27499 *===========================================================================*/
- 27500 PUBLIC void panic(format, num)
- 27501 char *format; /* format string */
- 27502 int num; /* number to go with format string */
- 27503 {
- 27504 /* Something awful has happened. Panics are caused when an internal
- 27505 * inconsistency is detected, e.g., a programming error or illegal value of a
- 27506 * defined constant.
- 27507 */
- 27508
- 27509 if (panicking) return; /* do not panic during a sync */
- 27510 panicking = TRUE; /* prevent another panic during the sync */
- 27511 printf("File system panic: %s ", format);
- 27512 if (num != NO_NUM) printf("%d",num);
- 27513 printf("n");
- 27514 (void) do_sync(); /* flush everything to the disk */
- .Ep 380 src/fs/utility.c
- 27515 sys_abort(RBT_PANIC);
- 27516 }
-
-
- 27519 /*===========================================================================*
- 27520 * conv2 *
- 27521 *===========================================================================*/
- 27522 PUBLIC unsigned conv2(norm, w)
- 27523 int norm; /* TRUE if no swap, FALSE for byte swap */
- 27524 int w; /* promotion of 16-bit word to be swapped */
- 27525 {
- 27526 /* Possibly swap a 16-bit word between 8086 and 68000 byte order. */
- 27527
- 27528 if (norm) return( (unsigned) w & 0xFFFF);
- 27529 return( ((w&BYTE) << 8) | ( (w>>8) & BYTE));
- 27530 }
-
-
- 27533 /*===========================================================================*
- 27534 * conv4 *
- 27535 *===========================================================================*/
- 27536 PUBLIC long conv4(norm, x)
- 27537 int norm; /* TRUE if no swap, FALSE for byte swap */
- 27538 long x; /* 32-bit long to be byte swapped */
- 27539 {
- 27540 /* Possibly swap a 32-bit long between 8086 and 68000 byte order. */
- 27541
- 27542 unsigned lo, hi;
- 27543 long l;
- 27544
- 27545 if (norm) return(x); /* byte order was already ok */
- 27546 lo = conv2(FALSE, (int) x & 0xFFFF); /* low-order half, byte swapped */
- 27547 hi = conv2(FALSE, (int) (x>>16) & 0xFFFF); /* high-order half, swapped */
- 27548 l = ( (long) lo <<16) | hi;
- 27549 return(l);
- 27550 }
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- src/fs/putk.c
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- 27600 /* FS must occasionally print some message. It uses the standard library
- 27601 * routine prink(). (The name "printf" is really a macro defined as "printk").
- 27602 * Printing is done by calling the TTY task directly, not going through FS.
- 27603 */
- 27604
- 27605 #include "fs.h"
- 27606 #include <minix/com.h>
- 27607
- 27608 #define BUF_SIZE 100 /* print buffer size */
- 27609
- 27610 PRIVATE int buf_count; /* # characters in the buffer */
- 27611 PRIVATE char print_buf[BUF_SIZE]; /* output is buffered here */
- 27612 PRIVATE message putch_msg; /* used for message to TTY task */
- 27613
- 27614 FORWARD _PROTOTYPE( void flush, (void) );
- .Op 381 src/fs/putk.c
- 27615
- 27616 /*===========================================================================*
- 27617 * putk *
- 27618 *===========================================================================*/
- 27619 PUBLIC void putk(c)
- 27620 int c;
- 27621 {
- 27622 /* Accumulate another character. If 0 or buffer full, print it. */
- 27623
- 27624 if (c == 0 || buf_count == BUF_SIZE) flush();
- 27625 if (c == 'n') putk('r');
- 27626 if (c != 0) print_buf[buf_count++] = c;
- 27627 }
-
-
- 27630 /*===========================================================================*
- 27631 * flush *
- 27632 *===========================================================================*/
- 27633 PRIVATE void flush()
- 27634 {
- 27635 /* Flush the print buffer by calling TTY task. */
- 27636
- 27637
- 27638 if (buf_count == 0) return;
- 27639 putch_msg.m_type = DEV_WRITE;
- 27640 putch_msg.PROC_NR = 1;
- 27641 putch_msg.TTY_LINE = 0;
- 27642 putch_msg.ADDRESS = print_buf;
- 27643 putch_msg.COUNT = buf_count;
- 27644 call_task(TTY, &putch_msg);
- 27645 buf_count = 0;
- 27646 }
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- ./end_of_list
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++