| 1852 | } |
| 1853 | |
| 1854 | int |
| 1855 | kern_funlinkat(struct thread *td, int dfd, const char *path, int fd, |
| 1856 | enum uio_seg pathseg, int flag, ino_t oldinum) |
| 1857 | { |
| 1858 | struct mount *mp; |
| 1859 | struct file *fp; |
| 1860 | struct vnode *vp; |
| 1861 | struct nameidata nd; |
| 1862 | struct stat sb; |
| 1863 | int error; |
| 1864 | |
| 1865 | fp = NULL; |
| 1866 | if (fd != FD_NONE) { |
| 1867 | error = getvnode(td, fd, &cap_no_rights, &fp); |
| 1868 | if (error != 0) |
| 1869 | return (error); |
| 1870 | } |
| 1871 | |
| 1872 | restart: |
| 1873 | bwillwrite(); |
| 1874 | NDINIT_ATRIGHTS(&nd, DELETE, LOCKPARENT | LOCKLEAF | AUDITVNODE1 | |
| 1875 | at2cnpflags(flag, AT_RESOLVE_BENEATH), |
| 1876 | pathseg, path, dfd, &cap_unlinkat_rights, td); |
| 1877 | if ((error = namei(&nd)) != 0) { |
| 1878 | if (error == EINVAL) |
| 1879 | error = EPERM; |
| 1880 | goto fdout; |
| 1881 | } |
| 1882 | vp = nd.ni_vp; |
| 1883 | if (vp->v_type == VDIR && oldinum == 0) { |
| 1884 | error = EPERM; /* POSIX */ |
| 1885 | } else if (oldinum != 0 && |
| 1886 | ((error = VOP_STAT(vp, &sb, td->td_ucred, NOCRED, td)) == 0) && |
| 1887 | sb.st_ino != oldinum) { |
| 1888 | error = EIDRM; /* Identifier removed */ |
| 1889 | } else if (fp != NULL && fp->f_vnode != vp) { |
| 1890 | if (VN_IS_DOOMED(fp->f_vnode)) |
| 1891 | error = EBADF; |
| 1892 | else |
| 1893 | error = EDEADLK; |
| 1894 | } else { |
| 1895 | /* |
| 1896 | * The root of a mounted filesystem cannot be deleted. |
| 1897 | * |
| 1898 | * XXX: can this only be a VDIR case? |
| 1899 | */ |
| 1900 | if (vp->v_vflag & VV_ROOT) |
| 1901 | error = EBUSY; |
| 1902 | } |
| 1903 | if (error == 0) { |
| 1904 | if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { |
| 1905 | NDFREE(&nd, NDF_ONLY_PNBUF); |
| 1906 | vput(nd.ni_dvp); |
| 1907 | if (vp == nd.ni_dvp) |
| 1908 | vrele(vp); |
| 1909 | else |
| 1910 | vput(vp); |
| 1911 | if ((error = vn_start_write(NULL, &mp, |
no test coverage detected