vol: remove empty directories left by vos zap -force
[openafs.git] / src / vol / namei_ops.c
index 0c006f8..1916843 100644 (file)
 #include <direct.h>
 #endif
 
+#include <afs/opr.h>
+#include <rx/rx_queue.h>
+#ifdef AFS_PTHREAD_ENV
+# include <opr/lock.h>
+#endif
 #include <lock.h>
 #include <afs/afsutil.h>
 #include <lwp.h>
@@ -65,6 +70,7 @@
 
 int Testing=0;
 
+static void namei_UnlockLinkCount(FdHandle_t * fdP, Inode ino);
 
 afs_sfsize_t
 namei_iread(IHandle_t * h, afs_foff_t offset, char *buf, afs_fsize_t size)
@@ -320,7 +326,7 @@ int
 namei_ViceREADME(char *partition)
 {
     char filename[32];
-    int fd;
+    int fd, len, e = 0;
 
     /* Create the inode directory if we're starting for the first time */
     snprintf(filename, sizeof filename, "%s" OS_DIRSEP "%s", partition,
@@ -332,8 +338,12 @@ namei_ViceREADME(char *partition)
              partition, INODEDIR);
     fd = OS_OPEN(filename, O_WRONLY | O_CREAT | O_TRUNC, 0444);
     if (fd != INVALID_FD) {
-       (void)OS_WRITE(fd, VICE_README, strlen(VICE_README));
+       len = strlen(VICE_README);
+       if (OS_WRITE(fd, VICE_README, len) != len)
+           e = errno;
        OS_CLOSE(fd);
+       if (e)
+           errno = e;
     }
     return (errno);
 }
@@ -448,7 +458,7 @@ delTree(char *root, char *tree, int *errp)
            cp = tree + strlen(tree);   /* move cp to the end of string tree */
 
        /* now delete all entries in this dir */
