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