1433d6423SLionel Sambuc #include "fs.h" 2433d6423SLionel Sambuc #include <sys/stat.h> 3433d6423SLionel Sambuc #include <string.h> 4433d6423SLionel Sambuc #include <minix/com.h> 5433d6423SLionel Sambuc #include "buf.h" 6433d6423SLionel Sambuc #include "inode.h" 7433d6423SLionel Sambuc #include "super.h" 8433d6423SLionel Sambuc #include <minix/vfsif.h> 9433d6423SLionel Sambuc #include <sys/param.h> 10433d6423SLionel Sambuc 11433d6423SLionel Sambuc #define SAME 1000 12433d6423SLionel Sambuc 13433d6423SLionel Sambuc 14433d6423SLionel Sambuc static int freesp_inode(struct inode *rip, off_t st, off_t end); 15433d6423SLionel Sambuc static int remove_dir(struct inode *rldirp, struct inode *rip, char 16433d6423SLionel Sambuc dir_name[MFS_NAME_MAX]); 17433d6423SLionel Sambuc static int unlink_file(struct inode *dirp, struct inode *rip, char 18433d6423SLionel Sambuc file_name[MFS_NAME_MAX]); 19433d6423SLionel Sambuc static off_t nextblock(off_t pos, int zone_size); 20433d6423SLionel Sambuc static void zerozone_half(struct inode *rip, off_t pos, int half, int 21433d6423SLionel Sambuc zone_size); 22433d6423SLionel Sambuc static void zerozone_range(struct inode *rip, off_t pos, off_t len); 23433d6423SLionel Sambuc 24433d6423SLionel Sambuc /* Args to zerozone_half() */ 25433d6423SLionel Sambuc #define FIRST_HALF 0 26433d6423SLionel Sambuc #define LAST_HALF 1 27433d6423SLionel Sambuc 28433d6423SLionel Sambuc 29433d6423SLionel Sambuc /*===========================================================================* 30433d6423SLionel Sambuc * fs_link * 31433d6423SLionel Sambuc *===========================================================================*/ 32*ccaeedb2SDavid van Moolenbroek int fs_link(ino_t dir_nr, char *name, ino_t ino_nr) 33433d6423SLionel Sambuc { 34433d6423SLionel Sambuc /* Perform the link(name1, name2) system call. */ 35433d6423SLionel Sambuc 36433d6423SLionel Sambuc struct inode *ip, *rip; 37433d6423SLionel Sambuc register int r; 38433d6423SLionel Sambuc struct inode *new_ip; 39433d6423SLionel Sambuc 40433d6423SLionel Sambuc /* Temporarily open the file. */ 41*ccaeedb2SDavid van Moolenbroek if( (rip = get_inode(fs_dev, ino_nr)) == NULL) 42433d6423SLionel Sambuc return(EINVAL); 43433d6423SLionel Sambuc 44433d6423SLionel Sambuc /* Check to see if the file has maximum number of links already. */ 45433d6423SLionel Sambuc r = OK; 46433d6423SLionel Sambuc if(rip->i_nlinks >= LINK_MAX) 47433d6423SLionel Sambuc r = EMLINK; 48433d6423SLionel Sambuc 49*ccaeedb2SDavid van Moolenbroek /* Linking to directories is too dangerous to allow. */ 50433d6423SLionel Sambuc if(r == OK) 51*ccaeedb2SDavid van Moolenbroek if( (rip->i_mode & I_TYPE) == I_DIRECTORY) 52433d6423SLionel Sambuc r = EPERM; 53433d6423SLionel Sambuc 54433d6423SLionel Sambuc /* If error with 'name', return the inode. */ 55433d6423SLionel Sambuc if (r != OK) { 56433d6423SLionel Sambuc put_inode(rip); 57433d6423SLionel Sambuc return(r); 58433d6423SLionel Sambuc } 59433d6423SLionel Sambuc 60433d6423SLionel Sambuc /* Temporarily open the last dir */ 61*ccaeedb2SDavid van Moolenbroek if( (ip = get_inode(fs_dev, dir_nr)) == NULL) { 62433d6423SLionel Sambuc put_inode(rip); 63433d6423SLionel Sambuc return(EINVAL); 64433d6423SLionel Sambuc } 65433d6423SLionel Sambuc 66433d6423SLionel Sambuc if (ip->i_nlinks == NO_LINK) { /* Dir does not actually exist */ 67433d6423SLionel Sambuc put_inode(rip); 68433d6423SLionel Sambuc put_inode(ip); 69433d6423SLionel Sambuc return(ENOENT); 70433d6423SLionel Sambuc } 71433d6423SLionel Sambuc 72433d6423SLionel Sambuc /* If 'name2' exists in full (even if no space) set 'r' to error. */ 73*ccaeedb2SDavid van Moolenbroek if((new_ip = advance(ip, name)) == NULL) { 74433d6423SLionel Sambuc r = err_code; 75433d6423SLionel Sambuc if(r == ENOENT) 76433d6423SLionel Sambuc r = OK; 77433d6423SLionel Sambuc } else { 78433d6423SLionel Sambuc put_inode(new_ip); 79433d6423SLionel Sambuc r = EEXIST; 80433d6423SLionel Sambuc } 81433d6423SLionel Sambuc 82433d6423SLionel Sambuc /* Try to link. */ 83433d6423SLionel Sambuc if(r == OK) 84*ccaeedb2SDavid van Moolenbroek r = search_dir(ip, name, &rip->i_num, ENTER); 85433d6423SLionel Sambuc 86433d6423SLionel Sambuc /* If success, register the linking. */ 87433d6423SLionel Sambuc if(r == OK) { 88433d6423SLionel Sambuc rip->i_nlinks++; 89433d6423SLionel Sambuc rip->i_update |= CTIME; 90433d6423SLionel Sambuc IN_MARKDIRTY(rip); 91433d6423SLionel Sambuc } 92433d6423SLionel Sambuc 93433d6423SLionel Sambuc /* Done. Release both inodes. */ 94433d6423SLionel Sambuc put_inode(rip); 95433d6423SLionel Sambuc put_inode(ip); 96433d6423SLionel Sambuc return(r); 97433d6423SLionel Sambuc } 98433d6423SLionel Sambuc 99433d6423SLionel Sambuc 100433d6423SLionel Sambuc /*===========================================================================* 101433d6423SLionel Sambuc * fs_unlink * 102433d6423SLionel Sambuc *===========================================================================*/ 103*ccaeedb2SDavid van Moolenbroek int fs_unlink(ino_t dir_nr, char *name, int call) 104433d6423SLionel Sambuc { 105433d6423SLionel Sambuc /* Perform the unlink(name) or rmdir(name) system call. The code for these two 106*ccaeedb2SDavid van Moolenbroek * is almost the same. They differ only in some condition testing. 107433d6423SLionel Sambuc */ 108433d6423SLionel Sambuc register struct inode *rip; 109433d6423SLionel Sambuc struct inode *rldirp; 110433d6423SLionel Sambuc int r; 111433d6423SLionel Sambuc 112433d6423SLionel Sambuc /* Temporarily open the dir. */ 113*ccaeedb2SDavid van Moolenbroek if((rldirp = get_inode(fs_dev, dir_nr)) == NULL) 114433d6423SLionel Sambuc return(EINVAL); 115433d6423SLionel Sambuc 116433d6423SLionel Sambuc /* The last directory exists. Does the file also exist? */ 117*ccaeedb2SDavid van Moolenbroek rip = advance(rldirp, name); 118433d6423SLionel Sambuc r = err_code; 119433d6423SLionel Sambuc 120433d6423SLionel Sambuc /* If error, return inode. */ 121433d6423SLionel Sambuc if(r != OK) { 122433d6423SLionel Sambuc put_inode(rldirp); 123433d6423SLionel Sambuc return(r); 124433d6423SLionel Sambuc } 125*ccaeedb2SDavid van Moolenbroek if (rip->i_mountpoint) { 126*ccaeedb2SDavid van Moolenbroek put_inode(rip); 127*ccaeedb2SDavid van Moolenbroek put_inode(rldirp); 128*ccaeedb2SDavid van Moolenbroek return(EBUSY); 129*ccaeedb2SDavid van Moolenbroek } 130433d6423SLionel Sambuc 131433d6423SLionel Sambuc if(rip->i_sp->s_rd_only) { 132433d6423SLionel Sambuc r = EROFS; 133*ccaeedb2SDavid van Moolenbroek } else if (call == FSC_UNLINK) { 134433d6423SLionel Sambuc if( (rip->i_mode & I_TYPE) == I_DIRECTORY) r = EPERM; 135433d6423SLionel Sambuc 136433d6423SLionel Sambuc /* Actually try to unlink the file; fails if parent is mode 0 etc. */ 137*ccaeedb2SDavid van Moolenbroek if (r == OK) r = unlink_file(rldirp, rip, name); 138433d6423SLionel Sambuc } else { 139*ccaeedb2SDavid van Moolenbroek r = remove_dir(rldirp, rip, name); /* call is RMDIR */ 140433d6423SLionel Sambuc } 141433d6423SLionel Sambuc 142433d6423SLionel Sambuc /* If unlink was possible, it has been done, otherwise it has not. */ 143433d6423SLionel Sambuc put_inode(rip); 144433d6423SLionel Sambuc put_inode(rldirp); 145433d6423SLionel Sambuc return(r); 146433d6423SLionel Sambuc } 147433d6423SLionel Sambuc 148433d6423SLionel Sambuc 149433d6423SLionel Sambuc /*===========================================================================* 150433d6423SLionel Sambuc * fs_rdlink * 151433d6423SLionel Sambuc *===========================================================================*/ 152*ccaeedb2SDavid van Moolenbroek ssize_t fs_rdlink(ino_t ino_nr, struct fsdriver_data *data, size_t bytes) 153433d6423SLionel Sambuc { 154433d6423SLionel Sambuc struct buf *bp; /* buffer containing link text */ 155433d6423SLionel Sambuc register struct inode *rip; /* target inode */ 156433d6423SLionel Sambuc register int r; /* return value */ 157433d6423SLionel Sambuc 158433d6423SLionel Sambuc /* Temporarily open the file. */ 159*ccaeedb2SDavid van Moolenbroek if( (rip = get_inode(fs_dev, ino_nr)) == NULL) 160433d6423SLionel Sambuc return(EINVAL); 161433d6423SLionel Sambuc 162433d6423SLionel Sambuc if(!S_ISLNK(rip->i_mode)) 163433d6423SLionel Sambuc r = EACCES; 164433d6423SLionel Sambuc else { 165433d6423SLionel Sambuc if(!(bp = get_block_map(rip, 0))) 166433d6423SLionel Sambuc return EIO; 167433d6423SLionel Sambuc /* Passed all checks */ 168*ccaeedb2SDavid van Moolenbroek if (bytes > rip->i_size) 169*ccaeedb2SDavid van Moolenbroek bytes = rip->i_size; 170*ccaeedb2SDavid van Moolenbroek r = fsdriver_copyout(data, 0, b_data(bp), bytes); 171433d6423SLionel Sambuc put_block(bp, DIRECTORY_BLOCK); 172433d6423SLionel Sambuc if (r == OK) 173*ccaeedb2SDavid van Moolenbroek r = bytes; 174433d6423SLionel Sambuc } 175433d6423SLionel Sambuc 176433d6423SLionel Sambuc put_inode(rip); 177433d6423SLionel Sambuc return(r); 178433d6423SLionel Sambuc } 179433d6423SLionel Sambuc 180433d6423SLionel Sambuc 181433d6423SLionel Sambuc /*===========================================================================* 182433d6423SLionel Sambuc * remove_dir * 183433d6423SLionel Sambuc *===========================================================================*/ 184433d6423SLionel Sambuc static int remove_dir(rldirp, rip, dir_name) 185433d6423SLionel Sambuc struct inode *rldirp; /* parent directory */ 186433d6423SLionel Sambuc struct inode *rip; /* directory to be removed */ 187433d6423SLionel Sambuc char dir_name[MFS_NAME_MAX]; /* name of directory to be removed */ 188433d6423SLionel Sambuc { 189433d6423SLionel Sambuc /* A directory file has to be removed. Five conditions have to met: 190433d6423SLionel Sambuc * - The file must be a directory 191433d6423SLionel Sambuc * - The directory must be empty (except for . and ..) 192433d6423SLionel Sambuc * - The final component of the path must not be . or .. 193433d6423SLionel Sambuc * - The directory must not be the root of a mounted file system (VFS) 194433d6423SLionel Sambuc * - The directory must not be anybody's root/working directory (VFS) 195433d6423SLionel Sambuc */ 196433d6423SLionel Sambuc int r; 197433d6423SLionel Sambuc 198433d6423SLionel Sambuc /* search_dir checks that rip is a directory too. */ 199*ccaeedb2SDavid van Moolenbroek if ((r = search_dir(rip, "", NULL, IS_EMPTY)) != OK) 200433d6423SLionel Sambuc return(r); 201433d6423SLionel Sambuc 202433d6423SLionel Sambuc if (rip->i_num == ROOT_INODE) return(EBUSY); /* can't remove 'root' */ 203433d6423SLionel Sambuc 204433d6423SLionel Sambuc /* Actually try to unlink the file; fails if parent is mode 0 etc. */ 205433d6423SLionel Sambuc if ((r = unlink_file(rldirp, rip, dir_name)) != OK) return r; 206433d6423SLionel Sambuc 207433d6423SLionel Sambuc /* Unlink . and .. from the dir. The super user can link and unlink any dir, 208433d6423SLionel Sambuc * so don't make too many assumptions about them. 209433d6423SLionel Sambuc */ 210*ccaeedb2SDavid van Moolenbroek (void) unlink_file(rip, NULL, "."); 211*ccaeedb2SDavid van Moolenbroek (void) unlink_file(rip, NULL, ".."); 212433d6423SLionel Sambuc return(OK); 213433d6423SLionel Sambuc } 214433d6423SLionel Sambuc 215433d6423SLionel Sambuc 216433d6423SLionel Sambuc /*===========================================================================* 217433d6423SLionel Sambuc * unlink_file * 218433d6423SLionel Sambuc *===========================================================================*/ 219433d6423SLionel Sambuc static int unlink_file(dirp, rip, file_name) 220433d6423SLionel Sambuc struct inode *dirp; /* parent directory of file */ 221433d6423SLionel Sambuc struct inode *rip; /* inode of file, may be NULL too. */ 222433d6423SLionel Sambuc char file_name[MFS_NAME_MAX]; /* name of file to be removed */ 223433d6423SLionel Sambuc { 224433d6423SLionel Sambuc /* Unlink 'file_name'; rip must be the inode of 'file_name' or NULL. */ 225433d6423SLionel Sambuc 226433d6423SLionel Sambuc ino_t numb; /* inode number */ 227433d6423SLionel Sambuc int r; 228433d6423SLionel Sambuc 229433d6423SLionel Sambuc /* If rip is not NULL, it is used to get faster access to the inode. */ 230433d6423SLionel Sambuc if (rip == NULL) { 231433d6423SLionel Sambuc /* Search for file in directory and try to get its inode. */ 232*ccaeedb2SDavid van Moolenbroek err_code = search_dir(dirp, file_name, &numb, LOOK_UP); 233433d6423SLionel Sambuc if (err_code == OK) rip = get_inode(dirp->i_dev, (int) numb); 234433d6423SLionel Sambuc if (err_code != OK || rip == NULL) return(err_code); 235433d6423SLionel Sambuc } else { 236433d6423SLionel Sambuc dup_inode(rip); /* inode will be returned with put_inode */ 237433d6423SLionel Sambuc } 238433d6423SLionel Sambuc 239*ccaeedb2SDavid van Moolenbroek r = search_dir(dirp, file_name, NULL, DELETE); 240433d6423SLionel Sambuc 241433d6423SLionel Sambuc if (r == OK) { 242433d6423SLionel Sambuc rip->i_nlinks--; /* entry deleted from parent's dir */ 243433d6423SLionel Sambuc rip->i_update |= CTIME; 244433d6423SLionel Sambuc IN_MARKDIRTY(rip); 245433d6423SLionel Sambuc } 246433d6423SLionel Sambuc 247433d6423SLionel Sambuc put_inode(rip); 248433d6423SLionel Sambuc return(r); 249433d6423SLionel Sambuc } 250433d6423SLionel Sambuc 251433d6423SLionel Sambuc 252433d6423SLionel Sambuc /*===========================================================================* 253433d6423SLionel Sambuc * fs_rename * 254433d6423SLionel Sambuc *===========================================================================*/ 255*ccaeedb2SDavid van Moolenbroek int fs_rename(ino_t old_dir_nr, char *old_name, ino_t new_dir_nr, 256*ccaeedb2SDavid van Moolenbroek char *new_name) 257433d6423SLionel Sambuc { 258433d6423SLionel Sambuc /* Perform the rename(name1, name2) system call. */ 259433d6423SLionel Sambuc struct inode *old_dirp, *old_ip; /* ptrs to old dir, file inodes */ 260433d6423SLionel Sambuc struct inode *new_dirp, *new_ip; /* ptrs to new dir, file inodes */ 261433d6423SLionel Sambuc struct inode *new_superdirp, *next_new_superdirp; 262433d6423SLionel Sambuc int r = OK; /* error flag; initially no error */ 263433d6423SLionel Sambuc int odir, ndir; /* TRUE iff {old|new} file is dir */ 264433d6423SLionel Sambuc int same_pdir; /* TRUE iff parent dirs are the same */ 265433d6423SLionel Sambuc ino_t numb; 266433d6423SLionel Sambuc 267433d6423SLionel Sambuc /* Get old dir inode */ 268*ccaeedb2SDavid van Moolenbroek if ((old_dirp = get_inode(fs_dev, old_dir_nr)) == NULL) 269433d6423SLionel Sambuc return(err_code); 270433d6423SLionel Sambuc 271*ccaeedb2SDavid van Moolenbroek old_ip = advance(old_dirp, old_name); 272433d6423SLionel Sambuc r = err_code; 273433d6423SLionel Sambuc 274433d6423SLionel Sambuc if (old_ip == NULL) { 275433d6423SLionel Sambuc put_inode(old_dirp); 276433d6423SLionel Sambuc return(r); 277433d6423SLionel Sambuc } 278433d6423SLionel Sambuc 279*ccaeedb2SDavid van Moolenbroek if (old_ip->i_mountpoint) { 280*ccaeedb2SDavid van Moolenbroek put_inode(old_ip); 281*ccaeedb2SDavid van Moolenbroek put_inode(old_dirp); 282*ccaeedb2SDavid van Moolenbroek return(EBUSY); 283*ccaeedb2SDavid van Moolenbroek } 284*ccaeedb2SDavid van Moolenbroek 285433d6423SLionel Sambuc /* Get new dir inode */ 286*ccaeedb2SDavid van Moolenbroek if ((new_dirp = get_inode(fs_dev, new_dir_nr)) == NULL) { 287433d6423SLionel Sambuc put_inode(old_ip); 288433d6423SLionel Sambuc put_inode(old_dirp); 289433d6423SLionel Sambuc return(err_code); 290433d6423SLionel Sambuc } else { 291433d6423SLionel Sambuc if (new_dirp->i_nlinks == NO_LINK) { /* Dir does not actually exist */ 292433d6423SLionel Sambuc put_inode(old_ip); 293433d6423SLionel Sambuc put_inode(old_dirp); 294433d6423SLionel Sambuc put_inode(new_dirp); 295433d6423SLionel Sambuc return(ENOENT); 296433d6423SLionel Sambuc } 297433d6423SLionel Sambuc } 298433d6423SLionel Sambuc 299*ccaeedb2SDavid van Moolenbroek new_ip = advance(new_dirp, new_name); /* not required to exist */ 300433d6423SLionel Sambuc 301*ccaeedb2SDavid van Moolenbroek /* If the node does exist, make sure it's not a mountpoint. */ 302*ccaeedb2SDavid van Moolenbroek if (new_ip != NULL && new_ip->i_mountpoint) { 303433d6423SLionel Sambuc put_inode(new_ip); 304433d6423SLionel Sambuc new_ip = NULL; 305433d6423SLionel Sambuc r = EBUSY; 306433d6423SLionel Sambuc } 307433d6423SLionel Sambuc 308433d6423SLionel Sambuc odir = ((old_ip->i_mode & I_TYPE) == I_DIRECTORY); /* TRUE iff dir */ 309433d6423SLionel Sambuc 310433d6423SLionel Sambuc /* If it is ok, check for a variety of possible errors. */ 311433d6423SLionel Sambuc if(r == OK) { 312433d6423SLionel Sambuc same_pdir = (old_dirp == new_dirp); 313433d6423SLionel Sambuc 314433d6423SLionel Sambuc /* The old inode must not be a superdirectory of the new last dir. */ 315433d6423SLionel Sambuc if (odir && !same_pdir) { 316433d6423SLionel Sambuc dup_inode(new_superdirp = new_dirp); 317433d6423SLionel Sambuc while (TRUE) { /* may hang in a file system loop */ 318433d6423SLionel Sambuc if (new_superdirp == old_ip) { 319433d6423SLionel Sambuc put_inode(new_superdirp); 320433d6423SLionel Sambuc r = EINVAL; 321433d6423SLionel Sambuc break; 322433d6423SLionel Sambuc } 323*ccaeedb2SDavid van Moolenbroek next_new_superdirp = advance(new_superdirp, ".."); 324433d6423SLionel Sambuc 325433d6423SLionel Sambuc put_inode(new_superdirp); 326433d6423SLionel Sambuc if(next_new_superdirp == new_superdirp) { 327433d6423SLionel Sambuc put_inode(new_superdirp); 328433d6423SLionel Sambuc break; 329433d6423SLionel Sambuc } 330*ccaeedb2SDavid van Moolenbroek if(next_new_superdirp->i_num == ROOT_INODE) { 331433d6423SLionel Sambuc put_inode(next_new_superdirp); 332433d6423SLionel Sambuc err_code = OK; 333433d6423SLionel Sambuc break; 334433d6423SLionel Sambuc } 335433d6423SLionel Sambuc new_superdirp = next_new_superdirp; 336433d6423SLionel Sambuc if(new_superdirp == NULL) { 337433d6423SLionel Sambuc /* Missing ".." entry. Assume the worst. */ 338433d6423SLionel Sambuc r = EINVAL; 339433d6423SLionel Sambuc break; 340433d6423SLionel Sambuc } 341433d6423SLionel Sambuc } 342433d6423SLionel Sambuc } 343433d6423SLionel Sambuc 344433d6423SLionel Sambuc /* Some tests apply only if the new path exists. */ 345433d6423SLionel Sambuc if(new_ip == NULL) { 346433d6423SLionel Sambuc if (odir && new_dirp->i_nlinks >= LINK_MAX && 347433d6423SLionel Sambuc !same_pdir && r == OK) { 348433d6423SLionel Sambuc r = EMLINK; 349433d6423SLionel Sambuc } 350433d6423SLionel Sambuc } else { 351433d6423SLionel Sambuc if(old_ip == new_ip) r = SAME; /* old=new */ 352433d6423SLionel Sambuc 353433d6423SLionel Sambuc ndir = ((new_ip->i_mode & I_TYPE) == I_DIRECTORY);/* dir ? */ 354433d6423SLionel Sambuc if(odir == TRUE && ndir == FALSE) r = ENOTDIR; 355433d6423SLionel Sambuc if(odir == FALSE && ndir == TRUE) r = EISDIR; 356433d6423SLionel Sambuc } 357433d6423SLionel Sambuc } 358433d6423SLionel Sambuc 359433d6423SLionel Sambuc /* If a process has another root directory than the system root, we might 360433d6423SLionel Sambuc * "accidently" be moving it's working directory to a place where it's 361433d6423SLionel Sambuc * root directory isn't a super directory of it anymore. This can make 362433d6423SLionel Sambuc * the function chroot useless. If chroot will be used often we should 363433d6423SLionel Sambuc * probably check for it here. */ 364433d6423SLionel Sambuc 365433d6423SLionel Sambuc /* The rename will probably work. Only two things can go wrong now: 366433d6423SLionel Sambuc * 1. being unable to remove the new file. (when new file already exists) 367433d6423SLionel Sambuc * 2. being unable to make the new directory entry. (new file doesn't exists) 368433d6423SLionel Sambuc * [directory has to grow by one block and cannot because the disk 369433d6423SLionel Sambuc * is completely full]. 370433d6423SLionel Sambuc */ 371433d6423SLionel Sambuc if(r == OK) { 372433d6423SLionel Sambuc if(new_ip != NULL) { 373433d6423SLionel Sambuc /* There is already an entry for 'new'. Try to remove it. */ 374433d6423SLionel Sambuc if(odir) 375433d6423SLionel Sambuc r = remove_dir(new_dirp, new_ip, new_name); 376433d6423SLionel Sambuc else 377433d6423SLionel Sambuc r = unlink_file(new_dirp, new_ip, new_name); 378433d6423SLionel Sambuc } 379433d6423SLionel Sambuc /* if r is OK, the rename will succeed, while there is now an 380433d6423SLionel Sambuc * unused entry in the new parent directory. */ 381433d6423SLionel Sambuc } 382433d6423SLionel Sambuc 383433d6423SLionel Sambuc if(r == OK) { 384433d6423SLionel Sambuc /* If the new name will be in the same parent directory as the old 385433d6423SLionel Sambuc * one, first remove the old name to free an entry for the new name, 386433d6423SLionel Sambuc * otherwise first try to create the new name entry to make sure 387433d6423SLionel Sambuc * the rename will succeed. 388433d6423SLionel Sambuc */ 389433d6423SLionel Sambuc numb = old_ip->i_num; /* inode number of old file */ 390433d6423SLionel Sambuc 391433d6423SLionel Sambuc if(same_pdir) { 392*ccaeedb2SDavid van Moolenbroek r = search_dir(old_dirp, old_name, NULL, DELETE); 393433d6423SLionel Sambuc /* shouldn't go wrong. */ 394433d6423SLionel Sambuc if(r == OK) 395*ccaeedb2SDavid van Moolenbroek (void) search_dir(old_dirp, new_name, &numb, ENTER); 396433d6423SLionel Sambuc } else { 397*ccaeedb2SDavid van Moolenbroek r = search_dir(new_dirp, new_name, &numb, ENTER); 398433d6423SLionel Sambuc if(r == OK) 399*ccaeedb2SDavid van Moolenbroek (void) search_dir(old_dirp, old_name, NULL, DELETE); 400433d6423SLionel Sambuc } 401433d6423SLionel Sambuc } 402433d6423SLionel Sambuc /* If r is OK, the ctime and mtime of old_dirp and new_dirp have been marked 403433d6423SLionel Sambuc * for update in search_dir. */ 404433d6423SLionel Sambuc 405433d6423SLionel Sambuc if(r == OK && odir && !same_pdir) { 406433d6423SLionel Sambuc /* Update the .. entry in the directory (still points to old_dirp).*/ 407433d6423SLionel Sambuc numb = new_dirp->i_num; 408*ccaeedb2SDavid van Moolenbroek (void) unlink_file(old_ip, NULL, ".."); 409*ccaeedb2SDavid van Moolenbroek if(search_dir(old_ip, "..", &numb, ENTER) == OK) { 410433d6423SLionel Sambuc /* New link created. */ 411433d6423SLionel Sambuc new_dirp->i_nlinks++; 412433d6423SLionel Sambuc IN_MARKDIRTY(new_dirp); 413433d6423SLionel Sambuc } 414433d6423SLionel Sambuc } 415433d6423SLionel Sambuc 416433d6423SLionel Sambuc /* Release the inodes. */ 417433d6423SLionel Sambuc put_inode(old_dirp); 418433d6423SLionel Sambuc put_inode(old_ip); 419433d6423SLionel Sambuc put_inode(new_dirp); 420433d6423SLionel Sambuc put_inode(new_ip); 421433d6423SLionel Sambuc return(r == SAME ? OK : r); 422433d6423SLionel Sambuc } 423433d6423SLionel Sambuc 424433d6423SLionel Sambuc 425433d6423SLionel Sambuc /*===========================================================================* 426*ccaeedb2SDavid van Moolenbroek * fs_trunc * 427433d6423SLionel Sambuc *===========================================================================*/ 428*ccaeedb2SDavid van Moolenbroek int fs_trunc(ino_t ino_nr, off_t start, off_t end) 429433d6423SLionel Sambuc { 430433d6423SLionel Sambuc struct inode *rip; 431433d6423SLionel Sambuc int r; 432433d6423SLionel Sambuc 433*ccaeedb2SDavid van Moolenbroek if( (rip = find_inode(fs_dev, ino_nr)) == NULL) 434433d6423SLionel Sambuc return(EINVAL); 435433d6423SLionel Sambuc 436433d6423SLionel Sambuc if(rip->i_sp->s_rd_only) { 437433d6423SLionel Sambuc r = EROFS; 438433d6423SLionel Sambuc } else { 439433d6423SLionel Sambuc if (end == 0) 440433d6423SLionel Sambuc r = truncate_inode(rip, start); 441433d6423SLionel Sambuc else 442433d6423SLionel Sambuc r = freesp_inode(rip, start, end); 443433d6423SLionel Sambuc } 444433d6423SLionel Sambuc 445433d6423SLionel Sambuc return(r); 446433d6423SLionel Sambuc } 447433d6423SLionel Sambuc 448433d6423SLionel Sambuc 449433d6423SLionel Sambuc /*===========================================================================* 450433d6423SLionel Sambuc * truncate_inode * 451433d6423SLionel Sambuc *===========================================================================*/ 452433d6423SLionel Sambuc int truncate_inode(rip, newsize) 453433d6423SLionel Sambuc register struct inode *rip; /* pointer to inode to be truncated */ 454433d6423SLionel Sambuc off_t newsize; /* inode must become this size */ 455433d6423SLionel Sambuc { 456433d6423SLionel Sambuc /* Set inode to a certain size, freeing any zones no longer referenced 457433d6423SLionel Sambuc * and updating the size in the inode. If the inode is extended, the 458433d6423SLionel Sambuc * extra space is a hole that reads as zeroes. 459433d6423SLionel Sambuc * 460433d6423SLionel Sambuc * Nothing special has to happen to file pointers if inode is opened in 461433d6423SLionel Sambuc * O_APPEND mode, as this is different per fd and is checked when 462433d6423SLionel Sambuc * writing is done. 463433d6423SLionel Sambuc */ 464433d6423SLionel Sambuc int r; 465433d6423SLionel Sambuc mode_t file_type; 466433d6423SLionel Sambuc 467433d6423SLionel Sambuc file_type = rip->i_mode & I_TYPE; /* check to see if file is special */ 468433d6423SLionel Sambuc if (file_type == I_CHAR_SPECIAL || file_type == I_BLOCK_SPECIAL) 469433d6423SLionel Sambuc return(EINVAL); 470433d6423SLionel Sambuc if (newsize > rip->i_sp->s_max_size) /* don't let inode grow too big */ 471433d6423SLionel Sambuc return(EFBIG); 472433d6423SLionel Sambuc 473433d6423SLionel Sambuc /* Free the actual space if truncating. */ 474433d6423SLionel Sambuc if (newsize < rip->i_size) { 475433d6423SLionel Sambuc if ((r = freesp_inode(rip, newsize, rip->i_size)) != OK) 476433d6423SLionel Sambuc return(r); 477433d6423SLionel Sambuc } 478433d6423SLionel Sambuc 479433d6423SLionel Sambuc /* Clear the rest of the last zone if expanding. */ 480433d6423SLionel Sambuc if (newsize > rip->i_size) clear_zone(rip, rip->i_size, 0); 481433d6423SLionel Sambuc 482433d6423SLionel Sambuc /* Next correct the inode size. */ 483433d6423SLionel Sambuc rip->i_size = newsize; 484433d6423SLionel Sambuc rip->i_update |= CTIME | MTIME; 485433d6423SLionel Sambuc IN_MARKDIRTY(rip); 486433d6423SLionel Sambuc 487433d6423SLionel Sambuc return(OK); 488433d6423SLionel Sambuc } 489433d6423SLionel Sambuc 490433d6423SLionel Sambuc 491433d6423SLionel Sambuc /*===========================================================================* 492433d6423SLionel Sambuc * freesp_inode * 493433d6423SLionel Sambuc *===========================================================================*/ 494433d6423SLionel Sambuc static int freesp_inode(rip, start, end) 495433d6423SLionel Sambuc register struct inode *rip; /* pointer to inode to be partly freed */ 496433d6423SLionel Sambuc off_t start, end; /* range of bytes to free (end uninclusive) */ 497433d6423SLionel Sambuc { 498433d6423SLionel Sambuc /* Cut an arbitrary hole in an inode. The caller is responsible for checking 499433d6423SLionel Sambuc * the reasonableness of the inode type of rip. The reason is this is that 500433d6423SLionel Sambuc * this function can be called for different reasons, for which different 501433d6423SLionel Sambuc * sets of inode types are reasonable. Adjusting the final size of the inode 502433d6423SLionel Sambuc * is to be done by the caller too, if wished. 503433d6423SLionel Sambuc * 504433d6423SLionel Sambuc * Consumers of this function currently are truncate_inode() (used to 505433d6423SLionel Sambuc * free indirect and data blocks for any type of inode, but also to 506433d6423SLionel Sambuc * implement the ftruncate() and truncate() system calls) and the F_FREESP 507433d6423SLionel Sambuc * fcntl(). 508433d6423SLionel Sambuc */ 509433d6423SLionel Sambuc off_t p, e; 510433d6423SLionel Sambuc int zone_size, r; 511433d6423SLionel Sambuc int zero_last, zero_first; 512433d6423SLionel Sambuc 513433d6423SLionel Sambuc if(end > rip->i_size) /* freeing beyond end makes no sense */ 514433d6423SLionel Sambuc end = rip->i_size; 515433d6423SLionel Sambuc if(end <= start) /* end is uninclusive, so start<end */ 516433d6423SLionel Sambuc return(EINVAL); 517433d6423SLionel Sambuc 518433d6423SLionel Sambuc zone_size = rip->i_sp->s_block_size << rip->i_sp->s_log_zone_size; 519433d6423SLionel Sambuc 520433d6423SLionel Sambuc /* If freeing doesn't cross a zone boundary, then we may only zero 521433d6423SLionel Sambuc * a range of the zone, unless we are freeing up that entire zone. 522433d6423SLionel Sambuc */ 523433d6423SLionel Sambuc zero_last = start % zone_size; 524433d6423SLionel Sambuc zero_first = end % zone_size && end < rip->i_size; 525433d6423SLionel Sambuc if(start/zone_size == (end-1)/zone_size && (zero_last || zero_first)) { 526433d6423SLionel Sambuc zerozone_range(rip, start, end-start); 527433d6423SLionel Sambuc } else { 528433d6423SLionel Sambuc /* First zero unused part of partly used zones. */ 529433d6423SLionel Sambuc if(zero_last) 530433d6423SLionel Sambuc zerozone_half(rip, start, LAST_HALF, zone_size); 531433d6423SLionel Sambuc if(zero_first) 532433d6423SLionel Sambuc zerozone_half(rip, end, FIRST_HALF, zone_size); 533433d6423SLionel Sambuc 534433d6423SLionel Sambuc /* Now completely free the completely unused zones. 535433d6423SLionel Sambuc * write_map() will free unused (double) indirect 536433d6423SLionel Sambuc * blocks too. Converting the range to zone numbers avoids 537433d6423SLionel Sambuc * overflow on p when doing e.g. 'p += zone_size'. 538433d6423SLionel Sambuc */ 539433d6423SLionel Sambuc e = end/zone_size; 540433d6423SLionel Sambuc if(end == rip->i_size && (end % zone_size)) e++; 541433d6423SLionel Sambuc for(p = nextblock(start, zone_size)/zone_size; p < e; p ++) { 542433d6423SLionel Sambuc if((r = write_map(rip, p*zone_size, NO_ZONE, WMAP_FREE)) != OK) 543433d6423SLionel Sambuc return(r); 544433d6423SLionel Sambuc } 545433d6423SLionel Sambuc 546433d6423SLionel Sambuc } 547433d6423SLionel Sambuc 548433d6423SLionel Sambuc rip->i_update |= CTIME | MTIME; 549433d6423SLionel Sambuc IN_MARKDIRTY(rip); 550433d6423SLionel Sambuc 551433d6423SLionel Sambuc return(OK); 552433d6423SLionel Sambuc } 553433d6423SLionel Sambuc 554433d6423SLionel Sambuc 555433d6423SLionel Sambuc /*===========================================================================* 556433d6423SLionel Sambuc * nextblock * 557433d6423SLionel Sambuc *===========================================================================*/ 558433d6423SLionel Sambuc static off_t nextblock(pos, zone_size) 559433d6423SLionel Sambuc off_t pos; 560433d6423SLionel Sambuc int zone_size; 561433d6423SLionel Sambuc { 562433d6423SLionel Sambuc /* Return the first position in the next block after position 'pos' 563433d6423SLionel Sambuc * (unless this is the first position in the current block). 564433d6423SLionel Sambuc * This can be done in one expression, but that can overflow pos. 565433d6423SLionel Sambuc */ 566433d6423SLionel Sambuc off_t p; 567433d6423SLionel Sambuc p = (pos/zone_size)*zone_size; 568433d6423SLionel Sambuc if((pos % zone_size)) p += zone_size; /* Round up. */ 569433d6423SLionel Sambuc return(p); 570433d6423SLionel Sambuc } 571433d6423SLionel Sambuc 572433d6423SLionel Sambuc 573433d6423SLionel Sambuc /*===========================================================================* 574433d6423SLionel Sambuc * zerozone_half * 575433d6423SLionel Sambuc *===========================================================================*/ 576433d6423SLionel Sambuc static void zerozone_half(rip, pos, half, zone_size) 577433d6423SLionel Sambuc struct inode *rip; 578433d6423SLionel Sambuc off_t pos; 579433d6423SLionel Sambuc int half; 580433d6423SLionel Sambuc int zone_size; 581433d6423SLionel Sambuc { 582433d6423SLionel Sambuc /* Zero the upper or lower 'half' of a zone that holds position 'pos'. 583433d6423SLionel Sambuc * half can be FIRST_HALF or LAST_HALF. 584433d6423SLionel Sambuc * 585433d6423SLionel Sambuc * FIRST_HALF: 0..pos-1 will be zeroed 586433d6423SLionel Sambuc * LAST_HALF: pos..zone_size-1 will be zeroed 587433d6423SLionel Sambuc */ 588433d6423SLionel Sambuc off_t offset, len; 589433d6423SLionel Sambuc 590433d6423SLionel Sambuc /* Offset of zeroing boundary. */ 591433d6423SLionel Sambuc offset = pos % zone_size; 592433d6423SLionel Sambuc 593433d6423SLionel Sambuc if(half == LAST_HALF) { 594433d6423SLionel Sambuc len = zone_size - offset; 595433d6423SLionel Sambuc } else { 596433d6423SLionel Sambuc len = offset; 597433d6423SLionel Sambuc pos -= offset; 598433d6423SLionel Sambuc } 599433d6423SLionel Sambuc 600433d6423SLionel Sambuc zerozone_range(rip, pos, len); 601433d6423SLionel Sambuc } 602433d6423SLionel Sambuc 603433d6423SLionel Sambuc 604433d6423SLionel Sambuc /*===========================================================================* 605433d6423SLionel Sambuc * zerozone_range * 606433d6423SLionel Sambuc *===========================================================================*/ 607433d6423SLionel Sambuc static void zerozone_range(rip, pos, len) 608433d6423SLionel Sambuc struct inode *rip; 609433d6423SLionel Sambuc off_t pos; 610433d6423SLionel Sambuc off_t len; 611433d6423SLionel Sambuc { 612433d6423SLionel Sambuc /* Zero an arbitrary byte range in a zone, possibly spanning multiple blocks. 613433d6423SLionel Sambuc */ 614433d6423SLionel Sambuc struct buf *bp; 615433d6423SLionel Sambuc off_t offset; 616433d6423SLionel Sambuc unsigned short block_size; 617433d6423SLionel Sambuc size_t bytes; 618433d6423SLionel Sambuc 619433d6423SLionel Sambuc block_size = rip->i_sp->s_block_size; 620433d6423SLionel Sambuc 621433d6423SLionel Sambuc if(!len) return; /* no zeroing to be done. */ 622433d6423SLionel Sambuc 623433d6423SLionel Sambuc while (len > 0) { 624433d6423SLionel Sambuc if( (bp = get_block_map(rip, rounddown(pos, block_size))) == NULL) 625433d6423SLionel Sambuc return; 626433d6423SLionel Sambuc offset = pos % block_size; 627433d6423SLionel Sambuc bytes = block_size - offset; 628433d6423SLionel Sambuc if (bytes > (size_t) len) 629433d6423SLionel Sambuc bytes = len; 630433d6423SLionel Sambuc memset(b_data(bp) + offset, 0, bytes); 631433d6423SLionel Sambuc MARKDIRTY(bp); 632433d6423SLionel Sambuc put_block(bp, FULL_DATA_BLOCK); 633433d6423SLionel Sambuc 634433d6423SLionel Sambuc pos += bytes; 635433d6423SLionel Sambuc len -= bytes; 636433d6423SLionel Sambuc } 637433d6423SLionel Sambuc } 638433d6423SLionel Sambuc 639