#include <sys/types.h>
#include <sys/t_lock.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/systm.h>
#include <sys/sysmacros.h>
#include <sys/resource.h>
#include <sys/signal.h>
#include <sys/cred.h>
#include <sys/user.h>
#include <sys/buf.h>
#include <sys/vfs.h>
#include <sys/stat.h>
#include <sys/vnode.h>
#include <sys/mode.h>
#include <sys/proc.h>
#include <sys/disp.h>
#include <sys/file.h>
#include <sys/fcntl.h>
#include <sys/flock.h>
#include <sys/kmem.h>
#include <sys/uio.h>
#include <sys/dnlc.h>
#include <sys/conf.h>
#include <sys/errno.h>
#include <sys/mman.h>
#include <sys/fbuf.h>
#include <sys/pathname.h>
#include <sys/debug.h>
#include <sys/vmsystm.h>
#include <sys/cmn_err.h>
#include <sys/dirent.h>
#include <sys/errno.h>
#include <sys/modctl.h>
#include <sys/statvfs.h>
#include <sys/mount.h>
#include <sys/sunddi.h>
#include <sys/bootconf.h>
#include <sys/policy.h>
#include <vm/hat.h>
#include <vm/page.h>
#include <vm/pvn.h>
#include <vm/as.h>
#include <vm/seg.h>
#include <vm/seg_map.h>
#include <vm/seg_kmem.h>
#include <vm/seg_vn.h>
#include <vm/rm.h>
#include <vm/page.h>
#include <sys/swap.h>
#include <fs/fs_subr.h>
#include <sys/fs/udf_volume.h>
#include <sys/fs/udf_inode.h>
struct slot {
enum {NONE, COMPACT, FOUND, EXIST} status;
off_t offset;
int size;
struct fbuf *fbp;
struct file_id *ep;
off_t endoff;
};
int32_t ud_dircheckforname(struct ud_inode *, char *, int,
struct slot *, struct ud_inode **, uint8_t *, struct cred *);
int32_t ud_dirempty(struct ud_inode *, uint64_t, struct cred *);
int32_t str2cmp(char *, int32_t, char *, int32_t, char *, int32_t);
int32_t ud_dircheckpath(int32_t, struct ud_inode *, struct cred *);
int32_t ud_dirmakeinode(struct ud_inode *, struct ud_inode **,
struct vattr *, enum de_op, struct cred *);
int32_t ud_diraddentry(struct ud_inode *, char *,
enum de_op, int, struct slot *, struct ud_inode *,
struct ud_inode *, struct cred *);
int32_t ud_dirmakedirect(struct ud_inode *, struct ud_inode *, struct cred *);
int32_t ud_dirrename(struct ud_inode *, struct ud_inode *,
struct ud_inode *, struct ud_inode *, char *, uint8_t *,
struct slot *, struct cred *);
int32_t ud_dirprepareentry(struct ud_inode *,
struct slot *, uint8_t *, struct cred *);
int32_t ud_dirfixdotdot(struct ud_inode *, struct ud_inode *,
struct ud_inode *);
int32_t ud_write_fid(struct ud_inode *, struct slot *, uint8_t *);
int
ud_dirlook(struct ud_inode *dip,
char *namep, struct ud_inode **ipp, struct cred *cr, int32_t skipdnlc)
{
struct udf_vfs *udf_vfsp;
int32_t error = 0, namelen, adhoc_search;
u_offset_t offset, adhoc_offset, dirsize, end;
struct vnode *dvp, *vp;
struct fbuf *fbp;
struct file_id *fid;
uint8_t *fname, dummy[3];
int32_t id_len, doingchk;
uint32_t old_loc;
uint16_t old_prn;
uint8_t *dname;
uint8_t *buf = NULL;
ud_printf("ud_dirlook\n");
udf_vfsp = dip->i_udf;
restart:
doingchk = 0;
old_prn = 0xFFFF;
old_loc = 0;
dvp = ITOV(dip);
if (dip->i_type != VDIR) {
return (ENOTDIR);
}
if (error = ud_iaccess(dip, IEXEC, cr, 1)) {
return (error);
}
if (*namep == '\0') {
VN_HOLD(dvp);
*ipp = dip;
return (0);
}
namelen = strlen(namep);
if ((namelen == 1) &&
(namep[0] == '.') && (namep[1] == '\0')) {
VN_HOLD(dvp);
*ipp = dip;
dnlc_enter(dvp, namep, ITOV(*ipp));
return (0);
}
if ((!skipdnlc) && (vp = dnlc_lookup(dvp, namep))) {
*ipp = VTOI(vp);
return (0);
}
dname = kmem_zalloc(1024, KM_SLEEP);
buf = kmem_zalloc(udf_vfsp->udf_lbsize, KM_SLEEP);
rw_enter(&dip->i_rwlock, RW_READER);
recheck:
offset = dip->i_diroff;
end = dirsize = dip->i_size;
if (offset > dirsize) {
offset = 0;
}
adhoc_offset = offset;
adhoc_search = (offset == 0) ? 1 : 2;
fbp = NULL;
while (adhoc_search--) {
while (offset < end) {
error = ud_get_next_fid(dip, &fbp,
offset, &fid, &fname, buf);
if (error != 0) {
break;
}
if ((fid->fid_flags & FID_DELETED) == 0) {
if (fid->fid_flags & FID_PARENT) {
id_len = 2;
fname = dummy;
dummy[0] = '.';
dummy[1] = '.';
dummy[2] = '\0';
} else {
if ((error = ud_uncompress(
fid->fid_idlen, &id_len,
fname, dname)) != 0) {
break;
}
fname = (uint8_t *)dname;
fname[id_len] = '\0';
}
if ((namelen == id_len) &&
(strncmp(namep, (caddr_t)fname,
namelen) == 0)) {
uint32_t loc;
uint16_t prn;
loc = SWAP_32(fid->fid_icb.lad_ext_loc);
prn = SWAP_16(fid->fid_icb.lad_ext_prn);
dip->i_diroff = offset + FID_LEN(fid);
if (doingchk) {
if ((loc == old_loc) &&
(prn == old_prn)) {
goto checkok;
} else {
if (fbp != NULL) {
fbrelse(fbp,
S_READ);
fbp = NULL;
}
VN_RELE(ITOV(*ipp));
rw_exit(&dip->i_rwlock);
goto restart;
}
}
if (namelen == 2 &&
fname[0] == '.' &&
fname[1] == '.') {
struct timespec32 omtime;
omtime = dip->i_mtime;
rw_exit(&dip->i_rwlock);
error = ud_iget(dip->i_vfs, prn,
loc, ipp, NULL, cr);
rw_enter(&dip->i_rwlock,
RW_READER);
if (error) {
goto done;
}
if ((omtime.tv_sec !=
dip->i_mtime.tv_sec) ||
(omtime.tv_nsec !=
dip->i_mtime.tv_nsec)) {
doingchk = 1;
old_prn = prn;
old_loc = loc;
dip->i_diroff = 0;
if (fbp != NULL) {
fbrelse(fbp,
S_READ);
fbp = NULL;
}
goto recheck;
}
} else {
error = ud_iget(dip->i_vfs, prn,
loc, ipp, NULL, cr);
}
checkok:
if (error == 0) {
dnlc_enter(dvp, namep,
ITOV(*ipp));
}
goto done;
}
}
offset += FID_LEN(fid);
}
if (fbp != NULL) {
fbrelse(fbp, S_READ);
fbp = NULL;
}
end = adhoc_offset;
offset = 0;
}
error = ENOENT;
done:
kmem_free(buf, udf_vfsp->udf_lbsize);
kmem_free(dname, 1024);
if (fbp != NULL) {
fbrelse(fbp, S_READ);
}
rw_exit(&dip->i_rwlock);
return (error);
}
int
ud_direnter(
struct ud_inode *tdp,
char *namep,
enum de_op op,
struct ud_inode *sdp,
struct ud_inode *sip,
struct vattr *vap,
struct ud_inode **ipp,
struct cred *cr,
caller_context_t *ctp)
{
struct udf_vfs *udf_vfsp;
struct ud_inode *tip;
struct slot slot;
int32_t namlen, err;
char *s;
uint8_t *buf = NULL;
ud_printf("ud_direnter\n");
udf_vfsp = tdp->i_udf;
for (s = namep, namlen = 0; *s; s++, namlen++) {
if (*s == '/') {
return (EACCES);
}
}
if (namlen == 0) {
cmn_err(CE_WARN, "name length == 0 in ud_direnter");
return (EINVAL);
}
ASSERT(RW_WRITE_HELD(&tdp->i_rwlock));
if (namep[0] == '.' &&
(namlen == 1 || (namlen == 2 && namep[1] == '.'))) {
if (op == DE_RENAME) {
return (EINVAL);
}
if (ipp) {
rw_exit(&tdp->i_rwlock);
if (err = ud_dirlook(tdp, namep, ipp, cr, 0)) {
rw_enter(&tdp->i_rwlock, RW_WRITER);
return (err);
}
rw_enter(&tdp->i_rwlock, RW_WRITER);
}
return (EEXIST);
}
tip = NULL;
slot.status = NONE;
slot.offset = 0;
slot.size = 0;
slot.fbp = NULL;
slot.ep = NULL;
slot.endoff = 0;
if (op == DE_LINK || op == DE_RENAME) {
rw_enter(&sip->i_contents, RW_WRITER);
if (sip->i_nlink == 0) {
rw_exit(&sip->i_contents);
return (ENOENT);
}
if (sip->i_nlink == MAXLINK) {
rw_exit(&sip->i_contents);
return (EMLINK);
}
sip->i_nlink++;
mutex_enter(&sip->i_tlock);
sip->i_flag |= ICHG;
mutex_exit(&sip->i_tlock);
ud_iupdat(sip, 1);
rw_exit(&sip->i_contents);
}
if (tdp->i_nlink == 0) {
err = ENOENT;
goto out2;
}
if (tdp->i_type != VDIR) {
err = ENOTDIR;
goto out2;
}
if (err = ud_iaccess(tdp, IEXEC, cr, 1)) {
goto out2;
}
if (op == DE_RENAME) {
if (sip == tdp) {
err = EINVAL;
goto out2;
}
rw_enter(&sip->i_contents, RW_READER);
if ((sip->i_type == VDIR) && (sdp != tdp)) {
uint32_t blkno;
if ((err = ud_iaccess(sip, IWRITE, cr, 0))) {
rw_exit(&sip->i_contents);
goto out2;
}
blkno = sip->i_icb_lbano;
rw_exit(&sip->i_contents);
if ((err = ud_dircheckpath(blkno, tdp, cr))) {
goto out2;
}
} else {
rw_exit(&sip->i_contents);
}
}
buf = kmem_zalloc(udf_vfsp->udf_lbsize, KM_SLEEP);
rw_enter(&tdp->i_contents, RW_WRITER);
if (err = ud_dircheckforname(tdp,
namep, namlen, &slot, &tip, buf, cr)) {
goto out;
}
if (tip) {
switch (op) {
case DE_CREATE :
case DE_MKDIR :
if (ipp) {
*ipp = tip;
err = EEXIST;
} else {
VN_RELE(ITOV(tip));
}
break;
case DE_RENAME :
err = ud_dirrename(sdp, sip, tdp, tip,
namep, buf, &slot, cr);
break;
case DE_LINK :
VN_RELE(ITOV(tip));
err = EEXIST;
break;
}
} else {
if (err = ud_iaccess(tdp, IWRITE, cr, 0)) {
goto out;
}
if ((op == DE_CREATE) || (op == DE_MKDIR)) {
if (err = ud_dirmakeinode(tdp, &sip, vap, op, cr))
goto out;
}
if (err = ud_diraddentry(tdp, namep, op,
namlen, &slot, sip, sdp, cr)) {
if ((op == DE_CREATE) || (op == DE_MKDIR)) {
rw_enter(&sip->i_contents, RW_WRITER);
if (sip->i_type == VDIR) {
tdp->i_nlink--;
}
sip->i_nlink = 0;
mutex_enter(&sip->i_tlock);
sip->i_flag |= ICHG;
mutex_exit(&sip->i_tlock);
rw_exit(&sip->i_contents);
VN_RELE(ITOV(sip));
sip = NULL;
}
} else if (ipp) {
*ipp = sip;
} else if ((op == DE_CREATE) || (op == DE_MKDIR)) {
VN_RELE(ITOV(sip));
}
}
out:
if (buf != NULL) {
kmem_free(buf, udf_vfsp->udf_lbsize);
}
if (slot.fbp) {
fbrelse(slot.fbp, S_OTHER);
}
rw_exit(&tdp->i_contents);
if (op == DE_RENAME) {
if (err == 0) {
if (tip) {
vnevent_rename_dest(ITOV(tip), ITOV(tdp),
namep, ctp);
}
if (sdp != tdp) {
vnevent_rename_dest_dir(ITOV(tdp), ctp);
}
}
if (tip) {
VN_RELE(ITOV(tip));
}
}
out2:
if (err && ((op == DE_LINK) || (op == DE_RENAME))) {
rw_enter(&sip->i_contents, RW_WRITER);
sip->i_nlink--;
rw_exit(&sip->i_contents);
mutex_enter(&sip->i_tlock);
sip->i_flag |= ICHG;
mutex_exit(&sip->i_tlock);
}
return (err);
}
int
ud_dirremove(
struct ud_inode *dp,
char *namep,
struct ud_inode *oip,
struct vnode *cdir,
enum dr_op op,
struct cred *cr,
caller_context_t *ctp)
{
struct udf_vfs *udf_vfsp;
int32_t namelen, err = 0;
struct slot slot;
struct ud_inode *ip;
mode_t mode;
struct file_id *fid;
uint8_t *buf = NULL;
uint32_t tbno;
ud_printf("ud_dirremove\n");
ASSERT(RW_WRITE_HELD(&dp->i_rwlock));
udf_vfsp = dp->i_udf;
namelen = (int)strlen(namep);
if (namelen == 0) {
cmn_err(CE_WARN, "name length == 0 in ud_dirremove");
return (EINVAL);
}
if (namep[0] == '.') {
if (namelen == 1) {
return (EINVAL);
} else if (namelen == 2 && namep[1] == '.') {
return (EEXIST);
}
}
ASSERT(RW_WRITE_HELD(&dp->i_rwlock));
if (dp->i_type != VDIR) {
return (ENOTDIR);
}
ip = NULL;
slot.status = FOUND;
slot.offset = 0;
slot.size = 0;
slot.fbp = NULL;
slot.ep = NULL;
slot.endoff = 0;
if (err = ud_iaccess(dp, IEXEC|IWRITE, cr, 1)) {
return (err);
}
buf = (uint8_t *)kmem_zalloc(udf_vfsp->udf_lbsize, KM_SLEEP);
rw_enter(&dp->i_contents, RW_WRITER);
if (err = ud_dircheckforname(dp, namep, namelen, &slot, &ip,
buf, cr)) {
goto out_novfs;
}
if (ip == NULL) {
err = ENOENT;
goto out_novfs;
}
if (oip && oip != ip) {
err = ENOENT;
goto out_novfs;
}
if ((mode = ip->i_type) == VDIR) {
if (vn_vfswlock(ITOV(ip))) {
err = EBUSY;
goto out_novfs;
}
if (vn_mountedvfs(ITOV(ip)) != NULL && op != DR_RENAME) {
err = EBUSY;
goto out;
}
rw_enter(&ip->i_rwlock, RW_READER);
}
rw_enter(&ip->i_contents, RW_READER);
if (err = ud_sticky_remove_access(dp, ip, cr)) {
rw_exit(&ip->i_contents);
if (mode == VDIR) {
rw_exit(&ip->i_rwlock);
}
goto out;
}
if (op == DR_RMDIR) {
if (dp == ip || ITOV(ip) == cdir) {
err = EINVAL;
} else if (ip->i_type != VDIR) {
err = ENOTDIR;
} else if ((ip->i_nlink != 1) ||
(!ud_dirempty(ip, dp->i_uniqid, cr))) {
err = EEXIST;
}
if (err) {
rw_exit(&ip->i_contents);
if (mode == VDIR) {
rw_exit(&ip->i_rwlock);
}
goto out;
}
} else if (op == DR_REMOVE) {
struct vnode *vp = ITOV(ip);
if (vp->v_type == VDIR &&
secpolicy_fs_linkdir(cr, vp->v_vfsp)) {
err = EPERM;
rw_exit(&ip->i_contents);
rw_exit(&ip->i_rwlock);
goto out;
}
}
rw_exit(&ip->i_contents);
dnlc_remove(ITOV(dp), namep);
fid = slot.ep;
if ((slot.offset + FID_LEN(fid)) == dp->i_size) {
fbrelse(slot.fbp, S_OTHER);
if ((err = ud_itrunc(dp, slot.offset, 0, cr)) != 0) {
goto out;
}
} else {
fid->fid_flags |= FID_DELETED;
if ((err = ud_ip_off2bno(dp, slot.offset, &tbno)) != 0) {
goto out;
}
ud_make_tag(dp->i_udf, &fid->fid_tag,
UD_FILE_ID_DESC, tbno, FID_LEN(fid));
err = ud_write_fid(dp, &slot, buf);
}
slot.fbp = NULL;
if (mode == VDIR) {
rw_exit(&ip->i_rwlock);
}
mutex_enter(&dp->i_tlock);
dp->i_flag |= IUPD|ICHG;
mutex_exit(&dp->i_tlock);
mutex_enter(&ip->i_tlock);
ip->i_flag |= ICHG;
mutex_exit(&ip->i_tlock);
if (err != 0) {
goto out;
}
rw_enter(&ip->i_contents, RW_WRITER);
if (ip->i_nlink > 0) {
if ((op == DR_RMDIR) && (ip->i_type == VDIR)) {
ip->i_nlink --;
dp->i_nlink--;
dnlc_remove(ITOV(ip), ".");
dnlc_remove(ITOV(ip), "..");
} else {
ip->i_nlink--;
}
}
ITIMES_NOLOCK(dp);
ITIMES_NOLOCK(ip);
rw_exit(&ip->i_contents);
out:
if (mode == VDIR) {
vn_vfsunlock(ITOV(ip));
}
out_novfs:
ASSERT(RW_WRITE_HELD(&dp->i_contents));
if (slot.fbp != NULL) {
fbrelse(slot.fbp, S_OTHER);
}
rw_exit(&dp->i_contents);
if (ip) {
if (err == 0) {
if (op == DR_REMOVE) {
vnevent_remove(ITOV(ip), ITOV(dp), namep, ctp);
} else if (op == DR_RMDIR) {
vnevent_rmdir(ITOV(ip), ITOV(dp), namep, ctp);
}
}
VN_RELE(ITOV(ip));
}
kmem_free(buf, udf_vfsp->udf_lbsize);
return (err);
}
int
ud_dircheckforname(struct ud_inode *tdp,
char *namep, int32_t namelen, struct slot *slotp,
struct ud_inode **ipp, uint8_t *buf, struct cred *cr)
{
struct udf_vfs *udf_vfsp;
uint32_t dirsize, offset;
struct fbuf *fbp;
struct file_id *fid;
int32_t sz, error = 0, sz_req, matched = 0;
uint8_t *nm;
uint8_t *dname;
int32_t id_len;
ud_printf("ud_dircheckforname\n");
ASSERT(RW_WRITE_HELD(&tdp->i_rwlock));
fbp = NULL;
dname = (uint8_t *)kmem_zalloc(1024, KM_SLEEP);
udf_vfsp = tdp->i_udf;
offset = 0;
dirsize = tdp->i_size;
if (slotp->status != FOUND) {
int32_t temp;
temp = 1024;
if ((error = ud_compress(namelen, &temp,
(uint8_t *)namep, dname)) != 0) {
goto end;
}
sz_req = F_LEN + temp;
sz_req = (sz_req + 3) & ~3;
}
while (offset < dirsize) {
if ((error = ud_get_next_fid(tdp, &fbp,
offset, &fid, &nm, buf)) != 0) {
break;
}
if ((error = ud_uncompress(fid->fid_idlen,
&id_len, nm, dname)) != 0) {
break;
}
if ((fid->fid_flags & FID_DELETED) == 0) {
if (((namelen == id_len) &&
(strncmp(namep, (caddr_t)dname, namelen) == 0)) ||
((fid->fid_flags & FID_PARENT) &&
(namep[0] == '.' &&
(namelen == 1 ||
(namelen == 2 && namep[1] == '.'))))) {
tdp->i_diroff = offset;
if ((fid->fid_flags & FID_PARENT) &&
(namelen == 1) && (namep[0] == '.')) {
struct vnode *vp = ITOV(tdp);
*ipp = tdp;
VN_HOLD(vp);
} else {
uint16_t prn;
uint32_t loc;
prn = SWAP_16(fid->fid_icb.lad_ext_prn);
loc = SWAP_32(fid->fid_icb.lad_ext_loc);
if ((error = ud_iget(tdp->i_vfs, prn,
loc, ipp, NULL, cr)) != 0) {
fbrelse(fbp, S_OTHER);
goto end;
}
}
slotp->status = EXIST;
slotp->offset = offset;
slotp->size = FID_LEN(fid);
slotp->fbp = fbp;
slotp->ep = fid;
slotp->endoff = 0;
goto end;
}
} else {
if ((slotp->status != FOUND) || (matched == 0)) {
sz = FID_LEN(fid);
if (sz == sz_req) {
slotp->status = FOUND;
slotp->offset = offset;
slotp->size = sz;
}
if (matched == 0) {
if ((namelen == id_len) &&
(strncmp(namep, (caddr_t)dname,
namelen) == 0)) {
matched = 1;
slotp->status = FOUND;
slotp->offset = offset;
slotp->size = sz;
}
}
}
}
offset += FID_LEN(fid);
}
if (fbp) {
fbrelse(fbp, S_OTHER);
}
if (slotp->status == NONE) {
slotp->offset = dirsize;
if (tdp->i_desc_type == ICB_FLAG_ONE_AD) {
slotp->size = tdp->i_max_emb - tdp->i_size;
} else {
slotp->size = udf_vfsp->udf_lbsize -
slotp->offset & udf_vfsp->udf_lbmask;
}
slotp->endoff = 0;
}
*ipp = NULL;
end:
kmem_free((caddr_t)dname, 1024);
return (error);
}
int
ud_dirempty(struct ud_inode *ip, uint64_t ino, struct cred *cr)
{
offset_t off;
int32_t empty = 1, error, count, entry_len, rcount;
struct file_id *fid;
caddr_t addr;
uint32_t tbno;
int32_t desc_len;
ud_printf("ud_dirempty\n");
ASSERT(RW_LOCK_HELD(&ip->i_contents));
if (ip->i_size == 0) {
return (empty);
}
desc_len = 1024;
addr = kmem_zalloc(desc_len, KM_SLEEP);
fid = (struct file_id *)addr;
for (off = 0; off < ip->i_size; off += entry_len) {
rcount = sizeof (struct file_id);
error = ud_rdwri(UIO_READ, FREAD, ip, addr, rcount, off,
UIO_SYSSPACE, &count, cr);
if ((error != 0) || (count != 0)) {
empty = 0;
break;
}
if ((error = ud_ip_off2bno(ip, off, &tbno)) != 0) {
empty = 0;
break;
}
if (ud_verify_tag_and_desc(&fid->fid_tag,
UD_FILE_ID_DESC,
tbno, 0, desc_len) != 0) {
empty = 0;
break;
}
rcount = FID_LEN(fid);
error = ud_rdwri(UIO_READ, FREAD, ip, addr, rcount, off,
UIO_SYSSPACE, &count, cr);
if ((error != 0) || (count != 0)) {
empty = 0;
break;
}
if (ud_verify_tag_and_desc(&fid->fid_tag,
UD_FILE_ID_DESC,
tbno,
1, rcount) != 0) {
empty = 0;
break;
}
if ((fid->fid_flags & FID_DELETED) == 0) {
if ((fid->fid_flags & FID_PARENT) == 0) {
empty = 0;
break;
}
}
entry_len = FID_LEN(fid);
}
kmem_free(addr, 1024);
return (empty);
}
int
ud_dircheckpath(int32_t blkno,
struct ud_inode *target, struct cred *cr)
{
int32_t err = 0;
struct vfs *vfsp;
struct udf_vfs *udf_vfsp;
struct fbuf *fbp;
struct file_id *fid;
struct ud_inode *ip, *tip;
uint16_t prn;
uint32_t lbno, dummy, tbno;
daddr_t parent_icb_loc;
ud_printf("ud_dircheckpath\n");
udf_vfsp = target->i_udf;
ip = target;
ASSERT(udf_vfsp != NULL);
ASSERT(MUTEX_HELD(&target->i_udf->udf_rename_lck));
ASSERT(RW_WRITE_HELD(&ip->i_rwlock));
if (ip->i_icb_lbano == blkno) {
err = EINVAL;
goto out;
}
if (ip->i_icb_lbano == udf_vfsp->udf_root_blkno) {
goto out;
}
for (;;) {
if ((err = fbread(ITOV(ip), 0,
udf_vfsp->udf_lbsize, S_READ, &fbp)) != 0) {
break;
}
if ((err = ud_ip_off2bno(ip, 0, &tbno)) != 0) {
break;
}
fid = (struct file_id *)fbp->fb_addr;
if (ud_verify_tag_and_desc(&fid->fid_tag,
UD_FILE_ID_DESC,
tbno,
1, udf_vfsp->udf_lbsize) != 0) {
break;
}
if ((fid->fid_flags & FID_DELETED) != 0) {
break;
}
if ((fid->fid_flags & FID_PARENT) == 0) {
break;
}
prn = SWAP_16(fid->fid_icb.lad_ext_prn);
lbno = SWAP_32(fid->fid_icb.lad_ext_loc);
parent_icb_loc =
ud_xlate_to_daddr(udf_vfsp, prn, lbno, 1, &dummy);
ASSERT(dummy == 1);
if (parent_icb_loc == blkno) {
err = EINVAL;
break;
}
vfsp = ip->i_vfs;
udf_vfsp = ip->i_udf;
if (parent_icb_loc == udf_vfsp->udf_root_blkno) {
break;
}
if (fbp != NULL) {
fbrelse(fbp, S_OTHER);
fbp = NULL;
}
if (ip != target) {
rw_exit(&ip->i_rwlock);
VN_RELE(ITOV(ip));
}
if (err = ud_iget(vfsp, prn, lbno, &tip, NULL, cr)) {
ip = NULL;
break;
}
ip = tip;
rw_enter(&ip->i_rwlock, RW_READER);
}
if (fbp) {
fbrelse(fbp, S_OTHER);
}
out:
if (ip) {
if (ip != target) {
rw_exit(&ip->i_rwlock);
VN_RELE(ITOV(ip));
}
}
return (err);
}
int
ud_dirmakeinode(struct ud_inode *tdp, struct ud_inode **ipp,
struct vattr *vap, enum de_op op, struct cred *cr)
{
struct ud_inode *ip;
int32_t error;
ASSERT(vap != NULL);
ASSERT(op == DE_CREATE || op == DE_MKDIR);
ASSERT((vap->va_mask & (AT_TYPE|AT_MODE)) == (AT_TYPE|AT_MODE));
ASSERT(RW_WRITE_HELD(&tdp->i_rwlock));
if ((error = ud_ialloc(tdp, &ip, vap, cr)) != 0) {
return (error);
}
ASSERT(ip != NULL);
rw_enter(&ip->i_contents, RW_WRITER);
if (op == DE_MKDIR) {
error = ud_dirmakedirect(ip, tdp, cr);
}
ip->i_flag |= IACC|IUPD|ICHG;
if (vap->va_mask & AT_ATIME)
ip->i_flag &= ~IACC;
if (vap->va_mask & AT_MTIME)
ip->i_flag &= ~IUPD;
ud_iupdat(ip, 1);
*ipp = ip;
rw_exit(&ip->i_contents);
return (error);
}
int
ud_diraddentry(struct ud_inode *tdp, char *namep,
enum de_op op, int32_t namelen, struct slot *slotp,
struct ud_inode *sip, struct ud_inode *sdp, struct cred *cr)
{
struct udf_vfs *udf_vfsp;
int32_t error, temp;
struct file_id *fid;
uint8_t *buf = NULL;
ASSERT(RW_WRITE_HELD(&tdp->i_rwlock));
ud_printf("ud_diraddentry\n");
udf_vfsp = sip->i_udf;
if (ITOV(tdp)->v_vfsp != ITOV(sip)->v_vfsp) {
error = EXDEV;
goto bad;
}
if ((op == DE_RENAME) && (sip->i_type == VDIR)) {
if ((error = ud_dirfixdotdot(sip, sdp, tdp)) != 0) {
goto bad;
}
}
buf = (uint8_t *)kmem_zalloc(udf_vfsp->udf_lbsize, KM_SLEEP);
fid = (struct file_id *)buf;
fid->fid_ver = SWAP_16(1);
if (sip->i_type == VDIR) {
fid->fid_flags = FID_DIR;
} else {
fid->fid_flags = 0;
}
fid->fid_iulen = 0;
fid->fid_icb.lad_ext_len = SWAP_32(sip->i_udf->udf_lbsize);
fid->fid_icb.lad_ext_loc = SWAP_32(sip->i_icb_block);
fid->fid_icb.lad_ext_prn = SWAP_16(sip->i_icb_prn);
fid->fid_iulen = 0;
temp = MIN(udf_vfsp->udf_lbsize - F_LEN, MAXNAMELEN);
if ((error = ud_compress(namelen, &temp,
(uint8_t *)namep, fid->fid_spec)) == 0) {
fid->fid_idlen = (uint8_t)temp;
error = ud_dirprepareentry(tdp, slotp, buf, cr);
}
kmem_free(buf, udf_vfsp->udf_lbsize);
bad:
return (error);
}
int
ud_dirmakedirect(struct ud_inode *ip,
struct ud_inode *dp, struct cred *cr)
{
int32_t err;
uint32_t blkno, size, parent_len, tbno;
struct fbuf *fbp;
struct file_id *fid;
struct icb_ext *iext;
ud_printf("ud_dirmakedirect\n");
ASSERT(RW_WRITE_HELD(&ip->i_contents));
ASSERT(RW_WRITE_HELD(&dp->i_rwlock));
parent_len = sizeof (struct file_id);
if ((ip->i_desc_type != ICB_FLAG_ONE_AD) ||
(parent_len > ip->i_max_emb)) {
ASSERT(ip->i_ext);
if ((err = ud_alloc_space(ip->i_vfs, ip->i_icb_prn,
0, 1, &blkno, &size, 0, 0)) != 0) {
return (err);
}
ip->i_size = sizeof (struct file_id);
ip->i_flag |= IUPD|ICHG|IATTCHG;
iext = ip->i_ext;
iext->ib_prn = ip->i_icb_prn;
iext->ib_block = blkno;
iext->ib_count = ip->i_size;
iext->ib_offset = 0;
ip->i_ext_used = 1;
} else {
ip->i_size = sizeof (struct file_id);
ip->i_flag |= IUPD|ICHG|IATTCHG;
}
ITIMES_NOLOCK(ip);
if (dp->i_nlink == MAXLINK) {
return (EMLINK);
}
dp->i_nlink++;
dp->i_flag |= ICHG;
ud_iupdat(dp, 1);
rw_exit(&ip->i_contents);
if ((err = fbread(ITOV(ip), (offset_t)0,
ip->i_udf->udf_lbsize, S_WRITE, &fbp)) != 0) {
rw_enter(&ip->i_contents, RW_WRITER);
return (err);
}
bzero(fbp->fb_addr, ip->i_udf->udf_lbsize);
fid = (struct file_id *)fbp->fb_addr;
fid->fid_ver = SWAP_16(1);
fid->fid_flags = FID_DIR | FID_PARENT;
fid->fid_icb.lad_ext_len = SWAP_32(dp->i_udf->udf_lbsize);
fid->fid_icb.lad_ext_loc = SWAP_32(dp->i_icb_block);
fid->fid_icb.lad_ext_prn = SWAP_16(dp->i_icb_prn);
if ((err = ud_ip_off2bno(ip, 0, &tbno)) == 0) {
ud_make_tag(ip->i_udf, &fid->fid_tag,
UD_FILE_ID_DESC, tbno, FID_LEN(fid));
}
err = ud_fbwrite(fbp, ip);
rw_enter(&ip->i_contents, RW_WRITER);
return (err);
}
int
ud_dirrename(struct ud_inode *sdp, struct ud_inode *sip,
struct ud_inode *tdp, struct ud_inode *tip, char *namep,
uint8_t *buf, struct slot *slotp, struct cred *cr)
{
int32_t error = 0, doingdirectory;
struct file_id *fid;
ud_printf("ud_dirrename\n");
ASSERT(sdp->i_udf != NULL);
ASSERT(MUTEX_HELD(&sdp->i_udf->udf_rename_lck));
ASSERT(RW_WRITE_HELD(&tdp->i_rwlock));
ASSERT(RW_WRITE_HELD(&tdp->i_contents));
ASSERT(buf);
ASSERT(slotp->ep);
fid = slotp->ep;
if (sip->i_icb_lbano == tip->i_icb_lbano) {
return (ESAME);
}
rw_enter(&sip->i_contents, RW_READER);
rw_enter(&tip->i_contents, RW_READER);
if ((ITOV(tip)->v_vfsp != ITOV(tdp)->v_vfsp) ||
(ITOV(tip)->v_vfsp != ITOV(sip)->v_vfsp)) {
error = EXDEV;
goto out;
}
if ((error = ud_iaccess(tdp, IWRITE, cr, 0)) != 0 ||
(error = ud_sticky_remove_access(tdp, tip, cr)) != 0)
goto out;
doingdirectory = (sip->i_type == VDIR);
if (tip->i_type == VDIR) {
if (!doingdirectory) {
error = EISDIR;
goto out;
}
if (vn_vfswlock(ITOV(tip))) {
error = EBUSY;
goto out;
}
if (vn_mountedvfs(ITOV(tip)) != NULL) {
vn_vfsunlock(ITOV(tip));
error = EBUSY;
goto out;
}
if (!ud_dirempty(tip, tdp->i_uniqid, cr) || tip->i_nlink > 2) {
vn_vfsunlock(ITOV(tip));
error = EEXIST;
goto out;
}
} else if (doingdirectory) {
error = ENOTDIR;
goto out;
}
dnlc_remove(ITOV(tdp), namep);
fid->fid_icb.lad_ext_prn = SWAP_16(sip->i_icb_prn);
fid->fid_icb.lad_ext_loc = SWAP_32(sip->i_icb_block);
dnlc_enter(ITOV(tdp), namep, ITOV(sip));
ud_make_tag(tdp->i_udf, &fid->fid_tag, UD_FILE_ID_DESC,
SWAP_32(fid->fid_tag.tag_loc), FID_LEN(fid));
error = ud_write_fid(tdp, slotp, buf);
if (error) {
if (doingdirectory) {
vn_vfsunlock(ITOV(tip));
}
goto out;
}
rw_exit(&tip->i_contents);
rw_enter(&tip->i_contents, RW_WRITER);
mutex_enter(&tdp->i_tlock);
tdp->i_flag |= IUPD|ICHG;
mutex_exit(&tdp->i_tlock);
tip->i_nlink--;
mutex_enter(&tip->i_tlock);
tip->i_flag |= ICHG;
mutex_exit(&tip->i_tlock);
if (doingdirectory) {
vn_vfsunlock(ITOV(tip));
if (tip->i_nlink != 0) {
cmn_err(CE_WARN,
"ud_direnter: target directory link count != 0");
rw_exit(&tip->i_contents);
rw_exit(&sip->i_contents);
return (EINVAL);
}
tdp->i_nlink--;
mutex_enter(&tdp->i_tlock);
tdp->i_flag |= ICHG;
mutex_exit(&tdp->i_tlock);
ITIMES_NOLOCK(tdp);
if (sdp != tdp) {
rw_exit(&tip->i_contents);
rw_exit(&sip->i_contents);
error = ud_dirfixdotdot(sip, sdp, tdp);
return (error);
}
}
out:
rw_exit(&tip->i_contents);
rw_exit(&sip->i_contents);
return (error);
}
int
ud_dirprepareentry(struct ud_inode *dp,
struct slot *slotp, uint8_t *buf, struct cred *cr)
{
struct fbuf *fbp;
uint16_t old_dtype;
int32_t error = 0;
uint32_t entrysize, count, offset, tbno, old_size, off;
struct file_id *fid;
int32_t lbsize, lbmask, mask;
ASSERT(RW_WRITE_HELD(&dp->i_rwlock));
ASSERT((slotp->status == NONE) || (slotp->status == FOUND));
ud_printf("ud_dirprepareentry\n");
lbsize = dp->i_udf->udf_lbsize;
lbmask = dp->i_udf->udf_lbmask;
mask = ~lbmask;
fid = (struct file_id *)buf;
entrysize = FID_LEN(fid);
if (slotp->status == NONE) {
if (entrysize > slotp->size) {
old_dtype = dp->i_desc_type;
old_size = (uint32_t)dp->i_size;
error = ud_bmap_write(dp, slotp->offset,
blkoff(dp->i_udf, slotp->offset) + entrysize,
0, cr);
if (error != 0) {
return (error);
}
if (old_dtype != dp->i_desc_type) {
if ((error = ud_ip_off2bno(dp,
0, &tbno)) != 0) {
return (error);
}
if ((error = fbread(ITOV(dp), 0,
dp->i_udf->udf_lbsize,
S_WRITE, &fbp)) != 0) {
return (error);
}
off = 0;
while (off < old_size) {
struct file_id *tfid;
tfid = (struct file_id *)
(fbp->fb_addr + off);
ud_make_tag(dp->i_udf, &tfid->fid_tag,
UD_FILE_ID_DESC, tbno,
FID_LEN(tfid));
off += FID_LEN(tfid);
}
if (error = ud_fbwrite(fbp, dp)) {
return (error);
}
}
} else {
if (dp->i_desc_type != ICB_FLAG_ONE_AD) {
ASSERT(dp->i_ext);
dp->i_ext[dp->i_ext_used - 1].ib_count +=
entrysize;
}
}
dp->i_size += entrysize;
dp->i_flag |= IUPD|ICHG|IATTCHG;
ITIMES_NOLOCK(dp);
} else if (slotp->status != FOUND) {
cmn_err(CE_WARN, "status is not NONE/FOUND");
return (EINVAL);
}
if ((error = ud_ip_off2bno(dp, slotp->offset, &tbno)) != 0) {
return (error);
}
ud_make_tag(dp->i_udf, &fid->fid_tag, UD_FILE_ID_DESC,
tbno, FID_LEN(fid));
offset = slotp->offset;
if ((error = fbread(ITOV(dp), offset & mask, lbsize,
S_WRITE, &fbp)) != 0) {
return (error);
}
if ((offset & mask) != ((offset + entrysize) & mask)) {
count = entrysize - ((offset + entrysize) & lbmask);
} else {
count = entrysize;
}
bcopy((caddr_t)buf, fbp->fb_addr + (offset & lbmask), count);
if (error = ud_fbwrite(fbp, dp)) {
return (error);
}
if (entrysize > count) {
if ((error = fbread(ITOV(dp), (offset + entrysize) & mask,
lbsize, S_WRITE, &fbp)) != 0) {
return (error);
}
bcopy((caddr_t)(buf + count), fbp->fb_addr, entrysize - count);
if (error = ud_fbwrite(fbp, dp)) {
return (error);
}
}
dp->i_flag |= IUPD|ICHG|IATTCHG;
ITIMES_NOLOCK(dp);
return (error);
}
int
ud_dirfixdotdot(struct ud_inode *dp,
struct ud_inode *opdp, struct ud_inode *npdp)
{
int32_t err = 0;
struct fbuf *fbp;
struct file_id *fid;
uint32_t loc, dummy, tbno;
ud_printf("ud_dirfixdotdot\n");
ASSERT(opdp->i_type == VDIR);
ASSERT(npdp->i_type == VDIR);
ASSERT(RW_WRITE_HELD(&npdp->i_rwlock));
err = fbread(ITOV(dp), (offset_t)0,
dp->i_udf->udf_lbsize, S_WRITE, &fbp);
if (err || dp->i_nlink == 0 ||
dp->i_size < sizeof (struct file_id)) {
goto bad;
}
if ((err = ud_ip_off2bno(dp, 0, &tbno)) != 0) {
goto bad;
}
fid = (struct file_id *)fbp->fb_addr;
if ((ud_verify_tag_and_desc(&fid->fid_tag, UD_FILE_ID_DESC,
tbno,
1, dp->i_udf->udf_lbsize) != 0) ||
((fid->fid_flags & (FID_DIR | FID_PARENT)) !=
(FID_DIR | FID_PARENT))) {
err = ENOTDIR;
goto bad;
}
loc = ud_xlate_to_daddr(dp->i_udf,
SWAP_16(fid->fid_icb.lad_ext_prn),
SWAP_32(fid->fid_icb.lad_ext_loc), 1, &dummy);
ASSERT(dummy == 1);
if (loc == npdp->i_icb_lbano) {
goto bad;
}
if (npdp->i_nlink == MAXLINK) {
err = EMLINK;
goto bad;
}
npdp->i_nlink++;
mutex_enter(&npdp->i_tlock);
npdp->i_flag |= ICHG;
mutex_exit(&npdp->i_tlock);
ud_iupdat(npdp, 1);
dnlc_remove(ITOV(dp), "..");
fid->fid_icb.lad_ext_loc = SWAP_32(npdp->i_icb_block);
fid->fid_icb.lad_ext_prn = SWAP_16(npdp->i_icb_prn);
ud_make_tag(npdp->i_udf, &fid->fid_tag,
UD_FILE_ID_DESC, tbno, FID_LEN(fid));
dnlc_enter(ITOV(dp), "..", ITOV(npdp));
err = ud_fbwrite(fbp, dp);
fbp = NULL;
if (err != 0) {
goto bad;
}
if (opdp != NULL) {
rw_enter(&opdp->i_contents, RW_WRITER);
if (opdp->i_nlink != 0) {
opdp->i_nlink--;
mutex_enter(&opdp->i_tlock);
opdp->i_flag |= ICHG;
mutex_exit(&opdp->i_tlock);
ud_iupdat(opdp, 1);
}
rw_exit(&opdp->i_contents);
}
return (0);
bad:
if (fbp) {
fbrelse(fbp, S_OTHER);
}
return (err);
}
int32_t
ud_write_fid(struct ud_inode *dp, struct slot *slot, uint8_t *buf)
{
struct udf_vfs *udf_vfsp;
struct fbuf *lfbp;
struct file_id *fid;
int32_t error = 0;
uint32_t lbsize, lbmask, count, old_count;
ASSERT(slot->fbp);
ASSERT(slot->ep);
udf_vfsp = dp->i_udf;
fid = slot->ep;
lbsize = dp->i_udf->udf_lbsize;
lbmask = dp->i_udf->udf_lbmask;
if (((uint8_t *)fid >= buf) &&
((uint8_t *)fid < &buf[udf_vfsp->udf_lbsize])) {
if ((error = fbread(ITOV(dp),
(offset_t)(slot->offset & ~lbmask),
lbsize, S_WRITE, &lfbp)) != 0) {
goto out;
}
if (((slot->offset & lbmask) +
sizeof (struct file_id)) > lbsize) {
if ((slot->offset & lbmask) != 0) {
old_count = lbsize -
(slot->offset & lbmask);
count = (slot->offset +
sizeof (struct file_id)) &
lbmask;
} else {
old_count = 0;
count = sizeof (struct file_id);
}
bcopy(buf, lfbp->fb_addr +
(slot->offset & lbmask), old_count);
bcopy(buf + old_count,
slot->fbp->fb_addr, count);
error = ud_fbwrite(lfbp, dp);
error = ud_fbwrite(slot->fbp, dp);
} else {
bcopy(buf, lfbp->fb_addr +
(slot->offset & lbmask),
sizeof (struct file_id));
error = ud_fbwrite(lfbp, dp);
fbrelse(slot->fbp, S_OTHER);
}
} else {
if ((error = ud_fbwrite(slot->fbp, dp)) != 0) {
fid->fid_flags &= ~FID_DELETED;
ud_make_tag(dp->i_udf, &fid->fid_tag, UD_FILE_ID_DESC,
SWAP_32(fid->fid_tag.tag_loc), FID_LEN(fid));
}
}
slot->fbp = NULL;
out:
return (error);
}