xref: /minix/minix/fs/mfs/link.c (revision 7f5f010b)
1 #include "fs.h"
2 #include <sys/stat.h>
3 #include <string.h>
4 #include <minix/com.h>
5 #include "buf.h"
6 #include "inode.h"
7 #include "super.h"
8 #include <minix/vfsif.h>
9 #include <sys/param.h>
10 
11 #define SAME 1000
12 
13 
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
21 	zone_size);
22 static void zerozone_range(struct inode *rip, off_t pos, off_t len);
23 
24 /* Args to zerozone_half() */
25 #define FIRST_HALF	0
26 #define LAST_HALF	1
27 
28 
29 /*===========================================================================*
30  *				fs_link 				     *
31  *===========================================================================*/
32 int fs_link()
33 {
34 /* Perform the link(name1, name2) system call. */
35 
36   struct inode *ip, *rip;
37   register int r;
38   char string[MFS_NAME_MAX];
39   struct inode *new_ip;
40   phys_bytes len;
41 
42   len = min(fs_m_in.m_vfs_fs_link.path_len, sizeof(string));
43   /* Copy the link name's last component */
44   r = sys_safecopyfrom(VFS_PROC_NR, fs_m_in.m_vfs_fs_link.grant,
45   		       (vir_bytes) 0, (vir_bytes) string, (size_t) len);
46   if (r != OK) return r;
47   NUL(string, len, sizeof(string));
48 
49   /* Temporarily open the file. */
50   if( (rip = get_inode(fs_dev, fs_m_in.m_vfs_fs_link.inode)) == NULL)
51 	  return(EINVAL);
52 
53   /* Check to see if the file has maximum number of links already. */
54   r = OK;
55   if(rip->i_nlinks >= LINK_MAX)
56 	  r = EMLINK;
57 
58   /* Only super_user may link to directories. */
59   if(r == OK)
60 	  if( (rip->i_mode & I_TYPE) == I_DIRECTORY && caller_uid != SU_UID)
61 		  r = EPERM;
62 
63   /* If error with 'name', return the inode. */
64   if (r != OK) {
65 	  put_inode(rip);
66 	  return(r);
67   }
68 
69   /* Temporarily open the last dir */
70   if( (ip = get_inode(fs_dev, fs_m_in.m_vfs_fs_link.dir_ino)) == NULL) {
71 	put_inode(rip);
72 	return(EINVAL);
73   }
74 
75   if (ip->i_nlinks == NO_LINK) {	/* Dir does not actually exist */
76   	put_inode(rip);
77 	put_inode(ip);
78   	return(ENOENT);
79   }
80 
81   /* If 'name2' exists in full (even if no space) set 'r' to error. */
82   if((new_ip = advance(ip, string, IGN_PERM)) == NULL) {
83 	  r = err_code;
84 	  if(r == ENOENT)
85 		  r = OK;
86   } else {
87 	  put_inode(new_ip);
88 	  r = EEXIST;
89   }
90 
91   /* Try to link. */
92   if(r == OK)
93 	  r = search_dir(ip, string, &rip->i_num, ENTER, IGN_PERM);
94 
95   /* If success, register the linking. */
96   if(r == OK) {
97 	  rip->i_nlinks++;
98 	  rip->i_update |= CTIME;
99 	  IN_MARKDIRTY(rip);
100   }
101 
102   /* Done.  Release both inodes. */
103   put_inode(rip);
104   put_inode(ip);
105   return(r);
106 }
107 
108 
109 /*===========================================================================*
110  *				fs_unlink				     *
111  *===========================================================================*/
112 int fs_unlink()
113 {
114 /* Perform the unlink(name) or rmdir(name) system call. The code for these two
115  * is almost the same.  They differ only in some condition testing.  Unlink()
116  * may be used by the superuser to do dangerous things; rmdir() may not.
117  */
118   register struct inode *rip;
119   struct inode *rldirp;
120   int r;
121   char string[MFS_NAME_MAX];
122   phys_bytes len;
123 
124   /* Copy the last component */
125   len = min(fs_m_in.m_vfs_fs_unlink.path_len, sizeof(string));
126   r = sys_safecopyfrom(VFS_PROC_NR, fs_m_in.m_vfs_fs_unlink.grant,
127   		       (vir_bytes) 0, (vir_bytes) string, (size_t) len);
128   if (r != OK) return r;
129   NUL(string, len, sizeof(string));
130 
131   /* Temporarily open the dir. */
132   if((rldirp = get_inode(fs_dev, fs_m_in.m_vfs_fs_unlink.inode)) == NULL)
133 	  return(EINVAL);
134 
135   /* The last directory exists.  Does the file also exist? */
136   rip = advance(rldirp, string, IGN_PERM);
137   r = err_code;
138 
139   /* If error, return inode. */
140   if(r != OK) {
141 	  /* Mount point? */
142   	if (r == EENTERMOUNT || r == ELEAVEMOUNT) {
143   	  	put_inode(rip);
144   		r = EBUSY;
145   	}
146 	put_inode(rldirp);
147 	return(r);
148   }
149 
150   if(rip->i_sp->s_rd_only) {
151   	r = EROFS;
152   }  else if(fs_m_in.m_type == REQ_UNLINK) {
153   /* Now test if the call is allowed, separately for unlink() and rmdir(). */
154 	  /* Only the su may unlink directories, but the su can unlink any
155 	   * dir.*/
156 	  if( (rip->i_mode & I_TYPE) == I_DIRECTORY) r = EPERM;
157 
158 	  /* Actually try to unlink the file; fails if parent is mode 0 etc. */
159 	  if (r == OK) r = unlink_file(rldirp, rip, string);
160   } else {
161 	  r = remove_dir(rldirp, rip, string); /* call is RMDIR */
162   }
163 
164   /* If unlink was possible, it has been done, otherwise it has not. */
165   put_inode(rip);
166   put_inode(rldirp);
167   return(r);
168 }
169 
170 
171 /*===========================================================================*
172  *                             fs_rdlink                                     *
173  *===========================================================================*/
174 int fs_rdlink()
175 {
176   struct buf *bp;              /* buffer containing link text */
177   register struct inode *rip;  /* target inode */
178   register int r;              /* return value */
179   size_t copylen;
180 
181   copylen = min(fs_m_in.m_vfs_fs_rdlink.mem_size, UMAX_FILE_POS);
182 
183   /* Temporarily open the file. */
184   if( (rip = get_inode(fs_dev, fs_m_in.m_vfs_fs_rdlink.inode)) == NULL)
185 	  return(EINVAL);
186 
187   if(!S_ISLNK(rip->i_mode))
188 	  r = EACCES;
189   else {
190 	if(!(bp = get_block_map(rip, 0)))
191 		return EIO;
192 	/* Passed all checks */
193 	/* We can safely cast to unsigned, because copylen is guaranteed to be
194 	   below max file size */
195 	copylen = min( copylen, (unsigned) rip->i_size);
196 	r = sys_safecopyto(VFS_PROC_NR, fs_m_in.m_vfs_fs_rdlink.grant,
197 			   (vir_bytes) 0, (vir_bytes) b_data(bp),
198 	  		   (size_t) copylen);
199 	put_block(bp, DIRECTORY_BLOCK);
200 	if (r == OK)
201 		fs_m_out.m_fs_vfs_rdlink.nbytes = copylen;
202   }
203 
204   put_inode(rip);
205   return(r);
206 }
207 
208 
209 /*===========================================================================*
210  *				remove_dir				     *
211  *===========================================================================*/
212 static int remove_dir(rldirp, rip, dir_name)
213 struct inode *rldirp;		 	/* parent directory */
214 struct inode *rip;			/* directory to be removed */
215 char dir_name[MFS_NAME_MAX];		/* name of directory to be removed */
216 {
217   /* A directory file has to be removed. Five conditions have to met:
218    * 	- The file must be a directory
219    *	- The directory must be empty (except for . and ..)
220    *	- The final component of the path must not be . or ..
221    *	- The directory must not be the root of a mounted file system (VFS)
222    *	- The directory must not be anybody's root/working directory (VFS)
223    */
224   int r;
225 
226   /* search_dir checks that rip is a directory too. */
227   if ((r = search_dir(rip, "", NULL, IS_EMPTY, IGN_PERM)) != OK)
228   	return(r);
229 
230   if (strcmp(dir_name, ".") == 0 || strcmp(dir_name, "..") == 0)return(EINVAL);
231   if (rip->i_num == ROOT_INODE) return(EBUSY); /* can't remove 'root' */
232 
233   /* Actually try to unlink the file; fails if parent is mode 0 etc. */
234   if ((r = unlink_file(rldirp, rip, dir_name)) != OK) return r;
235 
236   /* Unlink . and .. from the dir. The super user can link and unlink any dir,
237    * so don't make too many assumptions about them.
238    */
239   (void) unlink_file(rip, NULL, dot1);
240   (void) unlink_file(rip, NULL, dot2);
241   return(OK);
242 }
243 
244 
245 /*===========================================================================*
246  *				unlink_file				     *
247  *===========================================================================*/
248 static int unlink_file(dirp, rip, file_name)
249 struct inode *dirp;		/* parent directory of file */
250 struct inode *rip;		/* inode of file, may be NULL too. */
251 char file_name[MFS_NAME_MAX];	/* name of file to be removed */
252 {
253 /* Unlink 'file_name'; rip must be the inode of 'file_name' or NULL. */
254 
255   ino_t numb;			/* inode number */
256   int	r;
257 
258   /* If rip is not NULL, it is used to get faster access to the inode. */
259   if (rip == NULL) {
260   	/* Search for file in directory and try to get its inode. */
261 	err_code = search_dir(dirp, file_name, &numb, LOOK_UP, IGN_PERM);
262 	if (err_code == OK) rip = get_inode(dirp->i_dev, (int) numb);
263 	if (err_code != OK || rip == NULL) return(err_code);
264   } else {
265 	dup_inode(rip);		/* inode will be returned with put_inode */
266   }
267 
268   r = search_dir(dirp, file_name, NULL, DELETE, IGN_PERM);
269 
270   if (r == OK) {
271 	rip->i_nlinks--;	/* entry deleted from parent's dir */
272 	rip->i_update |= CTIME;
273 	IN_MARKDIRTY(rip);
274   }
275 
276   put_inode(rip);
277   return(r);
278 }
279 
280 
281 /*===========================================================================*
282  *				fs_rename				     *
283  *===========================================================================*/
284 int fs_rename()
285 {
286 /* Perform the rename(name1, name2) system call. */
287   struct inode *old_dirp, *old_ip;	/* ptrs to old dir, file inodes */
288   struct inode *new_dirp, *new_ip;	/* ptrs to new dir, file inodes */
289   struct inode *new_superdirp, *next_new_superdirp;
290   int r = OK;				/* error flag; initially no error */
291   int odir, ndir;			/* TRUE iff {old|new} file is dir */
292   int same_pdir;			/* TRUE iff parent dirs are the same */
293   char old_name[MFS_NAME_MAX], new_name[MFS_NAME_MAX];
294   ino_t numb;
295   phys_bytes len;
296 
297   /* Copy the last component of the old name */
298   len = min( (unsigned) fs_m_in.m_vfs_fs_rename.len_old, sizeof(old_name));
299   r = sys_safecopyfrom(VFS_PROC_NR, fs_m_in.m_vfs_fs_rename.grant_old,
300   		       (vir_bytes) 0, (vir_bytes) old_name, (size_t) len);
301   if (r != OK) return r;
302   NUL(old_name, len, sizeof(old_name));
303 
304   /* Copy the last component of the new name */
305   len = min( (unsigned) fs_m_in.m_vfs_fs_rename.len_new, sizeof(new_name));
306   r = sys_safecopyfrom(VFS_PROC_NR, fs_m_in.m_vfs_fs_rename.grant_new,
307   		       (vir_bytes) 0, (vir_bytes) new_name, (size_t) len);
308   if (r != OK) return r;
309   NUL(new_name, len, sizeof(new_name));
310 
311   /* Get old dir inode */
312   if ((old_dirp = get_inode(fs_dev, fs_m_in.m_vfs_fs_rename.dir_old)) == NULL)
313 	return(err_code);
314 
315   old_ip = advance(old_dirp, old_name, IGN_PERM);
316   r = err_code;
317 
318   if (r == EENTERMOUNT || r == ELEAVEMOUNT) {
319 	put_inode(old_ip);
320 	old_ip = NULL;
321 	if (r == EENTERMOUNT) r = EXDEV;	/* should this fail at all? */
322 	else if (r == ELEAVEMOUNT) r = EINVAL;	/* rename on dot-dot */
323   }
324 
325   if (old_ip == NULL) {
326 	put_inode(old_dirp);
327 	return(r);
328   }
329 
330   /* Get new dir inode */
331   if ((new_dirp = get_inode(fs_dev, fs_m_in.m_vfs_fs_rename.dir_new)) == NULL){
332         put_inode(old_ip);
333         put_inode(old_dirp);
334         return(err_code);
335   } else {
336 	if (new_dirp->i_nlinks == NO_LINK) {	/* Dir does not actually exist */
337   		put_inode(old_ip);
338   		put_inode(old_dirp);
339   		put_inode(new_dirp);
340   		return(ENOENT);
341 	}
342   }
343 
344   new_ip = advance(new_dirp, new_name, IGN_PERM); /* not required to exist */
345 
346   /* However, if the check failed because the file does exist, don't continue.
347    * Note that ELEAVEMOUNT is covered by the dot-dot check later. */
348   if(err_code == EENTERMOUNT) {
349 	put_inode(new_ip);
350 	new_ip = NULL;
351 	r = EBUSY;
352   }
353 
354   odir = ((old_ip->i_mode & I_TYPE) == I_DIRECTORY); /* TRUE iff dir */
355 
356   /* If it is ok, check for a variety of possible errors. */
357   if(r == OK) {
358 	same_pdir = (old_dirp == new_dirp);
359 
360 	/* The old inode must not be a superdirectory of the new last dir. */
361 	if (odir && !same_pdir) {
362 		dup_inode(new_superdirp = new_dirp);
363 		while (TRUE) {	/* may hang in a file system loop */
364 			if (new_superdirp == old_ip) {
365 				put_inode(new_superdirp);
366 				r = EINVAL;
367 				break;
368 			}
369 			next_new_superdirp = advance(new_superdirp, dot2,
370 						     IGN_PERM);
371 
372 			put_inode(new_superdirp);
373 			if(next_new_superdirp == new_superdirp) {
374 				put_inode(new_superdirp);
375 				break;
376 			}
377 			if(err_code == ELEAVEMOUNT) {
378 				/* imitate that we are back at the root,
379 				 * cross device checked already on VFS */
380 				put_inode(next_new_superdirp);
381 				err_code = OK;
382 				break;
383 			}
384 			new_superdirp = next_new_superdirp;
385 			if(new_superdirp == NULL) {
386 				/* Missing ".." entry.  Assume the worst. */
387 				r = EINVAL;
388 				break;
389 			}
390 		}
391 	}
392 
393 	/* The old or new name must not be . or .. */
394 	if(strcmp(old_name, ".") == 0 || strcmp(old_name, "..") == 0 ||
395 	   strcmp(new_name, ".") == 0 || strcmp(new_name, "..") == 0) {
396 		r = EINVAL;
397 	}
398 	/* Both parent directories must be on the same device.
399 	if(old_dirp->i_dev != new_dirp->i_dev) r = EXDEV; */
400 
401 	/* Some tests apply only if the new path exists. */
402 	if(new_ip == NULL) {
403 		/* don't rename a file with a file system mounted on it.
404 		if (old_ip->i_dev != old_dirp->i_dev) r = EXDEV;*/
405 		if (odir && new_dirp->i_nlinks >= LINK_MAX &&
406 		    !same_pdir && r == OK) {
407 			r = EMLINK;
408 		}
409 	} else {
410 		if(old_ip == new_ip) r = SAME; /* old=new */
411 
412 		ndir = ((new_ip->i_mode & I_TYPE) == I_DIRECTORY);/* dir ? */
413 		if(odir == TRUE && ndir == FALSE) r = ENOTDIR;
414 		if(odir == FALSE && ndir == TRUE) r = EISDIR;
415 	}
416   }
417 
418   /* If a process has another root directory than the system root, we might
419    * "accidently" be moving it's working directory to a place where it's
420    * root directory isn't a super directory of it anymore. This can make
421    * the function chroot useless. If chroot will be used often we should
422    * probably check for it here. */
423 
424   /* The rename will probably work. Only two things can go wrong now:
425    * 1. being unable to remove the new file. (when new file already exists)
426    * 2. being unable to make the new directory entry. (new file doesn't exists)
427    *     [directory has to grow by one block and cannot because the disk
428    *      is completely full].
429    */
430   if(r == OK) {
431 	if(new_ip != NULL) {
432 		/* There is already an entry for 'new'. Try to remove it. */
433 		if(odir)
434 			r = remove_dir(new_dirp, new_ip, new_name);
435 		else
436 			r = unlink_file(new_dirp, new_ip, new_name);
437 	}
438 	/* if r is OK, the rename will succeed, while there is now an
439 	 * unused entry in the new parent directory. */
440   }
441 
442   if(r == OK) {
443 	  /* If the new name will be in the same parent directory as the old
444 	   * one, first remove the old name to free an entry for the new name,
445 	   * otherwise first try to create the new name entry to make sure
446 	   * the rename will succeed.
447 	   */
448 	numb = old_ip->i_num;		/* inode number of old file */
449 
450 	if(same_pdir) {
451 		r = search_dir(old_dirp, old_name, NULL, DELETE, IGN_PERM);
452 						/* shouldn't go wrong. */
453 		if(r == OK)
454 			(void) search_dir(old_dirp, new_name, &numb, ENTER,
455 					  IGN_PERM);
456 	} else {
457 		r = search_dir(new_dirp, new_name, &numb, ENTER, IGN_PERM);
458 		if(r == OK)
459 			(void) search_dir(old_dirp, old_name, NULL, DELETE,
460 					  IGN_PERM);
461 	}
462   }
463   /* If r is OK, the ctime and mtime of old_dirp and new_dirp have been marked
464    * for update in search_dir. */
465 
466   if(r == OK && odir && !same_pdir) {
467 	/* Update the .. entry in the directory (still points to old_dirp).*/
468 	numb = new_dirp->i_num;
469 	(void) unlink_file(old_ip, NULL, dot2);
470 	if(search_dir(old_ip, dot2, &numb, ENTER, IGN_PERM) == OK) {
471 		/* New link created. */
472 		new_dirp->i_nlinks++;
473 		IN_MARKDIRTY(new_dirp);
474 	}
475   }
476 
477   /* Release the inodes. */
478   put_inode(old_dirp);
479   put_inode(old_ip);
480   put_inode(new_dirp);
481   put_inode(new_ip);
482   return(r == SAME ? OK : r);
483 }
484 
485 
486 /*===========================================================================*
487  *				fs_ftrunc				     *
488  *===========================================================================*/
489 int fs_ftrunc(void)
490 {
491   struct inode *rip;
492   off_t start, end;
493   int r;
494 
495   if( (rip = find_inode(fs_dev, fs_m_in.m_vfs_fs_ftrunc.inode)) == NULL)
496 	  return(EINVAL);
497 
498   if(rip->i_sp->s_rd_only) {
499   	r = EROFS;
500   } else {
501     start = fs_m_in.m_vfs_fs_ftrunc.trc_start;
502     end = fs_m_in.m_vfs_fs_ftrunc.trc_end;
503 
504     if (end == 0)
505 	  r = truncate_inode(rip, start);
506     else
507 	  r = freesp_inode(rip, start, end);
508   }
509 
510   return(r);
511 }
512 
513 
514 /*===========================================================================*
515  *				truncate_inode				     *
516  *===========================================================================*/
517 int truncate_inode(rip, newsize)
518 register struct inode *rip;	/* pointer to inode to be truncated */
519 off_t newsize;			/* inode must become this size */
520 {
521 /* Set inode to a certain size, freeing any zones no longer referenced
522  * and updating the size in the inode. If the inode is extended, the
523  * extra space is a hole that reads as zeroes.
524  *
525  * Nothing special has to happen to file pointers if inode is opened in
526  * O_APPEND mode, as this is different per fd and is checked when
527  * writing is done.
528  */
529   int r;
530   mode_t file_type;
531 
532   file_type = rip->i_mode & I_TYPE;	/* check to see if file is special */
533   if (file_type == I_CHAR_SPECIAL || file_type == I_BLOCK_SPECIAL)
534 	return(EINVAL);
535   if (newsize > rip->i_sp->s_max_size)	/* don't let inode grow too big */
536 	return(EFBIG);
537 
538   /* Free the actual space if truncating. */
539   if (newsize < rip->i_size) {
540   	if ((r = freesp_inode(rip, newsize, rip->i_size)) != OK)
541   		return(r);
542   }
543 
544   /* Clear the rest of the last zone if expanding. */
545   if (newsize > rip->i_size) clear_zone(rip, rip->i_size, 0);
546 
547   /* Next correct the inode size. */
548   rip->i_size = newsize;
549   rip->i_update |= CTIME | MTIME;
550   IN_MARKDIRTY(rip);
551 
552   return(OK);
553 }
554 
555 
556 /*===========================================================================*
557  *				freesp_inode				     *
558  *===========================================================================*/
559 static int freesp_inode(rip, start, end)
560 register struct inode *rip;	/* pointer to inode to be partly freed */
561 off_t start, end;		/* range of bytes to free (end uninclusive) */
562 {
563 /* Cut an arbitrary hole in an inode. The caller is responsible for checking
564  * the reasonableness of the inode type of rip. The reason is this is that
565  * this function can be called for different reasons, for which different
566  * sets of inode types are reasonable. Adjusting the final size of the inode
567  * is to be done by the caller too, if wished.
568  *
569  * Consumers of this function currently are truncate_inode() (used to
570  * free indirect and data blocks for any type of inode, but also to
571  * implement the ftruncate() and truncate() system calls) and the F_FREESP
572  * fcntl().
573  */
574   off_t p, e;
575   int zone_size, r;
576   int zero_last, zero_first;
577 
578   if(end > rip->i_size)		/* freeing beyond end makes no sense */
579 	end = rip->i_size;
580   if(end <= start)		/* end is uninclusive, so start<end */
581 	return(EINVAL);
582 
583   zone_size = rip->i_sp->s_block_size << rip->i_sp->s_log_zone_size;
584 
585   /* If freeing doesn't cross a zone boundary, then we may only zero
586    * a range of the zone, unless we are freeing up that entire zone.
587    */
588   zero_last = start % zone_size;
589   zero_first = end % zone_size && end < rip->i_size;
590   if(start/zone_size == (end-1)/zone_size && (zero_last || zero_first)) {
591 	zerozone_range(rip, start, end-start);
592   } else {
593 	/* First zero unused part of partly used zones. */
594 	if(zero_last)
595 		zerozone_half(rip, start, LAST_HALF, zone_size);
596 	if(zero_first)
597 		zerozone_half(rip, end, FIRST_HALF, zone_size);
598 
599 	/* Now completely free the completely unused zones.
600 	 * write_map() will free unused (double) indirect
601 	 * blocks too. Converting the range to zone numbers avoids
602 	 * overflow on p when doing e.g. 'p += zone_size'.
603 	 */
604 	e = end/zone_size;
605 	if(end == rip->i_size && (end % zone_size)) e++;
606 	for(p = nextblock(start, zone_size)/zone_size; p < e; p ++) {
607 		if((r = write_map(rip, p*zone_size, NO_ZONE, WMAP_FREE)) != OK)
608 			return(r);
609 	}
610 
611   }
612 
613   rip->i_update |= CTIME | MTIME;
614   IN_MARKDIRTY(rip);
615 
616   return(OK);
617 }
618 
619 
620 /*===========================================================================*
621  *				nextblock				     *
622  *===========================================================================*/
623 static off_t nextblock(pos, zone_size)
624 off_t pos;
625 int zone_size;
626 {
627 /* Return the first position in the next block after position 'pos'
628  * (unless this is the first position in the current block).
629  * This can be done in one expression, but that can overflow pos.
630  */
631   off_t p;
632   p = (pos/zone_size)*zone_size;
633   if((pos % zone_size)) p += zone_size;	/* Round up. */
634   return(p);
635 }
636 
637 
638 /*===========================================================================*
639  *				zerozone_half				     *
640  *===========================================================================*/
641 static void zerozone_half(rip, pos, half, zone_size)
642 struct inode *rip;
643 off_t pos;
644 int half;
645 int zone_size;
646 {
647 /* Zero the upper or lower 'half' of a zone that holds position 'pos'.
648  * half can be FIRST_HALF or LAST_HALF.
649  *
650  * FIRST_HALF: 0..pos-1 will be zeroed
651  * LAST_HALF:  pos..zone_size-1 will be zeroed
652  */
653   off_t offset, len;
654 
655   /* Offset of zeroing boundary. */
656   offset = pos % zone_size;
657 
658   if(half == LAST_HALF)  {
659    	len = zone_size - offset;
660   } else {
661 	len = offset;
662 	pos -= offset;
663   }
664 
665   zerozone_range(rip, pos, len);
666 }
667 
668 
669 /*===========================================================================*
670  *				zerozone_range				     *
671  *===========================================================================*/
672 static void zerozone_range(rip, pos, len)
673 struct inode *rip;
674 off_t pos;
675 off_t len;
676 {
677 /* Zero an arbitrary byte range in a zone, possibly spanning multiple blocks.
678  */
679   struct buf *bp;
680   off_t offset;
681   unsigned short block_size;
682   size_t bytes;
683 
684   block_size = rip->i_sp->s_block_size;
685 
686   if(!len) return; /* no zeroing to be done. */
687 
688   while (len > 0) {
689 	if( (bp = get_block_map(rip, rounddown(pos, block_size))) == NULL)
690 		return;
691 	offset = pos % block_size;
692 	bytes = block_size - offset;
693 	if (bytes > (size_t) len)
694 		bytes = len;
695 	memset(b_data(bp) + offset, 0, bytes);
696 	MARKDIRTY(bp);
697 	put_block(bp, FULL_DATA_BLOCK);
698 
699 	pos += bytes;
700 	len -= bytes;
701   }
702 }
703 
704