-       if ((ds = opendir(root)) != (DIR *) NULL) {
+       if ((ds = opendir(root)) != NULL) {
            errno = 0;
            while ((dirp = readdir(ds))) {
                /* ignore . and .. */
@@ -594,7 +604,7 @@ namei_RemoveDataDirectories(namei_t * name)
  * types, but if we get that far, this could should be dead by then.
  */
 Inode
-namei_MakeSpecIno(int volid, int type)
+namei_MakeSpecIno(VolumeId volid, int type)
 {
     Inode ino;
     ino = NAMEI_INODESPECIAL;
@@ -947,20 +957,20 @@ bad:
 }
 #else /* !AFS_NT40_ENV */
 Inode
-namei_icreate(IHandle_t * lh, char *part, afs_uint32 p1, afs_uint32 p2, afs_uint32 p3, afs_uint32 p4)
+icreate(IHandle_t * lh, char *part, afs_uint32 p1, afs_uint32 p2, afs_uint32 p3, afs_uint32 p4,
+        IHandle_t **a_ih)
 {
     namei_t name;
     int fd = INVALID_FD;
     int code = 0;
     int created_dir = 0;
     IHandle_t tmp;
+    IHandle_t *realh = NULL;
     FdHandle_t *fdP;
-    FdHandle_t tfd;
     int tag;
     int ogm_parm;
 
     memset((void *)&tmp, 0, sizeof(IHandle_t));
-    memset(&tfd, 0, sizeof(FdHandle_t));
 
     tmp.ih_dev = volutil_GetPartitionID(part);
     if (tmp.ih_dev == -1) {
@@ -1028,19 +1038,20 @@ namei_icreate(IHandle_t * lh, char *part, afs_uint32 p1, afs_uint32 p2, afs_uint
        goto bad;
     }
 
+    IH_INIT(realh, tmp.ih_dev, tmp.ih_vid, tmp.ih_ino);
+    fdP = ih_attachfd(realh, fd);
+
+    /* ih_attachfd can only return NULL if we give it an invalid fd; our fd
+     * must be valid by this point. */
+    opr_Assert(fdP);
+
     if (p2 == (afs_uint32)-1 && p3 == VI_LINKTABLE) {
-       /* hack at tmp to setup for set link count call. */
-       memset((void *)&tfd, 0, sizeof(FdHandle_t));    /* minimalistic still, but a little cleaner */
-       tfd.fd_ih = &tmp;
-       tfd.fd_fd = fd;
-       code = namei_SetLinkCount(&tfd, (Inode) 0, 1, 0);
+       code = namei_SetLinkCount(fdP, (Inode) 0, 1, 0);
     }
 
-  bad:
-    if (fd != INVALID_FD)
-       OS_CLOSE(fd);
-
+    FDH_CLOSE(fdP);
 
+  bad:
     if (code || (fd == INVALID_FD)) {
        if (p2 != -1) {
            fdP = IH_OPEN(lh);
@@ -1049,8 +1060,45 @@ namei_icreate(IHandle_t * lh, char *part, afs_uint32 p1, afs_uint32 p2, afs_uint
                FDH_CLOSE(fdP);
            }
        }
+       IH_RELEASE(realh);
+    }
+
+    *a_ih = realh;
+
+    return code;
+}
+
+Inode
+namei_icreate(IHandle_t * lh, char *part,
+              afs_uint32 p1, afs_uint32 p2, afs_uint32 p3, afs_uint32 p4)
+{
+    Inode ino;
+    IHandle_t *ihP = NULL;
+    int code;
+
+    code = icreate(lh, part, p1, p2, p3, p4, &ihP);
+    if (code || !ihP) {
+       opr_Assert(!ihP);
+       ino = -1;
+    } else {
+       ino = ihP->ih_ino;
+       IH_RELEASE(ihP);
+    }
+    return ino;
+}
+
+IHandle_t *
+namei_icreate_init(IHandle_t * lh, int dev, char *part,
+                   afs_uint32 p1, afs_uint32 p2, afs_uint32 p3, afs_uint32 p4)
+{
+    int code;
+    IHandle_t *ihP = NULL;
+
+    code = icreate(lh, part, p1, p2, p3, p4, &ihP);
+    if (code) {
+       opr_Assert(!ihP);
     }
-    return (code || (fd == INVALID_FD)) ? (Inode) - 1 : tmp.ih_ino;
+    return ihP;
 }
 #endif
 
@@ -1111,20 +1159,34 @@ namei_dec(IHandle_t * ih, Inode ino, int p1)
            }
 
            count--;
-           if (namei_SetLinkCount(fdP, (Inode) 0, count < 0 ? 0 : count, 1) <
-               0) {
-               FDH_REALLYCLOSE(fdP);
-               IH_RELEASE(tmp);
-               return -1;
-           }
-
            if (count > 0) {
+               /* if our count is non-zero, we just set our new linkcount and
+                * return. But if our count is 0, don't bother updating the
+                * linktable, since we're about to delete the link table,
+                * below. */
+               if (namei_SetLinkCount(fdP, (Inode) 0, count < 0 ? 0 : count, 1) < 0) {
+                   FDH_REALLYCLOSE(fdP);
+                   IH_RELEASE(tmp);
+                   return -1;
+               }
+
                FDH_CLOSE(fdP);
                IH_RELEASE(tmp);
                return 0;
            }
+
+           namei_UnlockLinkCount(fdP, (Inode) 0);
        }
 
+       /* We should IH_REALLYCLOSE right before deleting the special file from
+        * disk, to ensure that somebody else cannot create a special inode,
+        * then IH_OPEN that special inode and get back a cached fd for the
+        * file we are deleting here (instead of an fd for the file they just
+        * created). */
+       IH_REALLYCLOSE(tmp);
+       FDH_REALLYCLOSE(fdP);
+       IH_RELEASE(tmp);
+
        if ((code = OS_UNLINK(name.n_path)) == 0) {
            if (type == VI_LINKTABLE) {
                /* Try to remove directory. If it fails, that's ok.
@@ -1138,8 +1200,6 @@ namei_dec(IHandle_t * ih, Inode ino, int p1)
                (void)namei_RemoveDataDirectories(&name);
            }
        }
-       FDH_REALLYCLOSE(fdP);
-       IH_RELEASE(tmp);
     } else {
        /* Get a file descriptor handle for this Inode */
        fdP = IH_OPEN(ih);
@@ -1161,8 +1221,8 @@ namei_dec(IHandle_t * ih, Inode ino, int p1)
        } else {
            IHandle_t *th;
            IH_INIT(th, ih->ih_dev, ih->ih_vid, ino);
-           Log("Warning: Lost ref on ihandle dev %d vid %d ino %lld\n",
-               th->ih_dev, th->ih_vid, (afs_int64)th->ih_ino);
+           Log("Warning: Lost ref on ihandle dev %d vid %" AFS_VOLID_FMT " ino %lld\n",
+               th->ih_dev, afs_printable_VolumeId_lu(th->ih_vid), (afs_int64)th->ih_ino);
            IH_RELEASE(th);
 
            /* If we're less than 0, someone presumably unlinked;
@@ -1257,7 +1317,7 @@ namei_copy_on_write(IHandle_t *h)
     if (afs_stat(name.n_path, &tstat) < 0)
        return EIO;
     if (tstat.st_nlink > 1) {                   /* do a copy on write */
-       char path[259];
+       char path[NAMEI_PATH_LEN + 4];
        char *buf;
        afs_size_t size;
        ssize_t tlen;
@@ -1451,8 +1511,8 @@ namei_GetLCOffsetAndIndexFromIno(Inode ino, afs_foff_t * offset, int *index)
 #ifdef AFS_PTHREAD_ENV
 /* XXX do static initializers work for WINNT/pthread? */
 pthread_mutex_t _namei_glc_lock = PTHREAD_MUTEX_INITIALIZER;
-#define NAMEI_GLC_LOCK MUTEX_ENTER(&_namei_glc_lock)
-#define NAMEI_GLC_UNLOCK MUTEX_EXIT(&_namei_glc_lock)
+#define NAMEI_GLC_LOCK opr_mutex_enter(&_namei_glc_lock)
+#define NAMEI_GLC_UNLOCK opr_mutex_exit(&_namei_glc_lock)
 #else /* !AFS_PTHREAD_ENV */
 #define NAMEI_GLC_LOCK
 #define NAMEI_GLC_UNLOCK
@@ -1512,7 +1572,10 @@ namei_GetLinkCount(FdHandle_t * h, Inode ino, int lockit, int fixup, int nowrite
            NAMEI_GLC_UNLOCK;
            goto bad_getLinkByte;
        }
-        FDH_TRUNC(h, offset+sizeof(row));
+        if (FDH_TRUNC(h, offset+sizeof(row))) {
+           NAMEI_GLC_UNLOCK;
+           goto bad_getLinkByte;
+       }
         row = 1 << index;
        rc = FDH_PWRITE(h, (char *)&row, sizeof(row), offset);
        NAMEI_GLC_UNLOCK;
@@ -1547,12 +1610,6 @@ namei_GetLinkCount(FdHandle_t * h, Inode ino, int lockit, int fixup, int nowrite
     return -1;
 }
 
-int
-namei_SetNonZLC(FdHandle_t * h, Inode ino)
-{
-    return namei_GetLinkCount(h, ino, 0, 1, 0);
-}
-
 /* Return a free column index for this vnode. */
 static int
 GetFreeTag(IHandle_t * ih, int vno)
@@ -1665,18 +1722,29 @@ namei_SetLinkCount(FdHandle_t * fdP, Inode ino, int count, int locked)
     return (int)nBytes;
 }
 
+static void
+namei_UnlockLinkCount(FdHandle_t * fdP, Inode ino)
+{
+    afs_foff_t offset;
+    int index;
+
+    namei_GetLCOffsetAndIndexFromIno(ino, &offset, &index);
+
+    FDH_UNLOCKFILE(fdP, offset);
+}
+
 
 /* ListViceInodes - write inode data to a results file. */
 static int DecodeInode(char *dpath, char *name, struct ViceInodeInfo *info,
-                      unsigned int volid);
-static int DecodeVolumeName(char *name, unsigned int *vid);
+                      IHandle_t *myIH);
+static int DecodeVolumeName(char *name, VolumeId *vid);
 static int namei_ListAFSSubDirs(IHandle_t * dirIH,
                                int (*write_fun) (FD_t,
                                                  struct ViceInodeInfo *,
                                                  char *, char *), FD_t fp,
                                int (*judgeFun) (struct ViceInodeInfo *,
-                                                afs_uint32 vid, void *),
-                               afs_uint32 singleVolumeNumber, void *rock);
+                                                VolumeId vid, void *),
+                               VolumeId singleVolumeNumber, void *rock);
 
 
 /* WriteInodeInfo
@@ -1740,8 +1808,8 @@ VerifyDirPerms(char *path)
  */
 int
 ListViceInodes(char *devname, char *mountedOn, FD_t inodeFile,
-              int (*judgeInode) (struct ViceInodeInfo * info, afs_uint32 vid, void *rock),
-              afs_uint32 singleVolumeNumber, int *forcep, int forceR, char *wpath,
+              int (*judgeInode) (struct ViceInodeInfo * info, VolumeId vid, void *rock),
+              VolumeId singleVolumeNumber, int *forcep, int forceR, char *wpath,
               void *rock)
 {
     int ninodes;
@@ -1807,8 +1875,8 @@ namei_ListAFSFiles(char *dev,
                   int (*writeFun) (FD_t, struct ViceInodeInfo *, char *,
                                    char *),
                   FD_t fp,
-                  int (*judgeFun) (struct ViceInodeInfo *, afs_uint32, void *),
-                  afs_uint32 singleVolumeNumber, void *rock)
+                  int (*judgeFun) (struct ViceInodeInfo *, VolumeId, void *),
+                  VolumeId singleVolumeNumber, void *rock)
 {
     IHandle_t ih;
     namei_t name;
@@ -1924,14 +1992,14 @@ _namei_examine_special(char * path1,
                       int (*writeFun) (FD_t, struct ViceInodeInfo *, char *,
                                        char *),
                       FD_t fp,
-                      int (*judgeFun) (struct ViceInodeInfo *, afs_uint32, void *),
-                      int singleVolumeNumber,
+                      int (*judgeFun) (struct ViceInodeInfo *, VolumeId, void *),
+                      VolumeId singleVolumeNumber,
                       void *rock)
 {
     int ret = 0;
     struct ViceInodeInfo info;
 
-    if (DecodeInode(path1, dname, &info, myIH->ih_vid) < 0) {
+    if (DecodeInode(path1, dname, &info, myIH) < 0) {
        ret = 0;
        goto error;
     }
@@ -1943,9 +2011,14 @@ _namei_examine_special(char * path1,
         * consistent with VGID encoded in namei path */
        Log("namei_ListAFSSubDirs: warning: inconsistent linktable "
            "filename \"%s" OS_DIRSEP "%s\"; salvager will delete it "
-           "(dir_vgid=%u, inode_vgid=%u)\n",
-           path1, dname, myIH->ih_vid,
-           info.u.param[0]);
+           "(dir_vgid=%" AFS_VOLID_FMT ", inode_vgid=%" AFS_VOLID_FMT ")\n",
+           path1, dname, afs_printable_VolumeId_lu(myIH->ih_vid),
+           afs_printable_VolumeId_lu(info.u.param[0]));
+       /* We need to set the linkCount to _something_, so linkCount
+        * doesn't just contain stack garbage. Set it to 0, so in case
+        * the salvager or whatever our caller is does try to process
+        * this like a normal file, we won't try to INC or DEC it. */
+       info.linkCount = 0;
     } else {
        char path2[512];
        /* Open this handle */
@@ -2007,8 +2080,8 @@ _namei_examine_reg(char * path3,
                   int (*writeFun) (FD_t, struct ViceInodeInfo *, char *,
                                    char *),
                   FD_t fp,
-                  int (*judgeFun) (struct ViceInodeInfo *, afs_uint32, void *),
-                  int singleVolumeNumber,
+                  int (*judgeFun) (struct ViceInodeInfo *, VolumeId, void *),
+                  VolumeId singleVolumeNumber,
                   void *rock)
 {
     int ret = 0;
@@ -2017,7 +2090,7 @@ _namei_examine_reg(char * path3,
     int dirl; /* Windows-only (one level hash dir) */
 #endif
 
-    if (DecodeInode(path3, dname, &info, myIH->ih_vid) < 0) {
+    if (DecodeInode(path3, dname, &info, myIH) < 0) {
        goto error;
     }
 
@@ -2078,8 +2151,8 @@ struct listsubdirs_work_node {
     int (*writeFun) (FD_t, struct ViceInodeInfo *, char *, char *);
 
     /** inode filter function */
-    int (*judgeFun) (struct ViceInodeInfo *, afs_uint32, void *);
-    int singleVolumeNumber;             /**< volume id filter */
+    int (*judgeFun) (struct ViceInodeInfo *, VolumeId, void *);
+    VolumeId singleVolumeNumber;             /**< volume id filter */
     void * rock;                        /**< pointer passed to writeFun and judgeFun */
     int code;                           /**< return code from examine function */
     int special;                        /**< asserted when this is a volume
@@ -2248,7 +2321,7 @@ static pthread_key_t wq_key;
 static void
 _namei_wq_keycreate(void)
 {
-    osi_Assert(pthread_key_create(&wq_key, NULL) == 0);
+    opr_Verify(pthread_key_create(&wq_key, NULL) == 0);
 }
 
 /**
@@ -2262,9 +2335,9 @@ _namei_wq_keycreate(void)
 void
 namei_SetWorkQueue(struct afs_work_queue *wq)
 {
-    osi_Assert(pthread_once(&wq_once, _namei_wq_keycreate) == 0);
+    opr_Verify(pthread_once(&wq_once, _namei_wq_keycreate) == 0);
 
-    osi_Assert(pthread_setspecific(wq_key, wq) == 0);
+    opr_Verify(pthread_setspecific(wq_key, wq) == 0);
 }
 
 /**
@@ -2405,8 +2478,8 @@ namei_ListAFSSubDirs(IHandle_t * dirIH,
                     int (*writeFun) (FD_t, struct ViceInodeInfo *, char *,
                                      char *),
                     FD_t fp,
-                    int (*judgeFun) (struct ViceInodeInfo *, afs_uint32, void *),
-                    afs_uint32 singleVolumeNumber, void *rock)
+                    int (*judgeFun) (struct ViceInodeInfo *, VolumeId, void *),
+                    VolumeId singleVolumeNumber, void *rock)
 {
     int code = 0, ret = 0;
     IHandle_t myIH = *dirIH;
@@ -2440,7 +2513,7 @@ namei_ListAFSSubDirs(IHandle_t * dirIH,
 
     linkHandle.fd_fd = INVALID_FD;
 #ifdef AFS_SALSRV_ENV
-    osi_Assert(pthread_once(&wq_once, _namei_wq_keycreate) == 0);
+    opr_Verify(pthread_once(&wq_once, _namei_wq_keycreate) == 0);
 
     wq = pthread_getspecific(wq_key);
     if (!wq) {
@@ -2510,8 +2583,8 @@ namei_ListAFSSubDirs(IHandle_t * dirIH,
 #endif
 
     if (linkHandle.fd_fd == INVALID_FD) {
-       Log("namei_ListAFSSubDirs: warning: VG %u does not have a link table; "
-           "salvager will recreate it.\n", dirIH->ih_vid);
+       Log("namei_ListAFSSubDirs: warning: VG %" AFS_VOLID_FMT " does not have a link table; "
+           "salvager will recreate it.\n", afs_printable_VolumeId_lu(dirIH->ih_vid));
     }
 
     /* Now run through all the other subdirs */
@@ -2638,7 +2711,7 @@ namei_ListAFSSubDirs(IHandle_t * dirIH,
 
 #ifdef AFS_NT40_ENV
 static int
-DecodeVolumeName(char *name, unsigned int *vid)
+DecodeVolumeName(char *name, VolumeId *vid)
 {
     /* Name begins with "Vol_" and ends with .data.  See nt_HandleToVolDir() */
     char stmp[32];
@@ -2658,7 +2731,7 @@ DecodeVolumeName(char *name, unsigned int *vid)
 }
 #else
 static int
-DecodeVolumeName(char *name, unsigned int *vid)
+DecodeVolumeName(char *name, VolumeId *vid)
 {
     if (strlen(name) < 1)
        return -1;
@@ -2676,7 +2749,7 @@ DecodeVolumeName(char *name, unsigned int *vid)
 #ifdef AFS_NT40_ENV
 static int
 DecodeInode(char *dpath, char *name, struct ViceInodeInfo *info,
-           unsigned int volid)
+           IHandle_t *myIH)
 {
     char fpath[512];
     int tag, vno;
@@ -2686,6 +2759,7 @@ DecodeInode(char *dpath, char *name, struct ViceInodeInfo *info,
     char stmp[16];
     FdHandle_t linkHandle;
     char dirl;
+    VolumeId volid = myIH->ih_vid;
 
     snprintf(fpath, sizeof(fpath), "%s" OS_DIRSEP "%s", dpath, name);
 
@@ -2752,12 +2826,16 @@ DecodeInode(char *dpath, char *name, struct ViceInodeInfo *info,
 #else
 static int
 DecodeInode(char *dpath, char *name, struct ViceInodeInfo *info,
-           unsigned int volid)
+           IHandle_t *myIH)
 {
     char fpath[512];
     struct afs_stat_st status;
+    struct afs_stat_st checkstatus;
     int parm, tag;
     lb64_string_t check;
+    VolumeId volid = myIH->ih_vid;
+    IHandle_t tmpih;
+    namei_t nameiname;
 
     snprintf(fpath, sizeof(fpath), "%s" OS_DIRSEP "%s", dpath, name);
 
@@ -2772,6 +2850,36 @@ DecodeInode(char *dpath, char *name, struct ViceInodeInfo *info,
     if (strcmp(name, check))
        return -1;
 
+    /* Check if the _full_ path is correct, to ensure we can actually open this
+     * file later. Otherwise, the salvager can choke. */
+    memset(&tmpih, 0, sizeof(tmpih));
+    tmpih.ih_dev = myIH->ih_dev;
+    tmpih.ih_vid = myIH->ih_vid;
+    tmpih.ih_ino = info->inodeNumber;
+    namei_HandleToName(&nameiname, &tmpih);
+    if ((afs_stat(nameiname.n_path, &checkstatus) < 0) ||
+        checkstatus.st_ino != status.st_ino ||
+        checkstatus.st_size != status.st_size) {
+       static int logged;
+       /* log something for this case, since this means the filename looks
+        * like a valid inode, but it's just in the wrong place. That's pretty
+        * strange. */
+       if (!logged) {
+           logged = 1;
+           Log("Note:\n");
+           Log("  Seemingly-misplaced files have been found, which I am\n");
+           Log("  ignoring for now. If you cannot salvage the relevant volume,\n");
+           Log("  you may try manually moving them to their correct location.\n");
+           Log("  If the relevant volume seems fine, and these files do not\n");
+           Log("  appear to contain important data, you can probably manually\n");
+           Log("  delete them, or leave them alone. Contact your local OpenAFS\n");
+           Log("  expert if you are unsure.\n");
+       }
+       Log("Ignoring misplaced file in volume group %u: %s (should be %s)\n",
+           (unsigned)myIH->ih_vid, fpath, nameiname.n_path);
+       return -1;
+    }
+
     if ((info->inodeNumber & NAMEI_INODESPECIAL) == NAMEI_INODESPECIAL) {
        parm = ((info->inodeNumber >> NAMEI_UNIQSHIFT) & NAMEI_UNIQMASK);
        tag = (int)((info->inodeNumber >> NAMEI_TAGSHIFT) & NAMEI_TAGMASK);
@@ -2801,15 +2909,15 @@ DecodeInode(char *dpath, char *name, struct ViceInodeInfo *info,
 
 #ifdef FSSYNC_BUILD_CLIENT
 static afs_int32
-convertVolumeInfo(FD_t fdr, FD_t fdw, afs_uint32 vid)
+convertVolumeInfo(FD_t fdr, FD_t fdw, VolumeId vid)
 {
     struct VolumeDiskData vd;
     char *p;
 
     if (OS_READ(fdr, &vd, sizeof(struct VolumeDiskData)) !=
        sizeof(struct VolumeDiskData)) {
-       Log("1 convertVolumeInfo: read failed for %lu with code %d\n",
-           afs_printable_uint32_lu(vid),
+       Log("1 convertVolumeInfo: read failed for %" AFS_VOLID_FMT " with code %d\n",
+           afs_printable_VolumeId_lu(vid),
            errno);
        return -1;
     }
@@ -2871,7 +2979,7 @@ convertVolumeInfo(FD_t fdr, FD_t fdw, afs_uint32 vid)
  */
 
 int
-namei_ConvertROtoRWvolume(char *pname, afs_uint32 volumeId)
+namei_ConvertROtoRWvolume(char *pname, VolumeId volumeId)
 {
     int code = 0;
 #ifdef FSSYNC_BUILD_CLIENT
@@ -2894,6 +3002,8 @@ namei_ConvertROtoRWvolume(char *pname, afs_uint32 volumeId)
     struct DiskPartition64 *partP;
     struct ViceInodeInfo info;
     struct VolumeDiskHeader h;
+    char *rwpart, *rwname;
+    Error ec;
 # ifdef AFS_DEMAND_ATTACH_FS
     int locktype = 0;
 # endif /* AFS_DEMAND_ATTACH_FS */
@@ -2923,6 +3033,19 @@ namei_ConvertROtoRWvolume(char *pname, afs_uint32 volumeId)
        goto done;
     }
 
+    /* check for existing RW on any partition on this server; */
+    /* if found, return EXDEV - invalid cross-device link */
+    VOL_LOCK;
+    VGetVolumePath(&ec, h.parent, &rwpart, &rwname);
+    if (ec == 0) {
+       Log("1 namei_ConvertROtoRWvolume: RW volume %lu already exists on server partition %s.\n",
+           afs_printable_uint32_lu(h.parent), rwpart);
+       code = EXDEV;
+       VOL_UNLOCK;
+       goto done;
+    }
+    VOL_UNLOCK;
+
     FSYNC_VolOp(volumeId, pname, FSYNC_VOL_BREAKCBKS, 0, NULL);
 
     ino = namei_MakeSpecIno(h.parent, VI_LINKTABLE);
@@ -2945,7 +3068,7 @@ namei_ConvertROtoRWvolume(char *pname, afs_uint32 volumeId)
        if (*dp->d_name == '.')
            continue;
 #endif
-       if (DecodeInode(dir_name, dp->d_name, &info, ih->ih_vid) < 0) {
+       if (DecodeInode(dir_name, dp->d_name, &info, ih) < 0) {
            Log("1 namei_ConvertROtoRWvolume: DecodeInode failed for %s" OS_DIRSEP "%s\n",
                dir_name, dp->d_name);
            closedir(dirp);
@@ -2983,7 +3106,7 @@ namei_ConvertROtoRWvolume(char *pname, afs_uint32 volumeId)
            largeSeen = 1;
        } else {
            closedir(dirp);
-           Log("1 namei_ConvertROtoRWvolume: unknown type %d of special file found : %s" OS_DIRSEP "%s\n", info.u.param[2], dir_name, dp->d_name);
+           Log("1 namei_ConvertROtoRWvolume: unknown type %u of special file found : %s" OS_DIRSEP "%s\n", info.u.param[2], dir_name, dp->d_name);
            code = -1;
            goto done;
        }
@@ -3049,7 +3172,11 @@ namei_ConvertROtoRWvolume(char *pname, afs_uint32 volumeId)
 #ifdef AFS_NT40_ENV
     MoveFileEx(n.n_path, newpath, MOVEFILE_WRITE_THROUGH);
 #else
-    link(newpath, n.n_path);
+    if (link(newpath, n.n_path)) {
+       Log("1 namei_ConvertROtoRWvolume: could not move SmallIndex file: %s\n", n.n_path);
+       code = -1;
+       goto done;
+    }
     OS_UNLINK(newpath);
 #endif
 
@@ -3068,7 +3195,11 @@ namei_ConvertROtoRWvolume(char *pname, afs_uint32 volumeId)
 #ifdef AFS_NT40_ENV
     MoveFileEx(n.n_path, newpath, MOVEFILE_WRITE_THROUGH);
 #else
-    link(newpath, n.n_path);
+    if (link(newpath, n.n_path)) {
+       Log("1 namei_ConvertROtoRWvolume: could not move LargeIndex file: %s\n", n.n_path);
+       code = -1;
+       goto done;
+    }
     OS_UNLINK(newpath);
 #endif
 
@@ -3139,7 +3270,7 @@ static zlcList_t *zlcCur = NULL;
 static void
 AddToZLCDeleteList(char dir, char *name)
 {
-    osi_Assert(strlen(name) <= MAX_ZLC_NAMELEN - 3);
+    opr_Assert(strlen(name) <= MAX_ZLC_NAMELEN - 3);
 
     if (!zlcCur || zlcCur->zlc_n >= MAX_ZLC_NAMES) {
        if (zlcCur && zlcCur->zlc_next)
@@ -3206,4 +3337,37 @@ FreeZLCList(void)
 }
 #endif
 
+/**
+ * Remove empty directories associated with the volume received
+ * as an argument.
+ *
+ * @param[in] pname    vice partition path
+ * @param[in] vid      volume id
+ *
+ * @return 0 on success
+ */
+int
+namei_RemoveDirectories(char *pname, afs_int32 vid)
+{
+    IHandle_t dirIH;
+    namei_t name;
+
+    memset(&dirIH, 0, sizeof(dirIH));
+
+    dirIH.ih_vid = vid;
+
+#ifdef AFS_NT40_ENV
+    dirIH.ih_dev = nt_DriveToDev(pname);
+#else
+    dirIH.ih_dev = volutil_GetPartitionID(pname);
+#endif
+    if (dirIH.ih_dev == -1) {
+       return -1;
+    }
+
+    namei_HandleToVolDir(&name, &dirIH);
+
+    return namei_RemoveDataDirectories(&name);
+}
+
 #endif /* AFS_NAMEI_ENV */