vfs_getcwd.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. /* $OpenBSD: vfs_getcwd.c,v 1.25 2015/03/14 03:38:51 jsg Exp $ */
  2. /* $NetBSD: vfs_getcwd.c,v 1.3.2.3 1999/07/11 10:24:09 sommerfeld Exp $ */
  3. /*
  4. * Copyright (c) 1999 The NetBSD Foundation, Inc.
  5. * All rights reserved.
  6. *
  7. * This code is derived from software contributed to The NetBSD Foundation
  8. * by Bill Sommerfeld.
  9. *
  10. * Redistribution and use in source and binary forms, with or without
  11. * modification, are permitted provided that the following conditions
  12. * are met:
  13. * 1. Redistributions of source code must retain the above copyright
  14. * notice, this list of conditions and the following disclaimer.
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in the
  17. * documentation and/or other materials provided with the distribution.
  18. *
  19. * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
  20. * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
  21. * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  22. * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
  23. * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  24. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  25. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  26. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  27. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  28. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  29. * POSSIBILITY OF SUCH DAMAGE.
  30. */
  31. #include <sys/param.h>
  32. #include <sys/systm.h>
  33. #include <sys/namei.h>
  34. #include <sys/filedesc.h>
  35. #include <sys/kernel.h>
  36. #include <sys/stat.h>
  37. #include <sys/lock.h>
  38. #include <sys/vnode.h>
  39. #include <sys/mount.h>
  40. #include <sys/proc.h>
  41. #include <sys/uio.h>
  42. #include <sys/malloc.h>
  43. #include <sys/dirent.h>
  44. #include <ufs/ufs/dir.h> /* only for DIRBLKSIZ */
  45. #include <sys/syscallargs.h>
  46. /* Find parent vnode of *lvpp, return in *uvpp */
  47. int
  48. vfs_getcwd_scandir(struct vnode **lvpp, struct vnode **uvpp, char **bpp,
  49. char *bufp, struct proc *p)
  50. {
  51. int eofflag, tries, dirbuflen, len, reclen, error = 0;
  52. off_t off;
  53. struct uio uio;
  54. struct iovec iov;
  55. char *dirbuf = NULL;
  56. ino_t fileno;
  57. struct vattr va;
  58. struct vnode *uvp = NULL;
  59. struct vnode *lvp = *lvpp;
  60. struct componentname cn;
  61. tries = 0;
  62. /*
  63. * If we want the filename, get some info we need while the
  64. * current directory is still locked.
  65. */
  66. if (bufp != NULL) {
  67. error = VOP_GETATTR(lvp, &va, p->p_ucred, p);
  68. if (error) {
  69. vput(lvp);
  70. *lvpp = NULL;
  71. *uvpp = NULL;
  72. return (error);
  73. }
  74. }
  75. cn.cn_nameiop = LOOKUP;
  76. cn.cn_flags = ISLASTCN | ISDOTDOT | RDONLY;
  77. cn.cn_proc = p;
  78. cn.cn_cred = p->p_ucred;
  79. cn.cn_pnbuf = NULL;
  80. cn.cn_nameptr = "..";
  81. cn.cn_namelen = 2;
  82. cn.cn_consume = 0;
  83. /* Get parent vnode using lookup of '..' */
  84. error = VOP_LOOKUP(lvp, uvpp, &cn);
  85. if (error) {
  86. vput(lvp);
  87. *lvpp = NULL;
  88. *uvpp = NULL;
  89. return (error);
  90. }
  91. uvp = *uvpp;
  92. /* If we don't care about the pathname, we're done */
  93. if (bufp == NULL) {
  94. vrele(lvp);
  95. *lvpp = NULL;
  96. return (0);
  97. }
  98. fileno = va.va_fileid;
  99. dirbuflen = DIRBLKSIZ;
  100. if (dirbuflen < va.va_blocksize)
  101. dirbuflen = va.va_blocksize;
  102. dirbuf = malloc(dirbuflen, M_TEMP, M_WAITOK);
  103. off = 0;
  104. do {
  105. char *cpos;
  106. struct dirent *dp;
  107. iov.iov_base = dirbuf;
  108. iov.iov_len = dirbuflen;
  109. uio.uio_iov = &iov;
  110. uio.uio_iovcnt = 1;
  111. uio.uio_offset = off;
  112. uio.uio_resid = dirbuflen;
  113. uio.uio_segflg = UIO_SYSSPACE;
  114. uio.uio_rw = UIO_READ;
  115. uio.uio_procp = p;
  116. eofflag = 0;
  117. /* Call VOP_READDIR of parent */
  118. error = VOP_READDIR(uvp, &uio, p->p_ucred, &eofflag);
  119. off = uio.uio_offset;
  120. /* Try again if NFS tosses its cookies */
  121. if (error == EINVAL && tries < 3) {
  122. tries++;
  123. off = 0;
  124. continue;
  125. } else if (error) {
  126. goto out; /* Old userland getcwd() behaviour */
  127. }
  128. cpos = dirbuf;
  129. tries = 0;
  130. /* Scan directory page looking for matching vnode */
  131. for (len = (dirbuflen - uio.uio_resid); len > 0;
  132. len -= reclen) {
  133. dp = (struct dirent *)cpos;
  134. reclen = dp->d_reclen;
  135. /* Check for malformed directory */
  136. if (reclen < DIRENT_RECSIZE(1)) {
  137. error = EINVAL;
  138. goto out;
  139. }
  140. if (dp->d_fileno == fileno) {
  141. char *bp = *bpp;
  142. bp -= dp->d_namlen;
  143. if (bp <= bufp) {
  144. error = ERANGE;
  145. goto out;
  146. }
  147. memmove(bp, dp->d_name, dp->d_namlen);
  148. error = 0;
  149. *bpp = bp;
  150. goto out;
  151. }
  152. cpos += reclen;
  153. }
  154. } while (!eofflag);
  155. error = ENOENT;
  156. out:
  157. vrele(lvp);
  158. *lvpp = NULL;
  159. free(dirbuf, M_TEMP, dirbuflen);
  160. return (error);
  161. }
  162. /* Do a lookup in the vnode-to-name reverse */
  163. int
  164. vfs_getcwd_getcache(struct vnode **lvpp, struct vnode **uvpp, char **bpp,
  165. char *bufp)
  166. {
  167. struct vnode *lvp, *uvp = NULL;
  168. struct proc *p = curproc;
  169. char *obp;
  170. int error, vpid;
  171. lvp = *lvpp;
  172. obp = *bpp; /* Save original position to restore to on error */
  173. error = cache_revlookup(lvp, uvpp, bpp, bufp);
  174. if (error) {
  175. if (error != -1) {
  176. vput(lvp);
  177. *lvpp = NULL;
  178. *uvpp = NULL;
  179. }
  180. return (error);
  181. }
  182. uvp = *uvpp;
  183. vpid = uvp->v_id;
  184. /* Release current lock before acquiring the parent lock */
  185. VOP_UNLOCK(lvp, 0, p);
  186. error = vget(uvp, LK_EXCLUSIVE | LK_RETRY, p);
  187. if (error)
  188. *uvpp = NULL;
  189. /*
  190. * Verify that vget() succeeded, and check that vnode capability
  191. * didn't change while we were waiting for the lock.
  192. */
  193. if (error || (vpid != uvp->v_id)) {
  194. /*
  195. * Try to get our lock back. If that works, tell the caller to
  196. * try things the hard way, otherwise give up.
  197. */
  198. if (!error)
  199. vput(uvp);
  200. *uvpp = NULL;
  201. error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY, p);
  202. if (!error) {
  203. *bpp = obp; /* restore the buffer */
  204. return (-1);
  205. }
  206. }
  207. vrele(lvp);
  208. *lvpp = NULL;
  209. return (error);
  210. }
  211. /* Common routine shared by sys___getcwd() and vn_isunder() */
  212. int
  213. vfs_getcwd_common(struct vnode *lvp, struct vnode *rvp, char **bpp, char *bufp,
  214. int limit, int flags, struct proc *p)
  215. {
  216. struct filedesc *fdp = p->p_fd;
  217. struct vnode *uvp = NULL;
  218. char *bp = NULL;
  219. int error, perms = VEXEC;
  220. if (rvp == NULL) {
  221. rvp = fdp->fd_rdir;
  222. if (rvp == NULL)
  223. rvp = rootvnode;
  224. }
  225. vref(rvp);
  226. vref(lvp);
  227. error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY, p);
  228. if (error) {
  229. vrele(lvp);
  230. lvp = NULL;
  231. goto out;
  232. }
  233. if (bufp)
  234. bp = *bpp;
  235. if (lvp == rvp) {
  236. if (bp)
  237. *(--bp) = '/';
  238. goto out;
  239. }
  240. /*
  241. * This loop will terminate when we hit the root, VOP_READDIR() or
  242. * VOP_LOOKUP() fails, or we run out of space in the user buffer.
  243. */
  244. do {
  245. if (lvp->v_type != VDIR) {
  246. error = ENOTDIR;
  247. goto out;
  248. }
  249. /* Check for access if caller cares */
  250. if (flags & GETCWD_CHECK_ACCESS) {
  251. error = VOP_ACCESS(lvp, perms, p->p_ucred, p);
  252. if (error)
  253. goto out;
  254. perms = VEXEC|VREAD;
  255. }
  256. /* Step up if we're a covered vnode */
  257. while (lvp->v_flag & VROOT) {
  258. struct vnode *tvp;
  259. if (lvp == rvp)
  260. goto out;
  261. tvp = lvp;
  262. lvp = lvp->v_mount->mnt_vnodecovered;
  263. vput(tvp);
  264. if (lvp == NULL) {
  265. error = ENOENT;
  266. goto out;
  267. }
  268. vref(lvp);
  269. error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY, p);
  270. if (error) {
  271. vrele(lvp);
  272. lvp = NULL;
  273. goto out;
  274. }
  275. }
  276. /* Look in the name cache */
  277. error = vfs_getcwd_getcache(&lvp, &uvp, &bp, bufp);
  278. if (error == -1) {
  279. /* If that fails, look in the directory */
  280. error = vfs_getcwd_scandir(&lvp, &uvp, &bp, bufp, p);
  281. }
  282. if (error)
  283. goto out;
  284. #ifdef DIAGNOSTIC
  285. if (lvp != NULL)
  286. panic("getcwd: oops, forgot to null lvp");
  287. if (bufp && (bp <= bufp)) {
  288. panic("getcwd: oops, went back too far");
  289. }
  290. #endif
  291. if (bp)
  292. *(--bp) = '/';
  293. lvp = uvp;
  294. uvp = NULL;
  295. limit--;
  296. } while ((lvp != rvp) && (limit > 0));
  297. out:
  298. if (bpp)
  299. *bpp = bp;
  300. if (uvp)
  301. vput(uvp);
  302. if (lvp)
  303. vput(lvp);
  304. vrele(rvp);
  305. return (error);
  306. }
  307. /* Find pathname of a process's current directory */
  308. int
  309. sys___getcwd(struct proc *p, void *v, register_t *retval)
  310. {
  311. struct sys___getcwd_args *uap = v;
  312. int error, lenused, len = SCARG(uap, len);
  313. char *path, *bp, *bend;
  314. if (len > MAXPATHLEN * 4)
  315. len = MAXPATHLEN * 4;
  316. else if (len < 2)
  317. return (ERANGE);
  318. path = malloc(len, M_TEMP, M_WAITOK);
  319. bp = &path[len];
  320. bend = bp;
  321. *(--bp) = '\0';
  322. /*
  323. * 5th argument here is "max number of vnodes to traverse".
  324. * Since each entry takes up at least 2 bytes in the output
  325. * buffer, limit it to N/2 vnodes for an N byte buffer.
  326. */
  327. error = vfs_getcwd_common(p->p_fd->fd_cdir, NULL, &bp, path, len/2,
  328. GETCWD_CHECK_ACCESS, p);
  329. if (error)
  330. goto out;
  331. lenused = bend - bp;
  332. *retval = lenused;
  333. /* Put the result into user buffer */
  334. error = copyout(bp, SCARG(uap, buf), lenused);
  335. out:
  336. free(path, M_TEMP, len);
  337. return (error);
  338. }