📄 ufs_namei.c
字号:
dp->g_flag |= GINCOMPLETE; return(dp); } /* * If directory is "sticky", then user must own * the directory, or the file in it, else he * may not delete it (unless he's root). This * implements append-only directories. */ if ((ndp->ni_pdir->g_mode & GSVTX) && u.u_uid != 0 && u.u_uid != ndp->ni_pdir->g_uid && dp->g_uid != u.u_uid) { gput(ndp->ni_pdir); u.u_error = EPERM; goto bad; } /* * POSIX check. Only directories can have * trailing slashes. */ if (((dp->g_mode & GFMT) != GFDIR) && trailing_slashes) { gput(ndp->ni_pdir); u.u_error = ENOTDIR; goto bad; } } } /* * Check cache one more time */ smp_lock(&lk_namecache, LK_RETRY);del_again: nhp = &nchash[NHASH(hash, (ndp->ni_pdir)->g_number, (ndp->ni_pdir)->g_dev)]; for (ncp = nhp->nch_forw; ncp != (struct nch *)nhp; ncp = ncp->nc_forw) { if (ncp->nc_ino == (ndp->ni_pdir)->g_number && ncp->nc_dev == (ndp->ni_pdir)->g_dev && ncp->nc_nlen == ndp->ni_dent.d_namlen && !bcmp(ncp->nc_name, ndp->ni_dent.d_name, ncp->nc_nlen)) { expunge_cache(ncp); goto del_again; } } smp_unlock(&lk_namecache); dp->g_flag &= ~GINCOMPLETE; ndp->ni_cp = cp; return (dp); } /* * Special handling for ".." allowing chdir out of mounted * file system: indirect .. in root inode to reevaluate * in directory file system was mounted on. */ if (isdotdot) { if (dp == u.u_rdir) { ndp->ni_dent.d_ino = dp->g_number; makeentry = 0; } else if (ndp->ni_dent.d_ino == ROOTINO && dp->g_number == ROOTINO) { if(dp->g_dev == rootdev) { while(*cp == '/') cp++; ndp->ni_pdir = dp = rootdir; ndp->ni_cp = cp; dp->g_flag &= ~GINCOMPLETE; if(*cp) goto dirloop2; return(dp); } mp = dp->g_mp; gput(dp); dp = mp->m_gnodp; gref(dp); cp -= 2; /* back over .. */ gfs_lock(dp); if(dp->g_mp->m_fstype != GT_ULTRIX) { ndp->ni_pdir = dp; ndp->ni_cp = cp; dp->g_flag |= GINCOMPLETE; return(dp); } goto dirloop2; } } /* * 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 ((flag == CREATE && lockparent) && *cp == 0) { if (access(dp, GWRITE)) goto bad; ndp->ni_pdir = dp; /* for dirrewrite() */ /* * Careful about locking second inode. * This can only occur if the target is ".". */ if (dp->g_number == ndp->ni_dent.d_ino) { u.u_error = EISDIR; /* XXX */ goto bad; } dp = gget(dp->g_mp, ndp->ni_dent.d_ino, nomount, NULL); if (dp == NULL) { gput(ndp->ni_pdir); goto bad; } if(dp->g_mp->m_fstype != GT_ULTRIX) { ndp->ni_cp = cp; gput(ndp->ni_pdir); ndp->ni_pdir = dp; dp->g_flag |= GINCOMPLETE; return(dp); } if (((dp->g_mode & GFMT) != GFDIR) && trailing_slashes) { gput(ndp->ni_pdir); u.u_error = ENOTDIR; goto bad; } dp->g_flag &= ~GINCOMPLETE; ndp->ni_cp = cp; return (dp); } /* * Put a partial entry into the hash chain. We do this * while we have the parent directory locked, in case * there is a DELETE waiting on the gnode. The DELETE process * could be blocked in two places. The first place is where * the cache is being searched. In this case the partial entry will * be expunged, and we will know not to complete the entry * further down. The second place where a DELETE could be blocked * is during a gget() of dp, before it rewrites the dir. Since * a DELETE will recheck the cache before it exits, this * partial entry will be expunged, and again we will know * not to complete it further down. */ if (makeentry) { if (ncp != NULL) panic("ufs_namei: duplicating cache"); smp_lock(&lk_namecache, LK_RETRY); /* * Search cache and verify entry will not be a * duplicate. */ nhp = &nchash[NHASH(hash, dp->g_number, dp->g_dev)]; for (ncp = nhp->nch_forw; ncp != (struct nch *)nhp; ncp = ncp->nc_forw) { if (ncp->nc_ino == dp->g_number && ncp->nc_dev == dp->g_dev && ncp->nc_nlen == ndp->ni_dent.d_namlen && !bcmp(ncp->nc_name, ndp->ni_dent.d_name, ncp->nc_nlen)) { smp_unlock(&lk_namecache); ncp = NULL; makeentry = 0; goto check_slink; } } /* * free the cache slot at head of lru chain */ if (ncp = nchhead) { /* * 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 old hash chain */ remque(ncp); /* * Null out fields that will be filled * in later (maybe). */ ncp->nc_idev = NULL; ncp->nc_ip = NULL; ncp->nc_id = NULL; /* * fill in what we know */ ncp->nc_ino = dp->g_number; /* parents inum */ ncp->nc_dev = dp->g_dev; /* & device */ ncp->nc_nlen = ndp->ni_dent.d_namlen; bcopy(ndp->ni_dent.d_name, ncp->nc_name, ncp->nc_nlen); /* * link at end of lru chain */ ncp->nc_nxt = NULL; ncp->nc_prev = nchtail; *nchtail = ncp; nchtail = &ncp->nc_nxt; /* * and insert on hash chain */ insque(ncp, nhp); } smp_unlock(&lk_namecache); }check_slink: /* * About to check for symbolic link, which may require us to * massage the * name before we continue translation. We do not `gput' the * directory because we may need it again if the 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 `iget' 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 = dp; if (isdotdot) { ufs_gunlock(pdp); /* race to get the inode */ dp = gget(dp->g_mp, ndp->ni_dent.d_ino, nomount, NULL); if (dp == NULL) goto bad2; if(dp->g_mp->m_fstype != GT_ULTRIX) { grele(ndp->ni_pdir); ndp->ni_cp = cp; ndp->ni_pdir = dp; dp->g_flag |= GINCOMPLETE; return(dp); } } else if (dp->g_number == ndp->ni_dent.d_ino) { gref(dp); /* we want ourself, ie "." */ } else { dp = gget(dp->g_mp, ndp->ni_dent.d_ino, nomount, NULL); ufs_gunlock(pdp); if (dp == NULL) goto bad2; if(dp->g_mp->m_fstype != GT_ULTRIX) { (void)grele(pdp); ndp->ni_cp = cp; ndp->ni_pdir = dp; dp->g_flag |= GINCOMPLETE; return(dp); } } /* * insert name into cache if appropriate */ if (makeentry) { smp_lock(&lk_namecache, LK_RETRY); /* * Check if partial cache entry is still on hash * chain. If it isn't, a DELETE has or is occuring, * and we shouldn't add it. If partial entry is still * on chain, verify that there are no duplicates. */ nhp = &nchash[NHASH(hash, pdp->g_number, pdp->g_dev)]; for (ncp = nhp->nch_forw; ncp != (struct nch *)nhp; ncp = ncp->nc_forw) { if (ncp->nc_ino == pdp->g_number && ncp->nc_dev == pdp->g_dev && ncp->nc_nlen == ndp->ni_dent.d_namlen && !bcmp(ncp->nc_name, ndp->ni_dent.d_name, ncp->nc_nlen)) break; } if (ncp == (struct nch *)nhp) { /* * A DELETE process expunged our entry * from cache. Continue without completing * the entry. */ smp_unlock(&lk_namecache); ncp = NULL; goto haveino; } /* * If dp is a mount point, we are an NFS server; dont put * underlying info into cache. */ if (dp->g_flag & GMOUNT) { expunge_cache(ncp); smp_unlock(&lk_namecache); makeentry = 0; ncp = NULL; goto haveino; } /* * Fill in the rest of the entry */ ncp->nc_ino = pdp->g_number; /* parents inum */ ncp->nc_dev = pdp->g_dev; /* & device */ ncp->nc_idev = dp->g_dev; /* our device */ ncp->nc_ip = dp; ncp->nc_id = dp->g_id; /* identifier */ ncp->nc_nlen = ndp->ni_dent.d_namlen; bcopy(ndp->ni_dent.d_name, ncp->nc_name, ncp->nc_nlen); smp_unlock(&lk_namecache); }haveino: mp = dp->g_mp; /* * Check for symbolic link */ if ((dp->g_mode & GFMT) == GFLNK && ((ndp->ni_nameiop & FOLLOW) || *cp == '/')) { u_int pathlen = strlen(cp) + 1;#define OSF_FASTLINK 0x0001 if (dp->g_size + pathlen >= MAXPATHLEN - 1) { u.u_error = ENAMETOOLONG; goto bad2; } if (++ndp->ni_slcnt > MAXSYMLINKS) { u.u_error = ELOOP; goto bad2; } ovbcopy(cp, ndp->ni_dirp + dp->g_size, pathlen); if (G_TO_I(dp)->di_flags & OSF_FASTLINK) u.u_error = ovbcopy(G_TO_I(dp)->di_db, ndp->ni_dirp, dp->g_size); else u.u_error = rdwri(UIO_READ, dp, ndp->ni_dirp, (int)dp->g_size, 0, 1, (int *)0); if (u.u_error) goto bad2; cp = ndp->ni_dirp; gput(dp); if (*cp == '/') { grele(pdp); while (*cp == '/') cp++; if ((dp = u.u_rdir) == NULL) dp = rootdir; if(dp->g_mp->m_fstype != GT_ULTRIX) { gref(dp); GLOCK(dp); ndp->ni_pdir = dp; ndp->ni_cp = cp; dp->g_flag |= GINCOMPLETE; return(dp); } gref(dp); ufs_glock(dp); } else { dp = pdp; ufs_glock(dp); } mp = dp->g_mp; goto dirloop; } /* * Not a symbolic link. If more pathname, * continue at next component, else return. */ /* * for nomount, if we are at a mount point and there is more * pathname to translate, we need to terminate the name translation, * set u.u_error and return */ if(*cp && nomount && (dp->g_flag & GMOUNT)) { u.u_error = ENOENT; goto bad2; } if (*cp == '/') { while (*cp == '/') cp++; grele(pdp); goto dirloop; } if (lockparent) ndp->ni_pdir = pdp; else grele(pdp); dp->g_flag &= ~GINCOMPLETE; ndp->ni_cp = cp; return (dp);bad2: grele(pdp);bad: if (bp) brelse(bp); if (dp) { dp->g_flag &= ~GINCOMPLETE; gput(dp); } ndp->ni_cp = cp; return (NULL);}expunge_cache(ncp) struct nch *ncp;{ nchstats.ncs_expunged++; /* * Last component and we are renaming or deleting, * the cache entry is invalid, or otherwise don't * want cache entry to exist. NOTE: the entry could * have been reused while we were doing other stuff, * but this is still safe since every namecache struct * is always linked into both chains. The worst that * can happen is that we disturb the normal LRU * pattern, which isn't worth the trouble to avoid.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -