📄 ufs_lookup.c
字号:
* If rewriting (RENAME), return the inode and the * information required to rewrite the present directory * Must get inode of directory entry to verify it's a * regular file, or empty directory. */ if (nameiop == RENAME && wantparent && (flags & ISLASTCN)) { if (error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc)) return (error); /* * Careful about locking second inode. * This can only occur if the target is ".". */ if (dp->i_number == dp->i_ino) return (EISDIR); if (error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) return (error); *vpp = tdp; cnp->cn_flags |= SAVENAME; if (!lockparent) VOP_UNLOCK(vdp); return (0); } /* * Step through the translation in the name. We do not `vput' the * directory because we may need it again if a symbolic link * is relative to the current directory. Instead we save it * unlocked as "pdp". We must get the target inode before unlocking * the directory to insure that the inode will not be removed * before we get it. We prevent deadlock by always fetching * inodes from the root, moving down the directory tree. Thus * when following backward pointers ".." we must unlock the * parent directory before getting the requested directory. * There is a potential race condition here if both the current * and parent directories are removed before the VFS_VGET for the * inode associated with ".." returns. We hope that this occurs * infrequently since we cannot avoid this race condition without * implementing a sophisticated deadlock detection algorithm. * Note also that this simple deadlock detection scheme will not * work if the file system has any hard links other than ".." * that point backwards in the directory structure. */ pdp = vdp; if (flags & ISDOTDOT) { VOP_UNLOCK(pdp); /* race to get the inode */ if (error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) { VOP_LOCK(pdp); return (error); } if (lockparent && (flags & ISLASTCN) && (error = VOP_LOCK(pdp))) { vput(tdp); return (error); } *vpp = tdp; } else if (dp->i_number == dp->i_ino) { VREF(vdp); /* we want ourself, ie "." */ *vpp = vdp; } else { if (error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) return (error); if (!lockparent || !(flags & ISLASTCN)) VOP_UNLOCK(pdp); *vpp = tdp; } /* * Insert name into cache if appropriate. */ if (cnp->cn_flags & MAKEENTRY) cache_enter(vdp, *vpp, cnp); return (0);}voidufs_dirbad(ip, offset, how) struct inode *ip; doff_t offset; char *how;{ struct mount *mp; mp = ITOV(ip)->v_mount; (void)printf("%s: bad dir ino %d at offset %d: %s\n", mp->mnt_stat.f_mntonname, ip->i_number, offset, how); if ((mp->mnt_stat.f_flags & MNT_RDONLY) == 0) panic("bad dir");}/* * Do consistency checking on a directory entry: * record length must be multiple of 4 * entry must fit in rest of its DIRBLKSIZ block * record must be large enough to contain entry * name is not longer than MAXNAMLEN * name must be as long as advertised, and null terminated */intufs_dirbadentry(dp, ep, entryoffsetinblock) struct vnode *dp; register struct direct *ep; int entryoffsetinblock;{ register int i; int namlen;# if (BYTE_ORDER == LITTLE_ENDIAN) if (dp->v_mount->mnt_maxsymlinklen > 0) namlen = ep->d_namlen; else namlen = ep->d_type;# else namlen = ep->d_namlen;# endif if ((ep->d_reclen & 0x3) != 0 || ep->d_reclen > DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)) || ep->d_reclen < DIRSIZ(FSFMT(dp), ep) || namlen > MAXNAMLEN) { /*return (1); */ printf("First bad\n"); goto bad; } for (i = 0; i < namlen; i++) if (ep->d_name[i] == '\0') { /*return (1); */ printf("Second bad\n"); goto bad; } if (ep->d_name[i]) goto bad; return (ep->d_name[i]);bad: return(1);}/* * Write a directory entry after a call to namei, using the parameters * that it left in nameidata. The argument ip is the inode which the new * directory entry will refer to. Dvp is a pointer to the directory to * be written, which was left locked by namei. Remaining parameters * (dp->i_offset, dp->i_count) indicate how the space for the new * entry is to be obtained. */intufs_direnter(ip, dvp, cnp) struct inode *ip; struct vnode *dvp; register struct componentname *cnp;{ register struct direct *ep, *nep; register struct inode *dp; struct buf *bp; struct direct newdir; struct iovec aiov; struct uio auio; u_int dsize; int error, loc, newentrysize, spacefree; char *dirbuf;#ifdef DIAGNOSTIC if ((cnp->cn_flags & SAVENAME) == 0) panic("direnter: missing name");#endif dp = VTOI(dvp); newdir.d_ino = ip->i_number; newdir.d_namlen = cnp->cn_namelen; bcopy(cnp->cn_nameptr, newdir.d_name, (unsigned)cnp->cn_namelen + 1); if (dvp->v_mount->mnt_maxsymlinklen > 0) newdir.d_type = IFTODT(ip->i_mode); else { newdir.d_type = 0;# if (BYTE_ORDER == LITTLE_ENDIAN) { u_char tmp = newdir.d_namlen; newdir.d_namlen = newdir.d_type; newdir.d_type = tmp; }# endif } newentrysize = DIRSIZ(FSFMT(dvp), &newdir); if (dp->i_count == 0) { /* * If dp->i_count is 0, then namei could find no * space in the directory. Here, dp->i_offset will * be on a directory block boundary and we will write the * new entry into a fresh block. */ if (dp->i_offset & (DIRBLKSIZ - 1)) panic("ufs_direnter: newblk"); auio.uio_offset = dp->i_offset; newdir.d_reclen = DIRBLKSIZ; auio.uio_resid = newentrysize; aiov.iov_len = newentrysize; aiov.iov_base = (caddr_t)&newdir; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_rw = UIO_WRITE; auio.uio_segflg = UIO_SYSSPACE; auio.uio_procp = (struct proc *)0; error = VOP_WRITE(dvp, &auio, IO_SYNC, cnp->cn_cred); if (DIRBLKSIZ > VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_bsize) /* XXX should grow with balloc() */ panic("ufs_direnter: frag size"); else if (!error) { dp->i_size = roundup(dp->i_size, DIRBLKSIZ); dp->i_flag |= IN_CHANGE; } return (error); } /* * If dp->i_count is non-zero, then namei found space * for the new entry in the range dp->i_offset to * dp->i_offset + dp->i_count in the directory. * To use this space, we may have to compact the entries located * there, by copying them together towards the beginning of the * block, leaving the free space in one usable chunk at the end. */ /* * Increase size of directory if entry eats into new space. * This should never push the size past a new multiple of * DIRBLKSIZE. * * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN. */ if (dp->i_offset + dp->i_count > dp->i_size) dp->i_size = dp->i_offset + dp->i_count; /* * Get the block containing the space for the new directory entry. */ if (error = VOP_BLKATOFF(dvp, (off_t)dp->i_offset, &dirbuf, &bp)) return (error); /* * Find space for the new entry. In the simple case, the entry at * offset base will have the space. If it does not, then namei * arranged that compacting the region dp->i_offset to * dp->i_offset + dp->i_count would yield the * space. */ ep = (struct direct *)dirbuf; dsize = DIRSIZ(FSFMT(dvp), ep); spacefree = ep->d_reclen - dsize; for (loc = ep->d_reclen; loc < dp->i_count; ) { nep = (struct direct *)(dirbuf + loc); if (ep->d_ino) { /* trim the existing slot */ ep->d_reclen = dsize; ep = (struct direct *)((char *)ep + dsize); } else { /* overwrite; nothing there; header is ours */ spacefree += dsize; } dsize = DIRSIZ(FSFMT(dvp), nep); spacefree += nep->d_reclen - dsize; loc += nep->d_reclen; bcopy((caddr_t)nep, (caddr_t)ep, dsize); } /* * Update the pointer fields in the previous entry (if any), * copy in the new entry, and write out the block. */ if (ep->d_ino == 0) { if (spacefree + dsize < newentrysize) panic("ufs_direnter: compact1"); newdir.d_reclen = spacefree + dsize; } else { if (spacefree < newentrysize) panic("ufs_direnter: compact2"); newdir.d_reclen = spacefree; ep->d_reclen = dsize; ep = (struct direct *)((char *)ep + dsize); } bcopy((caddr_t)&newdir, (caddr_t)ep, (u_int)newentrysize); error = VOP_BWRITE(bp); dp->i_flag |= IN_CHANGE | IN_UPDATE; if (!error && dp->i_endoff && dp->i_endoff < dp->i_size) error = VOP_TRUNCATE(dvp, (off_t)dp->i_endoff, IO_SYNC, cnp->cn_cred, cnp->cn_proc); return (error);}/* * Remove a directory entry after a call to namei, using * the parameters which it left in nameidata. The entry * dp->i_offset contains the offset into the directory of the * entry to be eliminated. The dp->i_count field contains the * size of the previous record in the directory. If this * is 0, the first entry is being deleted, so we need only * zero the inode number to mark the entry as free. If the * entry is not the first in the directory, we must reclaim * the space of the now empty record by adding the record size * to the size of the previous entry. */intufs_dirremove(dvp, cnp) struct vnode *dvp; struct componentname *cnp;{ register struct inode *dp; struct direct *ep; struct buf *bp; int error; dp = VTOI(dvp); if (dp->i_count == 0) { /* * First entry in block: set d_ino to zero. */ if (error = VOP_BLKATOFF(dvp, (off_t)dp->i_offset, (char **)&ep, &bp)) return (error); ep->d_ino = 0; error = VOP_BWRITE(bp); dp->i_flag |= IN_CHANGE | IN_UPDATE; return (error); } /* * Collapse new free space into previous entry. */ if (error = VOP_BLKATOFF(dvp, (off_t)(dp->i_offset - dp->i_count), (char **)&ep, &bp)) return (error); ep->d_reclen += dp->i_reclen; error = VOP_BWRITE(bp); dp->i_flag |= IN_CHANGE | IN_UPDATE; return (error);}/* * Rewrite an existing directory entry to point at the inode * supplied. The parameters describing the directory entry are * set up by a call to namei. */intufs_dirrewrite(dp, ip, cnp) struct inode *dp, *ip; struct componentname *cnp;{ struct buf *bp; struct direct *ep; struct vnode *vdp = ITOV(dp); int error; if (error = VOP_BLKATOFF(vdp, (off_t)dp->i_offset, (char **)&ep, &bp)) return (error); ep->d_ino = ip->i_number; if (vdp->v_mount->mnt_maxsymlinklen > 0) ep->d_type = IFTODT(ip->i_mode); error = VOP_BWRITE(bp); dp->i_flag |= IN_CHANGE | IN_UPDATE; return (error);}/* * Check if a directory is empty or not. * Inode supplied must be locked. * * Using a struct dirtemplate here is not precisely * what we want, but better than using a struct direct. * * NB: does not handle corrupted directories. */intufs_dirempty(ip, parentino, cred) register struct inode *ip; ino_t parentino; struct ucred *cred;{ register off_t off; struct dirtemplate dbuf; register struct direct *dp = (struct direct *)&dbuf; int error, count, namlen;#define MINDIRSIZ (sizeof (struct dirtemplate) / 2) for (off = 0; off < ip->i_size; off += dp->d_reclen) { error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ, off, UIO_SYSSPACE, IO_NODELOCKED, cred, &count, (struct proc *)0); /* * Since we read MINDIRSIZ, residual must * be 0 unless we're at end of file. */ if (error || count != 0) return (0); /* avoid infinite loops */ if (dp->d_reclen == 0) return (0); /* skip empty entries */ if (dp->d_ino == 0) continue; /* accept only "." and ".." */# if (BYTE_ORDER == LITTLE_ENDIAN) if (ITOV(ip)->v_mount->mnt_maxsymlinklen > 0) namlen = dp->d_namlen; else namlen = dp->d_type;# else namlen = dp->d_namlen;# endif if (namlen > 2) return (0); if (dp->d_name[0] != '.') return (0); /* * At this point namlen must be 1 or 2. * 1 implies ".", 2 implies ".." if second * char is also "." */ if (namlen == 1) continue; if (dp->d_name[1] == '.' && dp->d_ino == parentino) continue; return (0); } return (1);}/* * Check if source directory is in the path of the target directory. * Target is supplied locked, source is unlocked. * The target is always vput before returning. */intufs_checkpath(source, target, cred) struct inode *source, *target; struct ucred *cred;{ struct vnode *vp; int error, rootino, namlen; struct dirtemplate dirbuf; vp = ITOV(target); if (target->i_number == source->i_number) { error = EEXIST; goto out; } rootino = ROOTINO; error = 0; if (target->i_number == rootino) goto out; for (;;) { if (vp->v_type != VDIR) { error = ENOTDIR; break; } error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf, sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE, IO_NODELOCKED, cred, (int *)0, (struct proc *)0); if (error != 0) break;# if (BYTE_ORDER == LITTLE_ENDIAN) if (vp->v_mount->mnt_maxsymlinklen > 0) namlen = dirbuf.dotdot_namlen; else namlen = dirbuf.dotdot_type;# else namlen = dirbuf.dotdot_namlen;# endif if (namlen != 2 || dirbuf.dotdot_name[0] != '.' || dirbuf.dotdot_name[1] != '.') { error = ENOTDIR; break; } if (dirbuf.dotdot_ino == source->i_number) { error = EINVAL; break; } if (dirbuf.dotdot_ino == rootino) break; vput(vp); if (error = VFS_VGET(vp->v_mount, dirbuf.dotdot_ino, &vp)) { vp = NULL; break; } }out: if (error == ENOTDIR) printf("checkpath: .. not a directory\n"); if (vp != NULL) vput(vp); return (error);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -