| [9] | 1 | /* This file performs the MOUNT and UMOUNT system calls. | 
|---|
|  | 2 | * | 
|---|
|  | 3 | * The entry points into this file are | 
|---|
|  | 4 | *   do_mount:  perform the MOUNT system call | 
|---|
|  | 5 | *   do_umount: perform the UMOUNT system call | 
|---|
|  | 6 | */ | 
|---|
|  | 7 |  | 
|---|
|  | 8 | #include "fs.h" | 
|---|
|  | 9 | #include <fcntl.h> | 
|---|
|  | 10 | #include <string.h> | 
|---|
|  | 11 | #include <minix/com.h> | 
|---|
|  | 12 | #include <sys/stat.h> | 
|---|
|  | 13 | #include "buf.h" | 
|---|
|  | 14 | #include "file.h" | 
|---|
|  | 15 | #include "fproc.h" | 
|---|
|  | 16 | #include "inode.h" | 
|---|
|  | 17 | #include "param.h" | 
|---|
|  | 18 | #include "super.h" | 
|---|
|  | 19 |  | 
|---|
|  | 20 | /* Allow the root to be replaced before the first 'real' mount. */ | 
|---|
|  | 21 | PRIVATE int allow_newroot= 1; | 
|---|
|  | 22 |  | 
|---|
|  | 23 | FORWARD _PROTOTYPE( dev_t name_to_dev, (char *path)                     ); | 
|---|
|  | 24 |  | 
|---|
|  | 25 | /*===========================================================================* | 
|---|
|  | 26 | *                              do_mount                                     * | 
|---|
|  | 27 | *===========================================================================*/ | 
|---|
|  | 28 | PUBLIC int do_mount() | 
|---|
|  | 29 | { | 
|---|
|  | 30 | /* Perform the mount(name, mfile, rd_only) system call. */ | 
|---|
|  | 31 |  | 
|---|
|  | 32 | register struct inode *rip, *root_ip; | 
|---|
|  | 33 | struct super_block *xp, *sp; | 
|---|
|  | 34 | dev_t dev; | 
|---|
|  | 35 | mode_t bits; | 
|---|
|  | 36 | int rdir, mdir;               /* TRUE iff {root|mount} file is dir */ | 
|---|
|  | 37 | int i, r, found; | 
|---|
|  | 38 | struct fproc *tfp; | 
|---|
|  | 39 |  | 
|---|
|  | 40 | /* Only the super-user may do MOUNT. */ | 
|---|
|  | 41 | if (!super_user) return(EPERM); | 
|---|
|  | 42 |  | 
|---|
|  | 43 | /* If 'name' is not for a block special file, return error. */ | 
|---|
|  | 44 | if (fetch_name(m_in.name1, m_in.name1_length, M1) != OK) return(err_code); | 
|---|
|  | 45 | if ( (dev = name_to_dev(user_path)) == NO_DEV) return(err_code); | 
|---|
|  | 46 |  | 
|---|
|  | 47 | /* Scan super block table to see if dev already mounted & find a free slot.*/ | 
|---|
|  | 48 | sp = NIL_SUPER; | 
|---|
|  | 49 | found = FALSE; | 
|---|
|  | 50 | for (xp = &super_block[0]; xp < &super_block[NR_SUPERS]; xp++) { | 
|---|
|  | 51 | if (xp->s_dev == dev) | 
|---|
|  | 52 | { | 
|---|
|  | 53 | /* is it mounted already? */ | 
|---|
|  | 54 | found = TRUE; | 
|---|
|  | 55 | sp= xp; | 
|---|
|  | 56 | break; | 
|---|
|  | 57 | } | 
|---|
|  | 58 | if (xp->s_dev == NO_DEV) sp = xp;       /* record free slot */ | 
|---|
|  | 59 | } | 
|---|
|  | 60 | if (found) | 
|---|
|  | 61 | { | 
|---|
|  | 62 | printf( | 
|---|
|  | 63 | "do_mount: s_imount = 0x%x (%x, %d), s_isup = 0x%x (%x, %d), fp_rootdir = 0x%x\n", | 
|---|
|  | 64 | xp->s_imount, xp->s_imount->i_dev, xp->s_imount->i_num, | 
|---|
|  | 65 | xp->s_isup, xp->s_isup->i_dev, xp->s_isup->i_num, | 
|---|
|  | 66 | fproc[FS_PROC_NR].fp_rootdir); | 
|---|
|  | 67 | /* It is possible that we have an old root lying around that | 
|---|
|  | 68 | * needs to be remounted. | 
|---|
|  | 69 | */ | 
|---|
|  | 70 | if (xp->s_imount != xp->s_isup || | 
|---|
|  | 71 | xp->s_isup == fproc[FS_PROC_NR].fp_rootdir) | 
|---|
|  | 72 | { | 
|---|
|  | 73 | /* Normally, s_imount refers to the mount point. For a root | 
|---|
|  | 74 | * filesystem, s_imount is equal to the root inode. We assume | 
|---|
|  | 75 | * that the root of FS is always the real root. If the two | 
|---|
|  | 76 | * inodes are different or if the root of FS is equal two the | 
|---|
|  | 77 | * root of the filesystem we found, we found a filesystem that | 
|---|
|  | 78 | * is in use. | 
|---|
|  | 79 | */ | 
|---|
|  | 80 | return(EBUSY);  /* already mounted */ | 
|---|
|  | 81 | } | 
|---|
|  | 82 |  | 
|---|
|  | 83 | if (root_dev == xp->s_dev) | 
|---|
|  | 84 | { | 
|---|
|  | 85 | panic("fs", "inconsistency remounting old root", | 
|---|
|  | 86 | NO_NUM); | 
|---|
|  | 87 | } | 
|---|
|  | 88 |  | 
|---|
|  | 89 | /* Now get the inode of the file to be mounted on. */ | 
|---|
|  | 90 | if (fetch_name(m_in.name2, m_in.name2_length, M1) != OK) { | 
|---|
|  | 91 | return(err_code); | 
|---|
|  | 92 | } | 
|---|
|  | 93 |  | 
|---|
|  | 94 | if ( (rip = eat_path(user_path)) == NIL_INODE) { | 
|---|
|  | 95 | return(err_code); | 
|---|
|  | 96 | } | 
|---|
|  | 97 |  | 
|---|
|  | 98 | r = OK; | 
|---|
|  | 99 |  | 
|---|
|  | 100 | /* It may not be special. */ | 
|---|
|  | 101 | bits = rip->i_mode & I_TYPE; | 
|---|
|  | 102 | if (bits == I_BLOCK_SPECIAL || bits == I_CHAR_SPECIAL) | 
|---|
|  | 103 | r = ENOTDIR; | 
|---|
|  | 104 |  | 
|---|
|  | 105 | /* Get the root inode of the mounted file system. */ | 
|---|
|  | 106 | root_ip= sp->s_isup; | 
|---|
|  | 107 |  | 
|---|
|  | 108 | /* File types of 'rip' and 'root_ip' may not conflict. */ | 
|---|
|  | 109 | if (r == OK) { | 
|---|
|  | 110 | mdir = ((rip->i_mode & I_TYPE) == I_DIRECTORY); | 
|---|
|  | 111 | /* TRUE iff dir */ | 
|---|
|  | 112 | rdir = ((root_ip->i_mode & I_TYPE) == I_DIRECTORY); | 
|---|
|  | 113 | if (!mdir && rdir) r = EISDIR; | 
|---|
|  | 114 | } | 
|---|
|  | 115 |  | 
|---|
|  | 116 | /* If error, return the mount point. */ | 
|---|
|  | 117 | if (r != OK) { | 
|---|
|  | 118 | put_inode(rip); | 
|---|
|  | 119 | return(r); | 
|---|
|  | 120 | } | 
|---|
|  | 121 |  | 
|---|
|  | 122 | /* Nothing else can go wrong.  Perform the mount. */ | 
|---|
|  | 123 | rip->i_mount = I_MOUNT; /* this bit says the inode is | 
|---|
|  | 124 | * mounted on | 
|---|
|  | 125 | */ | 
|---|
|  | 126 | put_inode(sp->s_imount); | 
|---|
|  | 127 | sp->s_imount = rip; | 
|---|
|  | 128 | sp->s_rd_only = m_in.rd_only; | 
|---|
|  | 129 | allow_newroot= 0;               /* The root is now fixed */ | 
|---|
|  | 130 | return(OK); | 
|---|
|  | 131 | } | 
|---|
|  | 132 | if (sp == NIL_SUPER) return(ENFILE);  /* no super block available */ | 
|---|
|  | 133 |  | 
|---|
|  | 134 | /* Open the device the file system lives on. */ | 
|---|
|  | 135 | if (dev_open(dev, who_e, m_in.rd_only ? R_BIT : (R_BIT|W_BIT)) != OK) | 
|---|
|  | 136 | return(EINVAL); | 
|---|
|  | 137 |  | 
|---|
|  | 138 | /* Make the cache forget about blocks it has open on the filesystem */ | 
|---|
|  | 139 | (void) do_sync(); | 
|---|
|  | 140 | invalidate(dev); | 
|---|
|  | 141 |  | 
|---|
|  | 142 | /* Fill in the super block. */ | 
|---|
|  | 143 | sp->s_dev = dev;              /* read_super() needs to know which dev */ | 
|---|
|  | 144 | r = read_super(sp); | 
|---|
|  | 145 |  | 
|---|
|  | 146 | /* Is it recognized as a Minix filesystem? */ | 
|---|
|  | 147 | if (r != OK) { | 
|---|
|  | 148 | dev_close(dev); | 
|---|
|  | 149 | sp->s_dev = NO_DEV; | 
|---|
|  | 150 | return(r); | 
|---|
|  | 151 | } | 
|---|
|  | 152 |  | 
|---|
|  | 153 | /* Now get the inode of the file to be mounted on. */ | 
|---|
|  | 154 | if (fetch_name(m_in.name2, m_in.name2_length, M1) != OK) { | 
|---|
|  | 155 | dev_close(dev); | 
|---|
|  | 156 | sp->s_dev = NO_DEV; | 
|---|
|  | 157 | return(err_code); | 
|---|
|  | 158 | } | 
|---|
|  | 159 |  | 
|---|
|  | 160 | if (strcmp(user_path, "/") == 0 && allow_newroot) | 
|---|
|  | 161 | { | 
|---|
|  | 162 | printf("Replacing root\n"); | 
|---|
|  | 163 |  | 
|---|
|  | 164 | /* Get the root inode of the mounted file system. */ | 
|---|
|  | 165 | if ( (root_ip = get_inode(dev, ROOT_INODE)) == NIL_INODE) r = err_code; | 
|---|
|  | 166 | if (root_ip != NIL_INODE && root_ip->i_mode == 0) { | 
|---|
|  | 167 | r = EINVAL; | 
|---|
|  | 168 | } | 
|---|
|  | 169 |  | 
|---|
|  | 170 | /* If error, return the super block and both inodes; release the | 
|---|
|  | 171 | * maps. | 
|---|
|  | 172 | */ | 
|---|
|  | 173 | if (r != OK) { | 
|---|
|  | 174 | put_inode(root_ip); | 
|---|
|  | 175 | (void) do_sync(); | 
|---|
|  | 176 | invalidate(dev); | 
|---|
|  | 177 | dev_close(dev); | 
|---|
|  | 178 | sp->s_dev = NO_DEV; | 
|---|
|  | 179 | return(r); | 
|---|
|  | 180 | } | 
|---|
|  | 181 |  | 
|---|
|  | 182 | /* Nothing else can go wrong.  Perform the mount. */ | 
|---|
|  | 183 | sp->s_imount = root_ip; | 
|---|
|  | 184 | dup_inode(root_ip); | 
|---|
|  | 185 | sp->s_isup = root_ip; | 
|---|
|  | 186 | sp->s_rd_only = m_in.rd_only; | 
|---|
|  | 187 | root_dev= dev; | 
|---|
|  | 188 |  | 
|---|
|  | 189 | /* Replace all root and working directories */ | 
|---|
|  | 190 | for (i= 0, tfp= fproc; i<NR_PROCS; i++, tfp++) | 
|---|
|  | 191 | { | 
|---|
|  | 192 | if (tfp->fp_pid == PID_FREE) | 
|---|
|  | 193 | continue; | 
|---|
|  | 194 | if (tfp->fp_rootdir == NULL) | 
|---|
|  | 195 | panic("fs", "do_mount: null rootdir", i); | 
|---|
|  | 196 | put_inode(tfp->fp_rootdir); | 
|---|
|  | 197 | dup_inode(root_ip); | 
|---|
|  | 198 | tfp->fp_rootdir= root_ip; | 
|---|
|  | 199 |  | 
|---|
|  | 200 | if (tfp->fp_workdir == NULL) | 
|---|
|  | 201 | panic("fs", "do_mount: null workdir", i); | 
|---|
|  | 202 | put_inode(tfp->fp_workdir); | 
|---|
|  | 203 | dup_inode(root_ip); | 
|---|
|  | 204 | tfp->fp_workdir= root_ip; | 
|---|
|  | 205 | } | 
|---|
|  | 206 |  | 
|---|
|  | 207 | /* Leave the old filesystem lying around. */ | 
|---|
|  | 208 | return(OK); | 
|---|
|  | 209 | } | 
|---|
|  | 210 |  | 
|---|
|  | 211 | if ( (rip = eat_path(user_path)) == NIL_INODE) { | 
|---|
|  | 212 | dev_close(dev); | 
|---|
|  | 213 | sp->s_dev = NO_DEV; | 
|---|
|  | 214 | return(err_code); | 
|---|
|  | 215 | } | 
|---|
|  | 216 |  | 
|---|
|  | 217 | /* It may not be busy. */ | 
|---|
|  | 218 | r = OK; | 
|---|
|  | 219 | if (rip->i_count > 1) r = EBUSY; | 
|---|
|  | 220 |  | 
|---|
|  | 221 | /* It may not be special. */ | 
|---|
|  | 222 | bits = rip->i_mode & I_TYPE; | 
|---|
|  | 223 | if (bits == I_BLOCK_SPECIAL || bits == I_CHAR_SPECIAL) r = ENOTDIR; | 
|---|
|  | 224 |  | 
|---|
|  | 225 | /* Get the root inode of the mounted file system. */ | 
|---|
|  | 226 | root_ip = NIL_INODE;          /* if 'r' not OK, make sure this is defined */ | 
|---|
|  | 227 | if (r == OK) { | 
|---|
|  | 228 | if ( (root_ip = get_inode(dev, ROOT_INODE)) == NIL_INODE) r = err_code; | 
|---|
|  | 229 | } | 
|---|
|  | 230 | if (root_ip != NIL_INODE && root_ip->i_mode == 0) { | 
|---|
|  | 231 | r = EINVAL; | 
|---|
|  | 232 | } | 
|---|
|  | 233 |  | 
|---|
|  | 234 | /* File types of 'rip' and 'root_ip' may not conflict. */ | 
|---|
|  | 235 | if (r == OK) { | 
|---|
|  | 236 | mdir = ((rip->i_mode & I_TYPE) == I_DIRECTORY);  /* TRUE iff dir */ | 
|---|
|  | 237 | rdir = ((root_ip->i_mode & I_TYPE) == I_DIRECTORY); | 
|---|
|  | 238 | if (!mdir && rdir) r = EISDIR; | 
|---|
|  | 239 | } | 
|---|
|  | 240 |  | 
|---|
|  | 241 | /* If error, return the super block and both inodes; release the maps. */ | 
|---|
|  | 242 | if (r != OK) { | 
|---|
|  | 243 | put_inode(rip); | 
|---|
|  | 244 | put_inode(root_ip); | 
|---|
|  | 245 | (void) do_sync(); | 
|---|
|  | 246 | invalidate(dev); | 
|---|
|  | 247 | dev_close(dev); | 
|---|
|  | 248 | sp->s_dev = NO_DEV; | 
|---|
|  | 249 | return(r); | 
|---|
|  | 250 | } | 
|---|
|  | 251 |  | 
|---|
|  | 252 | /* Nothing else can go wrong.  Perform the mount. */ | 
|---|
|  | 253 | rip->i_mount = I_MOUNT;       /* this bit says the inode is mounted on */ | 
|---|
|  | 254 | sp->s_imount = rip; | 
|---|
|  | 255 | sp->s_isup = root_ip; | 
|---|
|  | 256 | sp->s_rd_only = m_in.rd_only; | 
|---|
|  | 257 | allow_newroot= 0;             /* The root is now fixed */ | 
|---|
|  | 258 | return(OK); | 
|---|
|  | 259 | } | 
|---|
|  | 260 |  | 
|---|
|  | 261 | /*===========================================================================* | 
|---|
|  | 262 | *                              do_umount                                    * | 
|---|
|  | 263 | *===========================================================================*/ | 
|---|
|  | 264 | PUBLIC int do_umount() | 
|---|
|  | 265 | { | 
|---|
|  | 266 | /* Perform the umount(name) system call. */ | 
|---|
|  | 267 | dev_t dev; | 
|---|
|  | 268 |  | 
|---|
|  | 269 | /* Only the super-user may do UMOUNT. */ | 
|---|
|  | 270 | if (!super_user) return(EPERM); | 
|---|
|  | 271 |  | 
|---|
|  | 272 | /* If 'name' is not for a block special file, return error. */ | 
|---|
|  | 273 | if (fetch_name(m_in.name, m_in.name_length, M3) != OK) return(err_code); | 
|---|
|  | 274 | if ( (dev = name_to_dev(user_path)) == NO_DEV) return(err_code); | 
|---|
|  | 275 |  | 
|---|
|  | 276 | return(unmount(dev)); | 
|---|
|  | 277 | } | 
|---|
|  | 278 |  | 
|---|
|  | 279 | /*===========================================================================* | 
|---|
|  | 280 | *                              unmount                                      * | 
|---|
|  | 281 | *===========================================================================*/ | 
|---|
|  | 282 | PUBLIC int unmount(dev) | 
|---|
|  | 283 | Dev_t dev; | 
|---|
|  | 284 | { | 
|---|
|  | 285 | /* Unmount a file system by device number. */ | 
|---|
|  | 286 | register struct inode *rip; | 
|---|
|  | 287 | struct super_block *sp, *sp1; | 
|---|
|  | 288 | int count; | 
|---|
|  | 289 |  | 
|---|
|  | 290 | /* See if the mounted device is busy.  Only 1 inode using it should be | 
|---|
|  | 291 | * open -- the root inode -- and that inode only 1 time. | 
|---|
|  | 292 | */ | 
|---|
|  | 293 | count = 0; | 
|---|
|  | 294 | for (rip = &inode[0]; rip< &inode[NR_INODES]; rip++) | 
|---|
|  | 295 | if (rip->i_count > 0 && rip->i_dev == dev) count += rip->i_count; | 
|---|
|  | 296 | if (count > 1) return(EBUSY); /* can't umount a busy file system */ | 
|---|
|  | 297 |  | 
|---|
|  | 298 | /* Find the super block. */ | 
|---|
|  | 299 | sp = NIL_SUPER; | 
|---|
|  | 300 | for (sp1 = &super_block[0]; sp1 < &super_block[NR_SUPERS]; sp1++) { | 
|---|
|  | 301 | if (sp1->s_dev == dev) { | 
|---|
|  | 302 | sp = sp1; | 
|---|
|  | 303 | break; | 
|---|
|  | 304 | } | 
|---|
|  | 305 | } | 
|---|
|  | 306 |  | 
|---|
|  | 307 | /* Sync the disk, and invalidate cache. */ | 
|---|
|  | 308 | (void) do_sync();             /* force any cached blocks out of memory */ | 
|---|
|  | 309 | invalidate(dev);              /* invalidate cache entries for this dev */ | 
|---|
|  | 310 | if (sp == NIL_SUPER) { | 
|---|
|  | 311 | return(EINVAL); | 
|---|
|  | 312 | } | 
|---|
|  | 313 |  | 
|---|
|  | 314 | /* Close the device the file system lives on. */ | 
|---|
|  | 315 | dev_close(dev); | 
|---|
|  | 316 |  | 
|---|
|  | 317 | /* Finish off the unmount. */ | 
|---|
|  | 318 | sp->s_imount->i_mount = NO_MOUNT;     /* inode returns to normal */ | 
|---|
|  | 319 | put_inode(sp->s_imount);      /* release the inode mounted on */ | 
|---|
|  | 320 | put_inode(sp->s_isup);        /* release the root inode of the mounted fs */ | 
|---|
|  | 321 | sp->s_imount = NIL_INODE; | 
|---|
|  | 322 | sp->s_dev = NO_DEV; | 
|---|
|  | 323 | return(OK); | 
|---|
|  | 324 | } | 
|---|
|  | 325 |  | 
|---|
|  | 326 | /*===========================================================================* | 
|---|
|  | 327 | *                              name_to_dev                                  * | 
|---|
|  | 328 | *===========================================================================*/ | 
|---|
|  | 329 | PRIVATE dev_t name_to_dev(path) | 
|---|
|  | 330 | char *path;                     /* pointer to path name */ | 
|---|
|  | 331 | { | 
|---|
|  | 332 | /* Convert the block special file 'path' to a device number.  If 'path' | 
|---|
|  | 333 | * is not a block special file, return error code in 'err_code'. | 
|---|
|  | 334 | */ | 
|---|
|  | 335 |  | 
|---|
|  | 336 | register struct inode *rip; | 
|---|
|  | 337 | register dev_t dev; | 
|---|
|  | 338 |  | 
|---|
|  | 339 | /* If 'path' can't be opened, give up immediately. */ | 
|---|
|  | 340 | if ( (rip = eat_path(path)) == NIL_INODE) return(NO_DEV); | 
|---|
|  | 341 |  | 
|---|
|  | 342 | /* If 'path' is not a block special file, return error. */ | 
|---|
|  | 343 | if ( (rip->i_mode & I_TYPE) != I_BLOCK_SPECIAL) { | 
|---|
|  | 344 | err_code = ENOTBLK; | 
|---|
|  | 345 | put_inode(rip); | 
|---|
|  | 346 | return(NO_DEV); | 
|---|
|  | 347 | } | 
|---|
|  | 348 |  | 
|---|
|  | 349 | /* Extract the device number. */ | 
|---|
|  | 350 | dev = (dev_t) rip->i_zone[0]; | 
|---|
|  | 351 | put_inode(rip); | 
|---|
|  | 352 | return(dev); | 
|---|
|  | 353 | } | 
|---|