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