source: branches/minix3-book/servers/fs/open.c@ 4

Last change on this file since 4 was 4, checked in by Mattia Monga, 13 years ago

Importazione sorgenti libro

File size: 15.5 KB
Line 
1/* This file contains the procedures for creating, opening, closing, and
2 * seeking on files.
3 *
4 * The entry points into this file are
5 * do_creat: perform the CREAT system call
6 * do_open: perform the OPEN system call
7 * do_mknod: perform the MKNOD system call
8 * do_mkdir: perform the MKDIR system call
9 * do_close: perform the CLOSE system call
10 * do_lseek: perform the LSEEK system call
11 */
12
13#include "fs.h"
14#include <sys/stat.h>
15#include <fcntl.h>
16#include <minix/callnr.h>
17#include <minix/com.h>
18#include "buf.h"
19#include "file.h"
20#include "fproc.h"
21#include "inode.h"
22#include "lock.h"
23#include "param.h"
24#include "super.h"
25
26#define offset m2_l1
27
28PRIVATE char mode_map[] = {R_BIT, W_BIT, R_BIT|W_BIT, 0};
29
30FORWARD _PROTOTYPE( int common_open, (int oflags, mode_t omode) );
31FORWARD _PROTOTYPE( int pipe_open, (struct inode *rip,mode_t bits,int oflags));
32FORWARD _PROTOTYPE( struct inode *new_node, (char *path, mode_t bits,
33 zone_t z0) );
34
35/*===========================================================================*
36 * do_creat *
37 *===========================================================================*/
38PUBLIC int do_creat()
39{
40/* Perform the creat(name, mode) system call. */
41 int r;
42
43 if (fetch_name(m_in.name, m_in.name_length, M3) != OK) return(err_code);
44 r = common_open(O_WRONLY | O_CREAT | O_TRUNC, (mode_t) m_in.mode);
45 return(r);
46}
47
48/*===========================================================================*
49 * do_open *
50 *===========================================================================*/
51PUBLIC int do_open()
52{
53/* Perform the open(name, flags,...) system call. */
54
55 int create_mode = 0; /* is really mode_t but this gives problems */
56 int r;
57
58 /* If O_CREAT is set, open has three parameters, otherwise two. */
59 if (m_in.mode & O_CREAT) {
60 create_mode = m_in.c_mode;
61 r = fetch_name(m_in.c_name, m_in.name1_length, M1);
62 } else {
63 r = fetch_name(m_in.name, m_in.name_length, M3);
64 }
65
66 if (r != OK) return(err_code); /* name was bad */
67 r = common_open(m_in.mode, create_mode);
68 return(r);
69}
70
71/*===========================================================================*
72 * common_open *
73 *===========================================================================*/
74PRIVATE int common_open(register int oflags, mode_t omode)
75{
76/* Common code from do_creat and do_open. */
77
78 register struct inode *rip;
79 int r, b, exist = TRUE;
80 dev_t dev;
81 mode_t bits;
82 off_t pos;
83 struct filp *fil_ptr, *filp2;
84
85 /* Remap the bottom two bits of oflags. */
86 bits = (mode_t) mode_map[oflags & O_ACCMODE];
87
88 /* See if file descriptor and filp slots are available. */
89 if ( (r = get_fd(0, bits, &m_in.fd, &fil_ptr)) != OK) return(r);
90
91 /* If O_CREATE is set, try to make the file. */
92 if (oflags & O_CREAT) {
93 /* Create a new inode by calling new_node(). */
94 omode = I_REGULAR | (omode & ALL_MODES & fp->fp_umask);
95 rip = new_node(user_path, omode, NO_ZONE);
96 r = err_code;
97 if (r == OK) exist = FALSE; /* we just created the file */
98 else if (r != EEXIST) return(r); /* other error */
99 else exist = !(oflags & O_EXCL); /* file exists, if the O_EXCL
100 flag is set this is an error */
101 } else {
102 /* Scan path name. */
103 if ( (rip = eat_path(user_path)) == NIL_INODE) return(err_code);
104 }
105
106 /* Claim the file descriptor and filp slot and fill them in. */
107 fp->fp_filp[m_in.fd] = fil_ptr;
108 fil_ptr->filp_count = 1;
109 fil_ptr->filp_ino = rip;
110 fil_ptr->filp_flags = oflags;
111
112 /* Only do the normal open code if we didn't just create the file. */
113 if (exist) {
114 /* Check protections. */
115 if ((r = forbidden(rip, bits)) == OK) {
116 /* Opening reg. files directories and special files differ. */
117 switch (rip->i_mode & I_TYPE) {
118 case I_REGULAR:
119 /* Truncate regular file if O_TRUNC. */
120 if (oflags & O_TRUNC) {
121 if ((r = forbidden(rip, W_BIT)) !=OK) break;
122 truncate(rip);
123 wipe_inode(rip);
124 /* Send the inode from the inode cache to the
125 * block cache, so it gets written on the next
126 * cache flush.
127 */
128 rw_inode(rip, WRITING);
129 }
130 break;
131
132 case I_DIRECTORY:
133 /* Directories may be read but not written. */
134 r = (bits & W_BIT ? EISDIR : OK);
135 break;
136
137 case I_CHAR_SPECIAL:
138 case I_BLOCK_SPECIAL:
139 /* Invoke the driver for special processing. */
140 dev = (dev_t) rip->i_zone[0];
141 r = dev_open(dev, who, bits | (oflags & ~O_ACCMODE));
142 break;
143
144 case I_NAMED_PIPE:
145 oflags |= O_APPEND; /* force append mode */
146 fil_ptr->filp_flags = oflags;
147 r = pipe_open(rip, bits, oflags);
148 if (r != ENXIO) {
149 /* See if someone else is doing a rd or wt on
150 * the FIFO. If so, use its filp entry so the
151 * file position will be automatically shared.
152 */
153 b = (bits & R_BIT ? R_BIT : W_BIT);
154 fil_ptr->filp_count = 0; /* don't find self */
155 if ((filp2 = find_filp(rip, b)) != NIL_FILP) {
156 /* Co-reader or writer found. Use it.*/
157 fp->fp_filp[m_in.fd] = filp2;
158 filp2->filp_count++;
159 filp2->filp_ino = rip;
160 filp2->filp_flags = oflags;
161
162 /* i_count was incremented incorrectly
163 * by eatpath above, not knowing that
164 * we were going to use an existing
165 * filp entry. Correct this error.
166 */
167 rip->i_count--;
168 } else {
169 /* Nobody else found. Restore filp. */
170 fil_ptr->filp_count = 1;
171 if (b == R_BIT)
172 pos = rip->i_zone[V2_NR_DZONES+0];
173 else
174 pos = rip->i_zone[V2_NR_DZONES+1];
175 fil_ptr->filp_pos = pos;
176 }
177 }
178 break;
179 }
180 }
181 }
182
183 /* If error, release inode. */
184 if (r != OK) {
185 if (r == SUSPEND) return(r); /* Oops, just suspended */
186 fp->fp_filp[m_in.fd] = NIL_FILP;
187 fil_ptr->filp_count= 0;
188 put_inode(rip);
189 return(r);
190 }
191
192 return(m_in.fd);
193}
194
195/*===========================================================================*
196 * new_node *
197 *===========================================================================*/
198PRIVATE struct inode *new_node(char *path, mode_t bits, zone_t z0)
199{
200/* New_node() is called by common_open(), do_mknod(), and do_mkdir().
201 * In all cases it allocates a new inode, makes a directory entry for it on
202 * the path 'path', and initializes it. It returns a pointer to the inode if
203 * it can do this; otherwise it returns NIL_INODE. It always sets 'err_code'
204 * to an appropriate value (OK or an error code).
205 */
206
207 register struct inode *rlast_dir_ptr, *rip;
208 register int r;
209 char string[NAME_MAX];
210
211 /* See if the path can be opened down to the last directory. */
212 if ((rlast_dir_ptr = last_dir(path, string)) == NIL_INODE) return(NIL_INODE);
213
214 /* The final directory is accessible. Get final component of the path. */
215 rip = advance(rlast_dir_ptr, string);
216 if ( rip == NIL_INODE && err_code == ENOENT) {
217 /* Last path component does not exist. Make new directory entry. */
218 if ( (rip = alloc_inode(rlast_dir_ptr->i_dev, bits)) == NIL_INODE) {
219 /* Can't creat new inode: out of inodes. */
220 put_inode(rlast_dir_ptr);
221 return(NIL_INODE);
222 }
223
224 /* Force inode to the disk before making directory entry to make
225 * the system more robust in the face of a crash: an inode with
226 * no directory entry is much better than the opposite.
227 */
228 rip->i_nlinks++;
229 rip->i_zone[0] = z0; /* major/minor device numbers */
230 rw_inode(rip, WRITING); /* force inode to disk now */
231
232 /* New inode acquired. Try to make directory entry. */
233 if ((r = search_dir(rlast_dir_ptr, string, &rip->i_num,ENTER)) != OK) {
234 put_inode(rlast_dir_ptr);
235 rip->i_nlinks--; /* pity, have to free disk inode */
236 rip->i_dirt = DIRTY; /* dirty inodes are written out */
237 put_inode(rip); /* this call frees the inode */
238 err_code = r;
239 return(NIL_INODE);
240 }
241
242 } else {
243 /* Either last component exists, or there is some problem. */
244 if (rip != NIL_INODE)
245 r = EEXIST;
246 else
247 r = err_code;
248 }
249
250 /* Return the directory inode and exit. */
251 put_inode(rlast_dir_ptr);
252 err_code = r;
253 return(rip);
254}
255
256/*===========================================================================*
257 * pipe_open *
258 *===========================================================================*/
259PRIVATE int pipe_open(register struct inode *rip, register mode_t bits,
260 register int oflags)
261{
262/* This function is called from common_open. It checks if
263 * there is at least one reader/writer pair for the pipe, if not
264 * it suspends the caller, otherwise it revives all other blocked
265 * processes hanging on the pipe.
266 */
267
268 rip->i_pipe = I_PIPE;
269 if (find_filp(rip, bits & W_BIT ? R_BIT : W_BIT) == NIL_FILP) {
270 if (oflags & O_NONBLOCK) {
271 if (bits & W_BIT) return(ENXIO);
272 } else {
273 suspend(XPOPEN); /* suspend caller */
274 return(SUSPEND);
275 }
276 } else if (susp_count > 0) {/* revive blocked processes */
277 release(rip, OPEN, susp_count);
278 release(rip, CREAT, susp_count);
279 }
280 return(OK);
281}
282
283/*===========================================================================*
284 * do_mknod *
285 *===========================================================================*/
286PUBLIC int do_mknod()
287{
288/* Perform the mknod(name, mode, addr) system call. */
289
290 register mode_t bits, mode_bits;
291 struct inode *ip;
292
293 /* Only the super_user may make nodes other than fifos. */
294 mode_bits = (mode_t) m_in.mk_mode; /* mode of the inode */
295 if (!super_user && ((mode_bits & I_TYPE) != I_NAMED_PIPE)) return(EPERM);
296 if (fetch_name(m_in.name1, m_in.name1_length, M1) != OK) return(err_code);
297 bits = (mode_bits & I_TYPE) | (mode_bits & ALL_MODES & fp->fp_umask);
298 ip = new_node(user_path, bits, (zone_t) m_in.mk_z0);
299 put_inode(ip);
300 return(err_code);
301}
302
303/*===========================================================================*
304 * do_mkdir *
305 *===========================================================================*/
306PUBLIC int do_mkdir()
307{
308/* Perform the mkdir(name, mode) system call. */
309
310 int r1, r2; /* status codes */
311 ino_t dot, dotdot; /* inode numbers for . and .. */
312 mode_t bits; /* mode bits for the new inode */
313 char string[NAME_MAX]; /* last component of the new dir's path name */
314 register struct inode *rip, *ldirp;
315
316 /* Check to see if it is possible to make another link in the parent dir. */
317 if (fetch_name(m_in.name1, m_in.name1_length, M1) != OK) return(err_code);
318 ldirp = last_dir(user_path, string); /* pointer to new dir's parent */
319 if (ldirp == NIL_INODE) return(err_code);
320 if (ldirp->i_nlinks >= (ldirp->i_sp->s_version == V1 ?
321 CHAR_MAX : SHRT_MAX)) {
322 put_inode(ldirp); /* return parent */
323 return(EMLINK);
324 }
325
326 /* Next make the inode. If that fails, return error code. */
327 bits = I_DIRECTORY | (m_in.mode & RWX_MODES & fp->fp_umask);
328 rip = new_node(user_path, bits, (zone_t) 0);
329 if (rip == NIL_INODE || err_code == EEXIST) {
330 put_inode(rip); /* can't make dir: it already exists */
331 put_inode(ldirp); /* return parent too */
332 return(err_code);
333 }
334
335 /* Get the inode numbers for . and .. to enter in the directory. */
336 dotdot = ldirp->i_num; /* parent's inode number */
337 dot = rip->i_num; /* inode number of the new dir itself */
338
339 /* Now make dir entries for . and .. unless the disk is completely full. */
340 /* Use dot1 and dot2, so the mode of the directory isn't important. */
341 rip->i_mode = bits; /* set mode */
342 r1 = search_dir(rip, dot1, &dot, ENTER); /* enter . in the new dir */
343 r2 = search_dir(rip, dot2, &dotdot, ENTER); /* enter .. in the new dir */
344
345 /* If both . and .. were successfully entered, increment the link counts. */
346 if (r1 == OK && r2 == OK) {
347 /* Normal case. It was possible to enter . and .. in the new dir. */
348 rip->i_nlinks++; /* this accounts for . */
349 ldirp->i_nlinks++; /* this accounts for .. */
350 ldirp->i_dirt = DIRTY; /* mark parent's inode as dirty */
351 } else {
352 /* It was not possible to enter . or .. probably disk was full. */
353 (void) search_dir(ldirp, string, (ino_t *) 0, DELETE);
354 rip->i_nlinks--; /* undo the increment done in new_node() */
355 }
356 rip->i_dirt = DIRTY; /* either way, i_nlinks has changed */
357
358 put_inode(ldirp); /* return the inode of the parent dir */
359 put_inode(rip); /* return the inode of the newly made dir */
360 return(err_code); /* new_node() always sets 'err_code' */
361}
362
363/*===========================================================================*
364 * do_close *
365 *===========================================================================*/
366PUBLIC int do_close()
367{
368/* Perform the close(fd) system call. */
369
370 register struct filp *rfilp;
371 register struct inode *rip;
372 struct file_lock *flp;
373 int rw, mode_word, lock_count;
374 dev_t dev;
375
376 /* First locate the inode that belongs to the file descriptor. */
377 if ( (rfilp = get_filp(m_in.fd)) == NIL_FILP) return(err_code);
378 rip = rfilp->filp_ino; /* 'rip' points to the inode */
379
380 if (rfilp->filp_count - 1 == 0 && rfilp->filp_mode != FILP_CLOSED) {
381 /* Check to see if the file is special. */
382 mode_word = rip->i_mode & I_TYPE;
383 if (mode_word == I_CHAR_SPECIAL || mode_word == I_BLOCK_SPECIAL) {
384 dev = (dev_t) rip->i_zone[0];
385 if (mode_word == I_BLOCK_SPECIAL) {
386 /* Invalidate cache entries unless special is mounted
387 * or ROOT
388 */
389 if (!mounted(rip)) {
390 (void) do_sync(); /* purge cache */
391 invalidate(dev);
392 }
393 }
394 /* Do any special processing on device close. */
395 dev_close(dev);
396 }
397 }
398
399 /* If the inode being closed is a pipe, release everyone hanging on it. */
400 if (rip->i_pipe == I_PIPE) {
401 rw = (rfilp->filp_mode & R_BIT ? WRITE : READ);
402 release(rip, rw, NR_PROCS);
403 }
404
405 /* If a write has been done, the inode is already marked as DIRTY. */
406 if (--rfilp->filp_count == 0) {
407 if (rip->i_pipe == I_PIPE && rip->i_count > 1) {
408 /* Save the file position in the i-node in case needed later.
409 * The read and write positions are saved separately. The
410 * last 3 zones in the i-node are not used for (named) pipes.
411 */
412 if (rfilp->filp_mode == R_BIT)
413 rip->i_zone[V2_NR_DZONES+0] = (zone_t) rfilp->filp_pos;
414 else
415 rip->i_zone[V2_NR_DZONES+1] = (zone_t) rfilp->filp_pos;
416 }
417 put_inode(rip);
418 }
419
420 fp->fp_cloexec &= ~(1L << m_in.fd); /* turn off close-on-exec bit */
421 fp->fp_filp[m_in.fd] = NIL_FILP;
422
423 /* Check to see if the file is locked. If so, release all locks. */
424 if (nr_locks == 0) return(OK);
425 lock_count = nr_locks; /* save count of locks */
426 for (flp = &file_lock[0]; flp < &file_lock[NR_LOCKS]; flp++) {
427 if (flp->lock_type == 0) continue; /* slot not in use */
428 if (flp->lock_inode == rip && flp->lock_pid == fp->fp_pid) {
429 flp->lock_type = 0;
430 nr_locks--;
431 }
432 }
433 if (nr_locks < lock_count) lock_revive(); /* lock released */
434 return(OK);
435}
436
437/*===========================================================================*
438 * do_lseek *
439 *===========================================================================*/
440PUBLIC int do_lseek()
441{
442/* Perform the lseek(ls_fd, offset, whence) system call. */
443
444 register struct filp *rfilp;
445 register off_t pos;
446
447 /* Check to see if the file descriptor is valid. */
448 if ( (rfilp = get_filp(m_in.ls_fd)) == NIL_FILP) return(err_code);
449
450 /* No lseek on pipes. */
451 if (rfilp->filp_ino->i_pipe == I_PIPE) return(ESPIPE);
452
453 /* The value of 'whence' determines the start position to use. */
454 switch(m_in.whence) {
455 case 0: pos = 0; break;
456 case 1: pos = rfilp->filp_pos; break;
457 case 2: pos = rfilp->filp_ino->i_size; break;
458 default: return(EINVAL);
459 }
460
461 /* Check for overflow. */
462 if (((long)m_in.offset > 0) && ((long)(pos + m_in.offset) < (long)pos))
463 return(EINVAL);
464 if (((long)m_in.offset < 0) && ((long)(pos + m_in.offset) > (long)pos))
465 return(EINVAL);
466 pos = pos + m_in.offset;
467
468 if (pos != rfilp->filp_pos)
469 rfilp->filp_ino->i_seek = ISEEK; /* inhibit read ahead */
470 rfilp->filp_pos = pos;
471 m_out.reply_l1 = pos; /* insert the long into the output message */
472 return(OK);
473}
Note: See TracBrowser for help on using the repository browser.