xref: /minix3/minix/fs/mfs/link.c (revision 0314acfb2d68447dfa1b0b33aa4c25b1cbfa85d3)
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  *===========================================================================*/
fs_link(ino_t dir_nr,char * name,ino_t ino_nr)32 int fs_link(ino_t dir_nr, char *name, ino_t ino_nr)
33 {
34 /* Perform the link(name1, name2) system call. */
35 
36   struct inode *ip, *rip;
37   register int r;
38   struct inode *new_ip;
39 
40   /* Temporarily open the file. */
41   if( (rip = get_inode(fs_dev, ino_nr)) == NULL)
42 	  return(EINVAL);
43 
44   /* Check to see if the file has maximum number of links already. */
45   r = OK;
46   if(rip->i_nlinks >= LINK_MAX)
47 	  r = EMLINK;
48 
49   /* Linking to directories is too dangerous to allow. */
50   if(r == OK)
51 	  if( (rip->i_mode & I_TYPE) == I_DIRECTORY)
52 		  r = EPERM;
53 
54   /* If error with 'name', return the inode. */
55   if (r != OK) {
56 	  put_inode(rip);
57 	  return(r);
58   }
59 
60   /* Temporarily open the last dir */
61   if( (ip = get_inode(fs_dev, dir_nr)) == NULL) {
62 	put_inode(rip);
63 	return(EINVAL);
64   }
65 
66   if (ip->i_nlinks == NO_LINK) {	/* Dir does not actually exist */
67   	put_inode(rip);
68 	put_inode(ip);
69   	return(ENOENT);
70   }
71 
72   /* If 'name2' exists in full (even if no space) set 'r' to error. */
73   if((new_ip = advance(ip, name)) == NULL) {
74 	  r = err_code;
75 	  if(r == ENOENT)
76 		  r = OK;
77   } else {
78 	  put_inode(new_ip);
79 	  r = EEXIST;
80   }
81 
82   /* Try to link. */
83   if(r == OK)
84 	  r = search_dir(ip, name, &rip->i_num, ENTER);
85 
86   /* If success, register the linking. */
87   if(r == OK) {
88 	  rip->i_nlinks++;
89 	  rip->i_update |= CTIME;
90 	  IN_MARKDIRTY(rip);
91   }
92 
93   /* Done.  Release both inodes. */
94   put_inode(rip);
95   put_inode(ip);
96   return(r);
97 }
98 
99 
100 /*===========================================================================*
101  *				fs_unlink				     *
102  *===========================================================================*/
fs_unlink(ino_t dir_nr,char * name,int call)103 int fs_unlink(ino_t dir_nr, char *name, int call)
104 {
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.
107  */
108   register struct inode *rip;
109   struct inode *rldirp;
110   int r;
111 
112   /* Temporarily open the dir. */
113   if((rldirp = get_inode(fs_dev, dir_nr)) == NULL)
114 	  return(EINVAL);
115 
116   /* The last directory exists.  Does the file also exist? */
117   rip = advance(rldirp, name);
118   r = err_code;
119 
120   /* If error, return inode. */
121   if(r != OK) {
122 	put_inode(rldirp);
123 	return(r);
124   }
125   if (rip->i_mountpoint) {
126 	put_inode(rip);
127 	put_inode(rldirp);
128 	return(EBUSY);
129   }
130 
131   if(rip->i_sp->s_rd_only) {
132   	r = EROFS;
133   }  else if (call == FSC_UNLINK) {
134 	  if( (rip->i_mode & I_TYPE) == I_DIRECTORY) r = EPERM;
135 
136 	  /* Actually try to unlink the file; fails if parent is mode 0 etc. */
137 	  if (r == OK) r = unlink_file(rldirp, rip, name);
138   } else {
139 	  r = remove_dir(rldirp, rip, name); /* call is RMDIR */
140   }
141 
142   /* If unlink was possible, it has been done, otherwise it has not. */
143   put_inode(rip);
144   put_inode(rldirp);
145   return(r);
146 }
147 
148 
149 /*===========================================================================*
150  *                             fs_rdlink                                     *
151  *===========================================================================*/
fs_rdlink(ino_t ino_nr,struct fsdriver_data * data,size_t bytes)152 ssize_t fs_rdlink(ino_t ino_nr, struct fsdriver_data *data, size_t bytes)
153 {
154   struct buf *bp;              /* buffer containing link text */
155   register struct inode *rip;  /* target inode */
156   register int r;              /* return value */
157 
158   /* Temporarily open the file. */
159   if( (rip = get_inode(fs_dev, ino_nr)) == NULL)
160 	  return(EINVAL);
161 
162   if(!S_ISLNK(rip->i_mode))
163 	  r = EACCES;
164   else {
165 	if(!(bp = get_block_map(rip, 0)))
166 		return EIO;
167 	/* Passed all checks */
168 	if (bytes > rip->i_size)
169 		bytes = rip->i_size;
170 	r = fsdriver_copyout(data, 0, b_data(bp), bytes);
171 	put_block(bp);
172 	if (r == OK)
173 		r = bytes;
174   }
175 
176   put_inode(rip);
177   return(r);
178 }
179 
180 
181 /*===========================================================================*
182  *				remove_dir				     *
183  *===========================================================================*/
remove_dir(rldirp,rip,dir_name)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 */
188 {
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)
195    */
196   int r;
197 
198   /* search_dir checks that rip is a directory too. */
199   if ((r = search_dir(rip, "", NULL, IS_EMPTY)) != OK)
200   	return(r);
201 
202   if (rip->i_num == ROOT_INODE) return(EBUSY); /* can't remove 'root' */
203 
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;
206 
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.
209    */
210   (void) unlink_file(rip, NULL, ".");
211   (void) unlink_file(rip, NULL, "..");
212   return(OK);
213 }
214 
215 
216 /*===========================================================================*
217  *				unlink_file				     *
218  *===========================================================================*/
unlink_file(dirp,rip,file_name)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 */
223 {
224 /* Unlink 'file_name'; rip must be the inode of 'file_name' or NULL. */
225 
226   ino_t numb;			/* inode number */
227   int	r;
228 
229   /* If rip is not NULL, it is used to get faster access to the inode. */
230   if (rip == NULL) {
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);
235   } else {
236 	dup_inode(rip);		/* inode will be returned with put_inode */
237   }
238 
239   r = search_dir(dirp, file_name, NULL, DELETE);
240 
241   if (r == OK) {
242 	rip->i_nlinks--;	/* entry deleted from parent's dir */
243 	rip->i_update |= CTIME;
244 	IN_MARKDIRTY(rip);
245   }
246 
247   put_inode(rip);
248   return(r);
249 }
250 
251 
252 /*===========================================================================*
253  *				fs_rename				     *
254  *===========================================================================*/
fs_rename(ino_t old_dir_nr,char * old_name,ino_t new_dir_nr,char * new_name)255 int fs_rename(ino_t old_dir_nr, char *old_name, ino_t new_dir_nr,
256 	char *new_name)
257 {
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 */
265   ino_t numb;
266 
267   /* Get old dir inode */
268   if ((old_dirp = get_inode(fs_dev, old_dir_nr)) == NULL)
269 	return(err_code);
270 
271   old_ip = advance(old_dirp, old_name);
272   r = err_code;
273 
274   if (old_ip == NULL) {
275 	put_inode(old_dirp);
276 	return(r);
277   }
278 
279   if (old_ip->i_mountpoint) {
280 	put_inode(old_ip);
281 	put_inode(old_dirp);
282 	return(EBUSY);
283   }
284 
285   /* Get new dir inode */
286   if ((new_dirp = get_inode(fs_dev, new_dir_nr)) == NULL) {
287         put_inode(old_ip);
288         put_inode(old_dirp);
289         return(err_code);
290   } else {
291 	if (new_dirp->i_nlinks == NO_LINK) { /* Dir does not actually exist */
292   		put_inode(old_ip);
293   		put_inode(old_dirp);
294   		put_inode(new_dirp);
295   		return(ENOENT);
296 	}
297   }
298 
299   new_ip = advance(new_dirp, new_name); /* not required to exist */
300 
301   /* If the node does exist, make sure it's not a mountpoint. */
302   if (new_ip != NULL && new_ip->i_mountpoint) {
303 	put_inode(new_ip);
304 	new_ip = NULL;
305 	r = EBUSY;
306   }
307 
308   odir = ((old_ip->i_mode & I_TYPE) == I_DIRECTORY); /* TRUE iff dir */
309 
310   /* If it is ok, check for a variety of possible errors. */
311   if(r == OK) {
312 	same_pdir = (old_dirp == new_dirp);
313 
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);
320 				r = EINVAL;
321 				break;
322 			}
323 			next_new_superdirp = advance(new_superdirp, "..");
324 
325 			put_inode(new_superdirp);
326 			if(next_new_superdirp == new_superdirp) {
327 				put_inode(new_superdirp);
328 				break;
329 			}
330 			if(next_new_superdirp->i_num == ROOT_INODE) {
331 				put_inode(next_new_superdirp);
332 				err_code = OK;
333 				break;
334 			}
335 			new_superdirp = next_new_superdirp;
336 			if(new_superdirp == NULL) {
337 				/* Missing ".." entry.  Assume the worst. */
338 				r = EINVAL;
339 				break;
340 			}
341 		}
342 	}
343 
344 	/* Some tests apply only if the new path exists. */
345 	if(new_ip == NULL) {
346 		if (odir && new_dirp->i_nlinks >= LINK_MAX &&
347 		    !same_pdir && r == OK) {
348 			r = EMLINK;
349 		}
350 	} else {
351 		if(old_ip == new_ip) r = SAME; /* old=new */
352 
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;
356 	}
357   }
358 
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. */
364 
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].
370    */
371   if(r == OK) {
372 	if(new_ip != NULL) {
373 		/* There is already an entry for 'new'. Try to remove it. */
374 		if(odir)
375 			r = remove_dir(new_dirp, new_ip, new_name);
376 		else
377 			r = unlink_file(new_dirp, new_ip, new_name);
378 	}
379 	/* if r is OK, the rename will succeed, while there is now an
380 	 * unused entry in the new parent directory. */
381   }
382 
383   if(r == OK) {
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.
388 	   */
389 	numb = old_ip->i_num;		/* inode number of old file */
390 
391 	if(same_pdir) {
392 		r = search_dir(old_dirp, old_name, NULL, DELETE);
393 						/* shouldn't go wrong. */
394 		if(r == OK)
395 			(void) search_dir(old_dirp, new_name, &numb, ENTER);
396 	} else {
397 		r = search_dir(new_dirp, new_name, &numb, ENTER);
398 		if(r == OK)
399 			(void) search_dir(old_dirp, old_name, NULL, DELETE);
400 	}
401   }
402   /* If r is OK, the ctime and mtime of old_dirp and new_dirp have been marked
403    * for update in search_dir. */
404 
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);
413 	}
414   }
415 
416   /* Release the inodes. */
417   put_inode(old_dirp);
418   put_inode(old_ip);
419   put_inode(new_dirp);
420   put_inode(new_ip);
421   return(r == SAME ? OK : r);
422 }
423 
424 
425 /*===========================================================================*
426  *				fs_trunc				     *
427  *===========================================================================*/
fs_trunc(ino_t ino_nr,off_t start,off_t end)428 int fs_trunc(ino_t ino_nr, off_t start, off_t end)
429 {
430   struct inode *rip;
431   int r;
432 
433   if( (rip = find_inode(fs_dev, ino_nr)) == NULL)
434 	  return(EINVAL);
435 
436   if(rip->i_sp->s_rd_only) {
437   	r = EROFS;
438   } else {
439     if (end == 0)
440 	  r = truncate_inode(rip, start);
441     else
442 	  r = freesp_inode(rip, start, end);
443   }
444 
445   return(r);
446 }
447 
448 
449 /*===========================================================================*
450  *				truncate_inode				     *
451  *===========================================================================*/
truncate_inode(rip,newsize)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 */
455 {
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.
459  *
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
462  * writing is done.
463  */
464   int r;
465   mode_t file_type;
466 
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)
469 	return(EINVAL);
470   if (newsize > rip->i_sp->s_max_size)	/* don't let inode grow too big */
471 	return(EFBIG);
472 
473   /* Free the actual space if truncating. */
474   if (newsize < rip->i_size) {
475   	if ((r = freesp_inode(rip, newsize, rip->i_size)) != OK)
476   		return(r);
477   }
478 
479   /* Clear the rest of the last zone if expanding. */
480   if (newsize > rip->i_size) clear_zone(rip, rip->i_size, 0);
481 
482   /* Next correct the inode size. */
483   rip->i_size = newsize;
484   rip->i_update |= CTIME | MTIME;
485   IN_MARKDIRTY(rip);
486 
487   return(OK);
488 }
489 
490 
491 /*===========================================================================*
492  *				freesp_inode				     *
493  *===========================================================================*/
freesp_inode(rip,start,end)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) */
497 {
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.
503  *
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
507  * fcntl().
508  */
509   off_t p, e;
510   int zone_size, r;
511   int zero_last, zero_first;
512 
513   if(end > rip->i_size)		/* freeing beyond end makes no sense */
514 	end = rip->i_size;
515   if(end <= start)		/* end is uninclusive, so start<end */
516 	return(EINVAL);
517 
518   zone_size = rip->i_sp->s_block_size << rip->i_sp->s_log_zone_size;
519 
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.
522    */
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);
527   } else {
528 	/* First zero unused part of partly used zones. */
529 	if(zero_last)
530 		zerozone_half(rip, start, LAST_HALF, zone_size);
531 	if(zero_first)
532 		zerozone_half(rip, end, FIRST_HALF, zone_size);
533 
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'.
538 	 */
539 	e = end/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)
543 			return(r);
544 	}
545 
546   }
547 
548   rip->i_update |= CTIME | MTIME;
549   IN_MARKDIRTY(rip);
550 
551   return(OK);
552 }
553 
554 
555 /*===========================================================================*
556  *				nextblock				     *
557  *===========================================================================*/
nextblock(pos,zone_size)558 static off_t nextblock(pos, zone_size)
559 off_t pos;
560 int zone_size;
561 {
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.
565  */
566   off_t p;
567   p = (pos/zone_size)*zone_size;
568   if((pos % zone_size)) p += zone_size;	/* Round up. */
569   return(p);
570 }
571 
572 
573 /*===========================================================================*
574  *				zerozone_half				     *
575  *===========================================================================*/
zerozone_half(rip,pos,half,zone_size)576 static void zerozone_half(rip, pos, half, zone_size)
577 struct inode *rip;
578 off_t pos;
579 int half;
580 int zone_size;
581 {
582 /* Zero the upper or lower 'half' of a zone that holds position 'pos'.
583  * half can be FIRST_HALF or LAST_HALF.
584  *
585  * FIRST_HALF: 0..pos-1 will be zeroed
586  * LAST_HALF:  pos..zone_size-1 will be zeroed
587  */
588   off_t offset, len;
589 
590   /* Offset of zeroing boundary. */
591   offset = pos % zone_size;
592 
593   if(half == LAST_HALF)  {
594    	len = zone_size - offset;
595   } else {
596 	len = offset;
597 	pos -= offset;
598   }
599 
600   zerozone_range(rip, pos, len);
601 }
602 
603 
604 /*===========================================================================*
605  *				zerozone_range				     *
606  *===========================================================================*/
zerozone_range(rip,pos,len)607 static void zerozone_range(rip, pos, len)
608 struct inode *rip;
609 off_t pos;
610 off_t len;
611 {
612 /* Zero an arbitrary byte range in a zone, possibly spanning multiple blocks.
613  */
614   struct buf *bp;
615   off_t offset;
616   unsigned short block_size;
617   size_t bytes;
618 
619   block_size = rip->i_sp->s_block_size;
620 
621   if(!len) return; /* no zeroing to be done. */
622 
623   while (len > 0) {
624 	if( (bp = get_block_map(rip, rounddown(pos, block_size))) == NULL)
625 		return;
626 	offset = pos % block_size;
627 	bytes = block_size - offset;
628 	if (bytes > (size_t) len)
629 		bytes = len;
630 	memset(b_data(bp) + offset, 0, bytes);
631 	MARKDIRTY(bp);
632 	put_block(bp);
633 
634 	pos += bytes;
635 	len -= bytes;
636   }
637 }
638 
639