8 #include <minix/vfsif.h>
14 static int freesp_inode(struct inode
*rip
, off_t st
, off_t end
);
15 static int remove_dir(struct inode
*rldirp
, struct inode
*rip
, char
16 dir_name
[MFS_NAME_MAX
]);
17 static int unlink_file(struct inode
*dirp
, struct inode
*rip
, char
18 file_name
[MFS_NAME_MAX
]);
19 static off_t
nextblock(off_t pos
, int zone_size
);
20 static void zerozone_half(struct inode
*rip
, off_t pos
, int half
, int
22 static void zerozone_range(struct inode
*rip
, off_t pos
, off_t len
);
24 /* Args to zerozone_half() */
29 /*===========================================================================*
31 *===========================================================================*/
32 int fs_link(ino_t dir_nr
, char *name
, ino_t ino_nr
)
34 /* Perform the link(name1, name2) system call. */
36 struct inode
*ip
, *rip
;
40 /* Temporarily open the file. */
41 if( (rip
= get_inode(fs_dev
, ino_nr
)) == NULL
)
44 /* Check to see if the file has maximum number of links already. */
46 if(rip
->i_nlinks
>= LINK_MAX
)
49 /* Linking to directories is too dangerous to allow. */
51 if( (rip
->i_mode
& I_TYPE
) == I_DIRECTORY
)
54 /* If error with 'name', return the inode. */
60 /* Temporarily open the last dir */
61 if( (ip
= get_inode(fs_dev
, dir_nr
)) == NULL
) {
66 if (ip
->i_nlinks
== NO_LINK
) { /* Dir does not actually exist */
72 /* If 'name2' exists in full (even if no space) set 'r' to error. */
73 if((new_ip
= advance(ip
, name
)) == NULL
) {
84 r
= search_dir(ip
, name
, &rip
->i_num
, ENTER
);
86 /* If success, register the linking. */
89 rip
->i_update
|= CTIME
;
93 /* Done. Release both inodes. */
100 /*===========================================================================*
102 *===========================================================================*/
103 int fs_unlink(ino_t dir_nr
, char *name
, int call
)
105 /* Perform the unlink(name) or rmdir(name) system call. The code for these two
106 * is almost the same. They differ only in some condition testing.
108 register struct inode
*rip
;
109 struct inode
*rldirp
;
112 /* Temporarily open the dir. */
113 if((rldirp
= get_inode(fs_dev
, dir_nr
)) == NULL
)
116 /* The last directory exists. Does the file also exist? */
117 rip
= advance(rldirp
, name
);
120 /* If error, return inode. */
125 if (rip
->i_mountpoint
) {
131 if(rip
->i_sp
->s_rd_only
) {
133 } else if (call
== FSC_UNLINK
) {
134 if( (rip
->i_mode
& I_TYPE
) == I_DIRECTORY
) r
= EPERM
;
136 /* Actually try to unlink the file; fails if parent is mode 0 etc. */
137 if (r
== OK
) r
= unlink_file(rldirp
, rip
, name
);
139 r
= remove_dir(rldirp
, rip
, name
); /* call is RMDIR */
142 /* If unlink was possible, it has been done, otherwise it has not. */
149 /*===========================================================================*
151 *===========================================================================*/
152 ssize_t
fs_rdlink(ino_t ino_nr
, struct fsdriver_data
*data
, size_t bytes
)
154 struct buf
*bp
; /* buffer containing link text */
155 register struct inode
*rip
; /* target inode */
156 register int r
; /* return value */
158 /* Temporarily open the file. */
159 if( (rip
= get_inode(fs_dev
, ino_nr
)) == NULL
)
162 if(!S_ISLNK(rip
->i_mode
))
165 if(!(bp
= get_block_map(rip
, 0)))
167 /* Passed all checks */
168 if (bytes
> rip
->i_size
)
170 r
= fsdriver_copyout(data
, 0, b_data(bp
), bytes
);
181 /*===========================================================================*
183 *===========================================================================*/
184 static int remove_dir(rldirp
, rip
, dir_name
)
185 struct inode
*rldirp
; /* parent directory */
186 struct inode
*rip
; /* directory to be removed */
187 char dir_name
[MFS_NAME_MAX
]; /* name of directory to be removed */
189 /* A directory file has to be removed. Five conditions have to met:
190 * - The file must be a directory
191 * - The directory must be empty (except for . and ..)
192 * - The final component of the path must not be . or ..
193 * - The directory must not be the root of a mounted file system (VFS)
194 * - The directory must not be anybody's root/working directory (VFS)
198 /* search_dir checks that rip is a directory too. */
199 if ((r
= search_dir(rip
, "", NULL
, IS_EMPTY
)) != OK
)
202 if (rip
->i_num
== ROOT_INODE
) return(EBUSY
); /* can't remove 'root' */
204 /* Actually try to unlink the file; fails if parent is mode 0 etc. */
205 if ((r
= unlink_file(rldirp
, rip
, dir_name
)) != OK
) return r
;
207 /* Unlink . and .. from the dir. The super user can link and unlink any dir,
208 * so don't make too many assumptions about them.
210 (void) unlink_file(rip
, NULL
, ".");
211 (void) unlink_file(rip
, NULL
, "..");
216 /*===========================================================================*
218 *===========================================================================*/
219 static int unlink_file(dirp
, rip
, file_name
)
220 struct inode
*dirp
; /* parent directory of file */
221 struct inode
*rip
; /* inode of file, may be NULL too. */
222 char file_name
[MFS_NAME_MAX
]; /* name of file to be removed */
224 /* Unlink 'file_name'; rip must be the inode of 'file_name' or NULL. */
226 ino_t numb
; /* inode number */
229 /* If rip is not NULL, it is used to get faster access to the inode. */
231 /* Search for file in directory and try to get its inode. */
232 err_code
= search_dir(dirp
, file_name
, &numb
, LOOK_UP
);
233 if (err_code
== OK
) rip
= get_inode(dirp
->i_dev
, (int) numb
);
234 if (err_code
!= OK
|| rip
== NULL
) return(err_code
);
236 dup_inode(rip
); /* inode will be returned with put_inode */
239 r
= search_dir(dirp
, file_name
, NULL
, DELETE
);
242 rip
->i_nlinks
--; /* entry deleted from parent's dir */
243 rip
->i_update
|= CTIME
;
252 /*===========================================================================*
254 *===========================================================================*/
255 int fs_rename(ino_t old_dir_nr
, char *old_name
, ino_t new_dir_nr
,
258 /* Perform the rename(name1, name2) system call. */
259 struct inode
*old_dirp
, *old_ip
; /* ptrs to old dir, file inodes */
260 struct inode
*new_dirp
, *new_ip
; /* ptrs to new dir, file inodes */
261 struct inode
*new_superdirp
, *next_new_superdirp
;
262 int r
= OK
; /* error flag; initially no error */
263 int odir
, ndir
; /* TRUE iff {old|new} file is dir */
264 int same_pdir
; /* TRUE iff parent dirs are the same */
267 /* Get old dir inode */
268 if ((old_dirp
= get_inode(fs_dev
, old_dir_nr
)) == NULL
)
271 old_ip
= advance(old_dirp
, old_name
);
274 if (old_ip
== NULL
) {
279 if (old_ip
->i_mountpoint
) {
285 /* Get new dir inode */
286 if ((new_dirp
= get_inode(fs_dev
, new_dir_nr
)) == NULL
) {
291 if (new_dirp
->i_nlinks
== NO_LINK
) { /* Dir does not actually exist */
299 new_ip
= advance(new_dirp
, new_name
); /* not required to exist */
301 /* If the node does exist, make sure it's not a mountpoint. */
302 if (new_ip
!= NULL
&& new_ip
->i_mountpoint
) {
308 odir
= ((old_ip
->i_mode
& I_TYPE
) == I_DIRECTORY
); /* TRUE iff dir */
310 /* If it is ok, check for a variety of possible errors. */
312 same_pdir
= (old_dirp
== new_dirp
);
314 /* The old inode must not be a superdirectory of the new last dir. */
315 if (odir
&& !same_pdir
) {
316 dup_inode(new_superdirp
= new_dirp
);
317 while (TRUE
) { /* may hang in a file system loop */
318 if (new_superdirp
== old_ip
) {
319 put_inode(new_superdirp
);
323 next_new_superdirp
= advance(new_superdirp
, "..");
325 put_inode(new_superdirp
);
326 if(next_new_superdirp
== new_superdirp
) {
327 put_inode(new_superdirp
);
330 if(next_new_superdirp
->i_num
== ROOT_INODE
) {
331 put_inode(next_new_superdirp
);
335 new_superdirp
= next_new_superdirp
;
336 if(new_superdirp
== NULL
) {
337 /* Missing ".." entry. Assume the worst. */
344 /* Some tests apply only if the new path exists. */
346 if (odir
&& new_dirp
->i_nlinks
>= LINK_MAX
&&
347 !same_pdir
&& r
== OK
) {
351 if(old_ip
== new_ip
) r
= SAME
; /* old=new */
353 ndir
= ((new_ip
->i_mode
& I_TYPE
) == I_DIRECTORY
);/* dir ? */
354 if(odir
== TRUE
&& ndir
== FALSE
) r
= ENOTDIR
;
355 if(odir
== FALSE
&& ndir
== TRUE
) r
= EISDIR
;
359 /* If a process has another root directory than the system root, we might
360 * "accidently" be moving it's working directory to a place where it's
361 * root directory isn't a super directory of it anymore. This can make
362 * the function chroot useless. If chroot will be used often we should
363 * probably check for it here. */
365 /* The rename will probably work. Only two things can go wrong now:
366 * 1. being unable to remove the new file. (when new file already exists)
367 * 2. being unable to make the new directory entry. (new file doesn't exists)
368 * [directory has to grow by one block and cannot because the disk
369 * is completely full].
373 /* There is already an entry for 'new'. Try to remove it. */
375 r
= remove_dir(new_dirp
, new_ip
, new_name
);
377 r
= unlink_file(new_dirp
, new_ip
, new_name
);
379 /* if r is OK, the rename will succeed, while there is now an
380 * unused entry in the new parent directory. */
384 /* If the new name will be in the same parent directory as the old
385 * one, first remove the old name to free an entry for the new name,
386 * otherwise first try to create the new name entry to make sure
387 * the rename will succeed.
389 numb
= old_ip
->i_num
; /* inode number of old file */
392 r
= search_dir(old_dirp
, old_name
, NULL
, DELETE
);
393 /* shouldn't go wrong. */
395 (void) search_dir(old_dirp
, new_name
, &numb
, ENTER
);
397 r
= search_dir(new_dirp
, new_name
, &numb
, ENTER
);
399 (void) search_dir(old_dirp
, old_name
, NULL
, DELETE
);
402 /* If r is OK, the ctime and mtime of old_dirp and new_dirp have been marked
403 * for update in search_dir. */
405 if(r
== OK
&& odir
&& !same_pdir
) {
406 /* Update the .. entry in the directory (still points to old_dirp).*/
407 numb
= new_dirp
->i_num
;
408 (void) unlink_file(old_ip
, NULL
, "..");
409 if(search_dir(old_ip
, "..", &numb
, ENTER
) == OK
) {
410 /* New link created. */
411 new_dirp
->i_nlinks
++;
412 IN_MARKDIRTY(new_dirp
);
416 /* Release the inodes. */
421 return(r
== SAME
? OK
: r
);
425 /*===========================================================================*
427 *===========================================================================*/
428 int fs_trunc(ino_t ino_nr
, off_t start
, off_t end
)
433 if( (rip
= find_inode(fs_dev
, ino_nr
)) == NULL
)
436 if(rip
->i_sp
->s_rd_only
) {
440 r
= truncate_inode(rip
, start
);
442 r
= freesp_inode(rip
, start
, end
);
449 /*===========================================================================*
451 *===========================================================================*/
452 int truncate_inode(rip
, newsize
)
453 register struct inode
*rip
; /* pointer to inode to be truncated */
454 off_t newsize
; /* inode must become this size */
456 /* Set inode to a certain size, freeing any zones no longer referenced
457 * and updating the size in the inode. If the inode is extended, the
458 * extra space is a hole that reads as zeroes.
460 * Nothing special has to happen to file pointers if inode is opened in
461 * O_APPEND mode, as this is different per fd and is checked when
467 file_type
= rip
->i_mode
& I_TYPE
; /* check to see if file is special */
468 if (file_type
== I_CHAR_SPECIAL
|| file_type
== I_BLOCK_SPECIAL
)
470 if (newsize
> rip
->i_sp
->s_max_size
) /* don't let inode grow too big */
473 /* Free the actual space if truncating. */
474 if (newsize
< rip
->i_size
) {
475 if ((r
= freesp_inode(rip
, newsize
, rip
->i_size
)) != OK
)
479 /* Clear the rest of the last zone if expanding. */
480 if (newsize
> rip
->i_size
) clear_zone(rip
, rip
->i_size
, 0);
482 /* Next correct the inode size. */
483 rip
->i_size
= newsize
;
484 rip
->i_update
|= CTIME
| MTIME
;
491 /*===========================================================================*
493 *===========================================================================*/
494 static int freesp_inode(rip
, start
, end
)
495 register struct inode
*rip
; /* pointer to inode to be partly freed */
496 off_t start
, end
; /* range of bytes to free (end uninclusive) */
498 /* Cut an arbitrary hole in an inode. The caller is responsible for checking
499 * the reasonableness of the inode type of rip. The reason is this is that
500 * this function can be called for different reasons, for which different
501 * sets of inode types are reasonable. Adjusting the final size of the inode
502 * is to be done by the caller too, if wished.
504 * Consumers of this function currently are truncate_inode() (used to
505 * free indirect and data blocks for any type of inode, but also to
506 * implement the ftruncate() and truncate() system calls) and the F_FREESP
511 int zero_last
, zero_first
;
513 if(end
> rip
->i_size
) /* freeing beyond end makes no sense */
515 if(end
<= start
) /* end is uninclusive, so start<end */
518 zone_size
= rip
->i_sp
->s_block_size
<< rip
->i_sp
->s_log_zone_size
;
520 /* If freeing doesn't cross a zone boundary, then we may only zero
521 * a range of the zone, unless we are freeing up that entire zone.
523 zero_last
= start
% zone_size
;
524 zero_first
= end
% zone_size
&& end
< rip
->i_size
;
525 if(start
/zone_size
== (end
-1)/zone_size
&& (zero_last
|| zero_first
)) {
526 zerozone_range(rip
, start
, end
-start
);
528 /* First zero unused part of partly used zones. */
530 zerozone_half(rip
, start
, LAST_HALF
, zone_size
);
532 zerozone_half(rip
, end
, FIRST_HALF
, zone_size
);
534 /* Now completely free the completely unused zones.
535 * write_map() will free unused (double) indirect
536 * blocks too. Converting the range to zone numbers avoids
537 * overflow on p when doing e.g. 'p += zone_size'.
540 if(end
== rip
->i_size
&& (end
% zone_size
)) e
++;
541 for(p
= nextblock(start
, zone_size
)/zone_size
; p
< e
; p
++) {
542 if((r
= write_map(rip
, p
*zone_size
, NO_ZONE
, WMAP_FREE
)) != OK
)
548 rip
->i_update
|= CTIME
| MTIME
;
555 /*===========================================================================*
557 *===========================================================================*/
558 static off_t
nextblock(pos
, zone_size
)
562 /* Return the first position in the next block after position 'pos'
563 * (unless this is the first position in the current block).
564 * This can be done in one expression, but that can overflow pos.
567 p
= (pos
/zone_size
)*zone_size
;
568 if((pos
% zone_size
)) p
+= zone_size
; /* Round up. */
573 /*===========================================================================*
575 *===========================================================================*/
576 static void zerozone_half(rip
, pos
, half
, zone_size
)
582 /* Zero the upper or lower 'half' of a zone that holds position 'pos'.
583 * half can be FIRST_HALF or LAST_HALF.
585 * FIRST_HALF: 0..pos-1 will be zeroed
586 * LAST_HALF: pos..zone_size-1 will be zeroed
590 /* Offset of zeroing boundary. */
591 offset
= pos
% zone_size
;
593 if(half
== LAST_HALF
) {
594 len
= zone_size
- offset
;
600 zerozone_range(rip
, pos
, len
);
604 /*===========================================================================*
606 *===========================================================================*/
607 static void zerozone_range(rip
, pos
, len
)
612 /* Zero an arbitrary byte range in a zone, possibly spanning multiple blocks.
616 unsigned short block_size
;
619 block_size
= rip
->i_sp
->s_block_size
;
621 if(!len
) return; /* no zeroing to be done. */
624 if( (bp
= get_block_map(rip
, rounddown(pos
, block_size
))) == NULL
)
626 offset
= pos
% block_size
;
627 bytes
= block_size
- offset
;
628 if (bytes
> (size_t) len
)
630 memset(b_data(bp
) + offset
, 0, bytes
);