📄 ufs_namei.c
字号:
*/ /* remove from LRU chain */ *ncp->nc_prev = ncp->nc_nxt; if (ncp->nc_nxt) ncp->nc_nxt->nc_prev = ncp->nc_prev; else nchtail = ncp->nc_prev; /* remove from hash chain */ remque(ncp); /* insert at head of LRU list (first to grab) */ ncp->nc_nxt = nchhead; ncp->nc_prev = &nchhead; nchhead->nc_prev = &ncp->nc_nxt; nchhead = ncp; /* and make a dummy hash chain */ ncp->nc_forw = ncp; ncp->nc_back = ncp; ncp->nc_id = 0;}dirbad(gp, offset, how) struct gnode *gp; off_t offset; char *how;{ printf("%s: bad dir ino %d at offset %d: %s\n", FS(gp)->fs_fsmnt, gp->g_number, offset, how);}/* * Do consistency checking on a directory entry: * record length must be multiple of 4 * record length must not be non-negative * 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 */dirbadentry(ep, entryoffsetinblock) register struct direct *ep; int entryoffsetinblock;{ register int i; if ((ep->d_reclen & 0x3) != 0 || ep->d_reclen <= 0 || ep->d_reclen > DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)) || ep->d_reclen < DIRSIZ(ep) || ep->d_namlen > MAXNAMLEN) return (1); for (i = 0; i < ep->d_namlen; i++) if (ep->d_name[i] == '\0') return (1); return (ep->d_name[i]);}/* * Write a directory entry after a call to namei, using the parameters * which it left in the u. area. The argument gp is the inode which * the new directory entry will refer to. The u. area field ndp->ni_pdir is * a pointer to the directory to be written, which was left locked by * namei. Remaining parameters (ndp->ni_offset, ndp->ni_count) indicate * how the space for the new entry is to be gotten. */direnter(gp, ndp) struct gnode *gp; register struct nameidata *ndp;{ register struct direct *ep, *nep; register struct gnode *dp = (struct gnode *)ndp->ni_pdir; struct buf *bp; int loc, spacefree, error = 0; u_int dsize; int newentrysize; char *dirbuf; char *tcp; gassert(dp); /* * If POSIX, make sure non directory entries being * added do not have trailing slashes. */ if (u.u_procp->p_progenv == A_POSIX) { if ((gp->g_mode & GFMT) != GFDIR) { tcp = ndp->ni_dirp; while (*tcp) tcp++; tcp--; if (*tcp == '/') { gput(dp); u.u_error = ENOTDIR; return (u.u_error); } } } ndp->ni_dent.d_ino = gp->g_number; newentrysize = DIRSIZ(&ndp->ni_dent); if (ndp->ni_count == 0) { /* * If ndp->ni_count is 0, then namei could find no space in the * directory. In this case ndp->ni_offset will be on a directory * block boundary and we will write the new entry into a fresh * block. */ if (ndp->ni_offset&(DIRBLKSIZ-1)) panic("wdir: newblk"); ndp->ni_dent.d_reclen = DIRBLKSIZ; error = rdwri(UIO_WRITE, dp, (caddr_t)&ndp->ni_dent, newentrysize, ndp->ni_offset, 1, (int *)0); if (DIRBLKSIZ > FS(dp)->fs_fsize) panic("wdir: blksize"); /* XXX - should grow w/bmap() */ else dp->g_size = roundup(dp->g_size, DIRBLKSIZ); gput(dp); return (error); } /* * If ndp->ni_count is non-zero, then namei found space for the new * entry in the range ndp->ni_offset to ndp->ni_offset + ndp->ni_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 (ndp->ni_offset + ndp->ni_count > dp->g_size) dp->g_size = ndp->ni_offset + ndp->ni_count; /* * Get the block containing the space for the new directory * entry. Should return error by result instead of u.u_error. */ bp = blkatoff(dp, ndp->ni_offset, (char **)&dirbuf); if (bp == 0) { gput(dp); return (u.u_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 * ndp->ni_offset to ndp->ni_offset+ndp->ni_count would yield the space. */ ep = (struct direct *)dirbuf; dsize = DIRSIZ(ep); spacefree = ep->d_reclen - dsize; for (loc = ep->d_reclen; loc < ndp->ni_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(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("wdir: compact1"); ndp->ni_dent.d_reclen = spacefree + dsize; } else { if (spacefree < newentrysize) panic("wdir: compact2"); ndp->ni_dent.d_reclen = spacefree; ep->d_reclen = dsize; ep = (struct direct *)((char *)ep + dsize); } bcopy((caddr_t)&ndp->ni_dent, (caddr_t)ep, (u_int)newentrysize); bwrite(bp); dp->g_flag |= GUPD|GCHG; if (ndp->ni_endoff && ndp->ni_endoff < dp->g_size) ufs_gtrunc(dp, (u_long) ndp->ni_endoff, (struct ucred *) 0); gput(dp); return (error);}/* * Remove a directory entry after a call to namei, using the * parameters which it left in the u. area. The u. entry * ni_offset contains the offset into the directory of the * entry to be eliminated. The ni_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 isn't 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. */dirremove(ndp) register struct nameidata *ndp;{ register struct gnode *dp = (struct gnode *)ndp->ni_pdir; register struct buf *bp; struct direct *ep; gassert(dp); if (ndp->ni_count == 0) { /* * First entry in block: set d_ino to zero. */ ndp->ni_dent.d_ino = 0; (void) rdwri(UIO_WRITE, dp, (caddr_t)&ndp->ni_dent, (int)DIRSIZ(&ndp->ni_dent), ndp->ni_offset, 1, (int *)0); } else { /* * Collapse new free space into previous entry. */ bp = blkatoff(dp, (int)(ndp->ni_offset - ndp->ni_count), (char **)&ep); if (bp == 0) return (0); ep->d_reclen += ndp->ni_dent.d_reclen; bwrite(bp); dp->g_flag |= GUPD|GCHG; } return (1);}/* * 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. */dirrewrite(dp, gp, ndp) struct gnode *dp, *gp; struct nameidata *ndp;{ char *tcp; /* * If POSIX, make sure non directory entries being * added do not have trailing slashes. */ if (u.u_procp->p_progenv == A_POSIX) { if ((gp->g_mode & GFMT) != GFDIR) { tcp = ndp->ni_dirp; while (*tcp) tcp++; tcp--; if (*tcp == '/') { gput(dp); u.u_error = ENOTDIR; return (u.u_error); } } } ndp->ni_dent.d_ino = gp->g_number; u.u_error = rdwri(UIO_WRITE, dp, (caddr_t)&ndp->ni_dent, (int)DIRSIZ(&ndp->ni_dent), ndp->ni_offset, 1, (int *)0); gput(dp);}/* * Return buffer with contents of block "offset" * from the beginning of directory "gp". If "res" * is non-zero, fill it in with a pointer to the * remaining space in the directory. */struct buf *blkatoff(gp, offset, res) register struct gnode *gp; register off_t offset; char **res;{ register struct fs *fs = FS(gp); register daddr_t lbn = lblkno(fs, offset); register struct buf *bp; register daddr_t bn; int bsize = blksize(fs, gp, lbn); bn = ufs_bmap(gp, (int)lbn, B_READ, bsize, 0, 0); if (u.u_error) return (0); if (bn == (daddr_t)(-1)) { dirbad(gp, offset, "hole in dir"); return (0); } bp = bread(gp->g_dev, bn, bsize, (struct gnode *) NULL); if (bp->b_flags & B_ERROR) { brelse(bp); return (0); } if (res) *res = bp->b_un.b_addr + blkoff(fs, offset); return (bp);}/* * 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. */dirempty(gp, parentino) register struct gnode *gp; gno_t parentino;{ register off_t off; struct dirtemplate dbuf; register struct direct *dp = (struct direct *)&dbuf; int error, count;#define MINDIRSIZ (sizeof (struct dirtemplate) / 2) for (off = 0; off < gp->g_size; off += dp->d_reclen) { error = rdwri(UIO_READ, gp, (caddr_t)dp, MINDIRSIZ, off, 1, &count); /* * Since we read MINDIRSIZ, residual must * be 0 unless we're at end of file. */ if (error || count != 0) return (0); if (dp->d_reclen <= 0) return (0); /* skip empty entries */ if (dp->d_ino == 0) continue; /* accept only "." and ".." */ if (dp->d_namlen > 2) return (0); if (dp->d_name[0] != '.') return (0); /* * At this point d_namlen must be 1 or 2. * 1 implies ".", 2 implies ".." if second * char is also "." */ if (dp->d_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 gput() before returning. *//* this routine is ok for it's purposes. it should eventually go away * since it is only used by rename */checkpath(source, target, flag) struct gnode *source, *target; int flag;{ struct dirtemplate dirbuf; register struct gnode *gp; int error = 0; gp = target; gassert(gp); if (gp->g_number == source->g_number) { /* * This condition used to return EEXIST. However, * ufs_rename is the only invoker, and it called * with target being a parent directory of the * target_gp. This condition matches the EINVAL * return below during the first iteration of the * for loop. So we will return EINVAL. */ error = EINVAL; goto out; } if (gp->g_number == ROOTINO) goto out; for (;;) { if ((gp->g_mode&GFMT) != GFDIR) { error = ENOTDIR; break; } error = rdwri(UIO_READ, gp, (caddr_t)&dirbuf, sizeof (struct dirtemplate), (off_t)0, 1, (int *)0); if (error != 0) break; if (dirbuf.dotdot_namlen != 2 || dirbuf.dotdot_name[0] != '.' || dirbuf.dotdot_name[1] != '.') { error = ENOTDIR; break; } if (dirbuf.dotdot_ino == source->g_number) { error = EINVAL; break; } if (dirbuf.dotdot_ino == ROOTINO) break; gput(gp); gp = (struct gnode *)gget(gp->g_mp, dirbuf.dotdot_ino, flag, NULL); if (gp == NULL) { error = u.u_error; break; } }out: if (error == ENOTDIR) printf("checkpath: .. not a directory\n"); if (gp != NULL) gput(gp); return (error);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -