Exemple #1
0
/**
 * create and write a volume disk header to disk.
 *
 * @param[in] hdr   volume disk header
 * @param[in] dp    disk partition object
 *
 * @return operation status
 *    @retval 0 success
 *    @retval EEXIST volume header already exists
 *    @retval EIO failed to write volume header
 *
 * @internal
 */
afs_int32
VCreateVolumeDiskHeader(VolumeDiskHeader_t * hdr,
			struct DiskPartition64 * dp)
{
    afs_int32 code = 0;
#ifdef AFS_DEMAND_ATTACH_FS
    SYNC_response res;
#endif /* AFS_DEMAND_ATTACH_FS */

    code = _VWriteVolumeDiskHeader(hdr, dp, O_CREAT | O_EXCL);
    if (code) {
	goto done;
    }

#ifdef AFS_DEMAND_ATTACH_FS
    memset(&res, 0, sizeof(res));
    code = FSYNC_VGCAdd(dp->name, hdr->parent, hdr->id, FSYNC_WHATEVER, &res);
    if (code) {
	Log("VCreateVolumeDiskHeader: FSYNC_VGCAdd(%s, %lu, %lu) failed "
	    "with code %ld reason %ld\n", dp->name,
	    afs_printable_uint32_lu(hdr->parent),
	    afs_printable_uint32_lu(hdr->id),
	    afs_printable_int32_ld(code),
	    afs_printable_int32_ld(res.hdr.reason));
    }
#endif /* AFS_DEMAND_ATTACH_FS */

 done:
    return code;
}
Exemple #2
0
/**
 * read an existing volume disk header.
 *
 * @param[in]  volid  volume id
 * @param[in]  dp     disk partition object
 * @param[out] hdr    volume disk header or NULL
 *
 * @note if hdr is NULL, this is essentially an existence test for the vol
 *       header
 *
 * @return operation status
 *    @retval 0 success
 *    @retval -1 volume header doesn't exist
 *    @retval EIO failed to read volume header
 *
 * @internal
 */
afs_int32
VReadVolumeDiskHeader(VolumeId volid,
		      struct DiskPartition64 * dp,
		      VolumeDiskHeader_t * hdr)
{
    afs_int32 code = 0;
    int fd;
    char path[MAXPATHLEN];

    snprintf(path, sizeof(path), "%s" OS_DIRSEP VFORMAT,
	     VPartitionPath(dp), afs_printable_uint32_lu(volid));
    fd = open(path, O_RDONLY);
    if (fd < 0) {
	Log("VReadVolumeDiskHeader: Couldn't open header for volume %lu (errno %d).\n",
	    afs_printable_uint32_lu(volid), errno);
	code = -1;

    } else if (hdr && read(fd, hdr, sizeof(*hdr)) != sizeof(*hdr)) {
	Log("VReadVolumeDiskHeader: Couldn't read header for volume %lu.\n",
	    afs_printable_uint32_lu(volid));
	code = EIO;
    }

    if (fd >= 0) {
	close(fd);
    }
    return code;
}
Exemple #3
0
int
afscp_GetStatus(const struct afscp_venusfid *fid, struct AFSFetchStatus *s)
{
    struct afscp_volume *v;
    struct afscp_server *server;
    struct AFSCallBack cb;
    struct AFSVolSync vs;
    struct AFSFid tf = fid->fid;
    struct afscp_statent *stored, key;
    void *cached;
    int code, i, j;
    time_t now;

    v = afscp_VolumeById(fid->cell, fid->fid.Volume);
    if (v == NULL) {
	return -1;
    }
    memset(&key, 0, sizeof(key));
    memcpy(&key.me, fid, sizeof(*fid));

    cached = tfind(&key, &v->statcache, statcompare);
    if (cached != NULL) {
	stored = *(struct afscp_statent **)cached;
	pthread_mutex_lock(&(stored->mtx));
	memmove(s, &stored->status, sizeof(*s));
	afs_dprintf(("Stat %u.%lu.%lu.%lu returning cached result\n",
		     fid->cell->id, fid->fid.Volume, fid->fid.Vnode,
		     fid->fid.Unique));
	if (stored->nwaiters)
	    pthread_cond_broadcast(&(stored->cv));
	pthread_mutex_unlock(&(stored->mtx));
	return 0;
    }

    code = ENOENT;
    for (i = 0; i < v->nservers; i++) {
	server = afscp_ServerByIndex(v->servers[i]);
	if (server && server->naddrs > 0) {
	    for (j = 0; j < server->naddrs; j++) {
		time(&now);
		code = RXAFS_FetchStatus(server->conns[j], &tf, s, &cb, &vs);
		if (code == 0) {
		    afscp_AddCallBack(server, &fid->fid, s, &cb, now);	/* calls _StatStuff */
		    afs_dprintf(("Stat %d.%lu.%lu.%lu"
				 " ok: type %ld size %ld\n",
				 fid->cell->id,
				 afs_printable_uint32_lu(fid->fid.Volume),
				 afs_printable_uint32_lu(fid->fid.Vnode),
				 afs_printable_uint32_lu(fid->fid.Unique),
				 afs_printable_int32_ld(s->FileType),
				 afs_printable_int32_ld(s->Length)));
		    return 0;
		}
	    }
	}
    }
    afscp_errno = code;
    return -1;
}
Exemple #4
0
/**
 * destroy a volume disk header.
 *
 * @param[in] dp      disk partition object
 * @param[in] volid   volume id
 * @param[in] parent  parent's volume id, 0 if unknown
 *
 * @return operation status
 *    @retval 0 success
 *
 * @note if parent is 0, the parent volume ID will be looked up from the
 * fileserver
 *
 * @note for non-DAFS, parent is currently ignored
 */
afs_int32
VDestroyVolumeDiskHeader(struct DiskPartition64 * dp,
			 VolumeId volid,
			 VolumeId parent)
{
    afs_int32 code = 0;
    char path[MAXPATHLEN];
#ifdef AFS_DEMAND_ATTACH_FS
    SYNC_response res;
#endif /* AFS_DEMAND_ATTACH_FS */

    (void)afs_snprintf(path, sizeof(path),
                       "%s/" VFORMAT,
                       VPartitionPath(dp), afs_printable_uint32_lu(volid));
    code = unlink(path);
    if (code) {
	Log("VDestroyVolumeDiskHeader: Couldn't unlink disk header, error = %d\n", errno);
	goto done;
    }

#ifdef AFS_DEMAND_ATTACH_FS
    memset(&res, 0, sizeof(res));
    if (!parent) {
	FSSYNC_VGQry_response_t q_res;

	code = FSYNC_VGCQuery(dp->name, volid, &q_res, &res);
	if (code) {
	    Log("VDestroyVolumeDiskHeader: FSYNC_VGCQuery(%s, %lu) failed "
	        "with code %ld, reason %ld\n", dp->name,
	        afs_printable_uint32_lu(volid), afs_printable_int32_ld(code),
		afs_printable_int32_ld(res.hdr.reason));
	    goto done;
	}

	parent = q_res.rw;

    }
    code = FSYNC_VGCDel(dp->name, parent, volid, FSYNC_WHATEVER, &res);
    if (code) {
	Log("VDestroyVolumeDiskHeader: FSYNC_VGCDel(%s, %lu, %lu) failed "
	    "with code %ld reason %ld\n", dp->name,
	    afs_printable_uint32_lu(parent),
	    afs_printable_uint32_lu(volid),
	    afs_printable_int32_ld(code),
	    afs_printable_int32_ld(res.hdr.reason));
    }
#endif /* AFS_DEMAND_ATTACH_FS */

 done:
    return code;
}
Exemple #5
0
/**
 * add an entry to the hash table.
 *
 * @param[in]  dp        disk partition object
 * @param[in]  volid     volume id
 * @param[in]  ent       volume group object
 * @param[out] hash_out  address in which to store pointer to hash entry
 *
 * @pre VOL_LOCK held
 *
 * @return operation status
 *    @retval 0 success
 *    @retval EEXIST hash entry for volid already exists, and it points to
 *                   a different VG entry
 *
 * @internal
 */
static int
_VVGC_hash_entry_add(struct DiskPartition64 * dp,
		     VolumeId volid,
		     VVGCache_entry_t * ent,
		     VVGCache_hash_entry_t ** hash_out)
{
    int code = 0;
    VVGCache_hash_entry_t * hent;
    int hash = VVGC_HASH(volid);
    VVGCache_entry_t *nent;

    code = _VVGC_lookup(dp, volid, &nent, hash_out);
    if (!code) {
	if (ent != nent) {
	    ViceLog(0, ("_VVGC_hash_entry_add: tried to add a duplicate "
	                " nonmatching entry for vol %lu: original "
	                "(%"AFS_PTR_FMT",%lu) new (%"AFS_PTR_FMT",%lu)\n",
	                afs_printable_uint32_lu(volid),
	                nent, afs_printable_uint32_lu(nent->rw),
	                ent, afs_printable_uint32_lu(ent->rw)));
	    return EEXIST;
	}
	ViceLog(1, ("_VVGC_hash_entry_add: tried to add duplicate "
	              "hash entry for vol %lu, VG %lu",
	              afs_printable_uint32_lu(volid),
	              afs_printable_uint32_lu(ent->rw)));
	/* accept attempts to add matching duplicate entries; just
	 * pretend we added it */
	return 0;
    }

    code = _VVGC_hash_entry_alloc(&hent);
    if (code) {
	goto done;
    }

    hent->entry = ent;
    hent->dp    = dp;
    hent->volid = volid;
    queue_Append(&VVGCache_hash_table.hash_buckets[hash],
		 hent);

 done:
    if (hash_out) {
	*hash_out = hent;
    }
    return code;
}
Exemple #6
0
/**
 * flush all cache entries for a given disk partition.
 *
 * @param[in] part  disk partition object
 *
 * @pre VOL_LOCK held
 *
 * @return operation status
 *    @retval 0 success
 *
 * @internal
 */
int
_VVGC_flush_part_r(struct DiskPartition64 * part)
{
    int code = 0, res;
    int i;
    VVGCache_hash_entry_t * ent, * nent;

    for (i = 0; i < VolumeHashTable.Size; i++) {
	for (queue_Scan(&VVGCache_hash_table.hash_buckets[i],
			ent,
			nent,
			VVGCache_hash_entry)) {
	    if (ent->dp == part) {
		VolumeId volid = ent->volid;
		res = _VVGC_hash_entry_del(ent);
		if (res) {
		    ViceLog(0, ("_VVGC_flush_part_r: error %d deleting hash entry for %lu\n",
		        res, afs_printable_uint32_lu(volid)));
		    code = res;
		}
	    }
	}
    }

    return code;
}
Exemple #7
0
/**
 * write an existing volume disk header.
 *
 * @param[in] hdr   volume disk header
 * @param[in] dp    disk partition object
 * @param[in] cr    assert if O_CREAT | O_EXCL should be passed to open()
 *
 * @return operation status
 *    @retval 0 success
 *    @retval -1 volume header doesn't exist
 *    @retval EIO failed to write volume header
 *
 * @internal
 */
static afs_int32
_VWriteVolumeDiskHeader(VolumeDiskHeader_t * hdr,
			struct DiskPartition64 * dp,
			int flags)
{
    afs_int32 code = 0;
    int fd;
    char path[MAXPATHLEN];

#ifdef AFS_DEMAND_ATTACH_FS
    /* prevent racing with VGC scanners reading the vol header while we are
     * writing it */
    code = VPartHeaderLock(dp, READ_LOCK);
    if (code) {
	return EIO;
    }
#endif /* AFS_DEMAND_ATTACH_FS */

    flags |= O_RDWR;

    (void)afs_snprintf(path, sizeof(path),
		       "%s/" VFORMAT,
		       VPartitionPath(dp), afs_printable_uint32_lu(hdr->id));
    fd = open(path, flags, 0644);
    if (fd < 0) {
	code = errno;
	Log("_VWriteVolumeDiskHeader: Couldn't open header for volume %lu, "
	    "error = %d\n", afs_printable_uint32_lu(hdr->id), errno);
    } else if (write(fd, hdr, sizeof(*hdr)) != sizeof(*hdr)) {
	Log("_VWriteVolumeDiskHeader: Couldn't write header for volume %lu, "
	    "error = %d\n", afs_printable_uint32_lu(hdr->id), errno);
	code = EIO;
    }

    if (fd >= 0) {
	if (close(fd) != 0) {
	    Log("_VWriteVolumeDiskHeader: Error closing header for volume "
	        "%lu, errno %d\n", afs_printable_uint32_lu(hdr->id), errno);
	}
    }

#ifdef AFS_DEMAND_ATTACH_FS
    VPartHeaderUnlock(dp, READ_LOCK);
#endif /* AFS_DEMAND_ATTACH_FS */

    return code;
}
Exemple #8
0
static int
VolOpQuery(struct cmd_syndesc * as, void * rock)
{
    struct fssync_state state;
    SYNC_PROTO_BUF_DECL(res_buf);
    SYNC_response res;
    FSSYNC_VolOp_info vop;

    res.hdr.response_len = sizeof(res.hdr);
    res.payload.buf = res_buf;
    res.payload.len = SYNC_PROTO_MAX_LEN;

    common_prolog(as, &state);
    common_volop_prolog(as, &state);

    do_volop(&state, FSYNC_VOL_QUERY_VOP, &res);

    if (!(res.hdr.flags & SYNC_FLAG_DAFS_EXTENSIONS)) {
	printf("*** file server not compiled with demand attach extensions.\n");
	printf("*** pending volume operation metadata not available.\n");
    }

    if (res.hdr.response == SYNC_OK) {
	memcpy(&vop, res.payload.buf, sizeof(FSSYNC_VolOp_info));

	printf("pending_vol_op = {\n");

	printf("\tcom = {\n");
	printf("\t\tproto_version  = %u\n", vop.com.proto_version);
	printf("\t\tpkt_seq        = %u\n", vop.com.pkt_seq);
	printf("\t\tcom_seq        = %u\n", vop.com.com_seq);
	printf("\t\tprogramType    = %d (%s)\n",
	       vop.com.programType, VPTypeToString(vop.com.programType));
	printf("\t\tpid            = %d\n", vop.com.pid);
	printf("\t\ttid            = %d\n", vop.com.tid);
	printf("\t\tcommand        = %d (%s)\n",
	       vop.com.command, FSYNC_com2string(vop.com.command));
	printf("\t\treason         = %d (%s)\n",
	       vop.com.reason, FSYNC_reason2string(vop.com.reason));
	printf("\t\tcommand_len    = %u\n", vop.com.command_len);
	printf("\t\tflags          = 0x%lux\n", afs_printable_uint32_lu(vop.com.flags));
	printf("\t}\n");

	printf("\tvop = {\n");
	printf("\t\tvolume         = %" AFS_VOLID_FMT "\n", afs_printable_VolumeId_lu(vop.vop.volume));
	if (strnlen(vop.vop.partName, sizeof(vop.vop.partName)) <
	    sizeof(vop.vop.partName)) {
	    printf("\t\tpartName       = '%s'\n", vop.vop.partName);
	} else {
	    printf("\t\tpartName       = (illegal string)\n");
	}
	printf("\t}\n");

	printf("}\n");
    }

    return 0;
}
Exemple #9
0
/**
 * put back a reference to an entry.
 *
 * @param[in] dp      disk partition object
 * @param[in] entry   cache entry
 *
 * @pre VOL_LOCK held
 *
 * @warning do not attempt to deref pointer after calling this interface
 *
 * @return operation status
 *    @retval 0 success
 *
 * @note dp is needed to lookup the RW hash entry to unlink, if we are
 *       putting back the final reference and freeing
 *
 * @internal
 */
static int
_VVGC_entry_put(struct DiskPartition64 * dp, VVGCache_entry_t * entry)
{
    int code = 0;

    osi_Assert(entry->refcnt > 0);

    if (--entry->refcnt == 0) {
	VVGCache_entry_t *nentry;
	VVGCache_hash_entry_t *hentry;

	/* first, try to delete the RW id hash entry pointing to this
	 * entry */
	code = _VVGC_lookup(dp, entry->rw, &nentry, &hentry);
	if (!code) {
	    if (nentry != entry) {
		/* looking up the rw of this entry points to a different
		 * entry; should not happen */
		ViceLog(0, ("VVGC_entry_put: error: entry lookup for entry %lu "
		    "found different entry than was passed",
		    afs_printable_uint32_lu(entry->rw)));
		code = -1;
	    } else {
		code = _VVGC_hash_entry_unlink(hentry);
		hentry = NULL;
	    }
	} else if (code == ENOENT) {
	    /* ignore ENOENT; this shouldn't happen, since the RW hash
	     * entry should always exist if the entry does... but we
	     * were going to delete it anyway, so try to continue */
	    ViceLog(0, ("VVGC_entry_put: warning: tried to unlink entry for "
	        "vol %lu, but RW hash entry doesn't exist; continuing "
		"anyway...\n", afs_printable_uint32_lu(entry->rw)));

	    code = 0;
	}

	/* now, just free the entry itself */
	if (!code) {
	    code = _VVGC_entry_free(entry);
	}
    }

    return code;
}
Exemple #10
0
/**
 * lock an offset in a file descriptor.
 *
 * @param[in] fd       file descriptor to lock
 * @param[in] offset   offset in file to lock
 * @param[in] locktype READ_LOCK or WRITE_LOCK
 * @param[in] nonblock 1 to fail immediately, 0 to wait to acquire lock
 *
 * @return operation status
 *  @retval 0 success
 *  @retval EBUSY someone else is holding a conflicting lock and nonblock=1 was
 *                specified
 *  @retval EIO   error acquiring file lock
 */
static_inline int
_VLockFd(FD_t fd, afs_uint32 offset, int locktype, int nonblock)
{
    int l_type = F_WRLCK;
    int cmd = AFS_SETLKW;
    struct afs_st_flock sf;

    if (locktype == READ_LOCK) {
	l_type = F_RDLCK;
    }
    if (nonblock) {
	cmd = AFS_SETLK;
    }

    sf.l_start = offset;
    sf.l_len = 1;
    sf.l_type = l_type;
    sf.l_whence = SEEK_SET;

    if (fcntl(fd, cmd, &sf)) {
	if (nonblock && (errno == EACCES || errno == EAGAIN)) {
	    /* We asked for a nonblocking lock, and it was already locked */
	    sf.l_pid = 0;
	    if (fcntl(fd, F_GETLK, &sf) != 0 || sf.l_pid == 0) {
		Log("_VLockFd: fcntl failed with error %d when trying to "
		    "query the conflicting lock for fd %d (locktype=%d, "
		    "offset=%lu)\n", errno, fd, locktype,
		    afs_printable_uint32_lu(offset));
	    } else {
		Log("_VLockFd: conflicting lock held on fd %d, offset %lu by "
		    "pid %ld (locktype=%d)\n", fd,
		    afs_printable_uint32_lu(offset), (long int)sf.l_pid,
		    locktype);
	    }
	    return EBUSY;
	}
	Log("_VLockFd: fcntl failed with error %d when trying to lock "
	    "fd %d (locktype=%d, offset=%lu)\n", errno, fd, locktype,
	    afs_printable_uint32_lu(offset));
	return EIO;
    }

    return 0;
}
Exemple #11
0
/**
 * add a volid to the entry's child list.
 *
 * @param[in]   ent     volume group object
 * @param[in]   volid   volume id
 *
 * @return operation status
 *    @retval 0 success
 *    @retval -1 child table is full
 *
 * @internal
 */
static int
_VVGC_entry_cl_add(VVGCache_entry_t * ent,
		   VolumeId volid)
{
    int code = 0, i;
    int empty_idx = -1;

    /* search table to avoid duplicates */
    for (i = 0; i < VOL_VG_MAX_VOLS; i++) {
	if (ent->children[i] == volid) {
	    ViceLog(1, ("VVGC_entry_cl_add: tried to add duplicate vol "
	               "%lu to VG %lu\n",
		       afs_printable_uint32_lu(volid),
		       afs_printable_uint32_lu(ent->rw)));
	    goto done;
	}
	if (empty_idx == -1 && !ent->children[i]) {
	    empty_idx = i;
	    /* don't break; make sure we go through all children so we don't
	     * add a duplicate entry */
	}
    }

    /* verify table isn't full */
    if (empty_idx == -1) {
	code = -1;
	ViceLog(0, ("VVGC_entry_cl_add: tried to add vol %lu to VG %lu, but VG "
	    "is full\n", afs_printable_uint32_lu(volid),
	    afs_printable_uint32_lu(ent->rw)));
	goto done;
    }

    /* add entry */
    ent->children[empty_idx] = volid;

    /* inc refcount */
    code = _VVGC_entry_get(ent);

 done:
    return code;
}
Exemple #12
0
/**
 * delete an entry from the volume group cache.
 *
 * @param[in] dp       disk partition object
 * @param[in] parent   parent volume id
 * @param[in] child    child volume id
 *
 * @pre VOL_LOCK held
 *
 * @internal
 *
 * @return operation status
 *    @retval 0 success
 */
int
_VVGC_entry_purge_r(struct DiskPartition64 * dp,
                    VolumeId parent, VolumeId child)
{
    int code = 0, res;
    VVGCache_entry_t * parent_ent, * child_ent;
    VVGCache_hash_entry_t * child_hent;

    /* check mappings for each volid */
    res = _VVGC_lookup(dp, parent, &parent_ent, NULL);
    if (res) {
	code = res;
	goto done;
    }
    res = _VVGC_lookup(dp, child, &child_ent, &child_hent);
    if (res) {
	code = res;
	goto done;
    }

    /* if the mappings don't match, we have a serious error */
    if (parent_ent != child_ent) {
	ViceLog(0, ("VVGCache_entry_del: trying to delete vol %lu from VG %lu, "
	    "but vol %lu points to VGC entry %"AFS_PTR_FMT" and VG %lu "
	    "points to VGC entry %"AFS_PTR_FMT"\n",
	    afs_printable_uint32_lu(child),
	    afs_printable_uint32_lu(parent),
	    afs_printable_uint32_lu(child),
	    child_ent, afs_printable_uint32_lu(parent), parent_ent));
	code = -1;
	goto done;
    }

    code = _VVGC_hash_entry_del(child_hent);

 done:
    return code;
}
Exemple #13
0
static int
handleit(struct cmd_syndesc *as, void *arock)
{
    struct cmd_item *ti;
    int err = 0;
    afs_uint32 volumeId = 0;
    char *partName = 0;
    struct DiskPartition64 *partP = NULL;


#ifndef AFS_NT40_ENV
    if (geteuid() != 0) {
	printf("vol-info must be run as root; sorry\n");
	exit(1);
    }
#endif

    if (as->parms[0].items)
	online = 1;
    else
	online = 0;
    if (as->parms[1].items)
	DumpVnodes = 1;
    else
	DumpVnodes = 0;
    if (as->parms[2].items)
	DumpDate = 1;
    else
	DumpDate = 0;
    if (as->parms[3].items)
	DumpInodeNumber = 1;
    else
	DumpInodeNumber = 0;
    if (as->parms[4].items)
	InodeTimes = 1;
    else
	InodeTimes = 0;
    if ((ti = as->parms[5].items))
	partName = ti->data;
    if ((ti = as->parms[6].items))
	volumeId = strtoul(ti->data, NULL, 10);
    if (as->parms[7].items)
	dheader = 1;
    else
	dheader = 0;
    if (as->parms[8].items) {
	dsizeOnly = 1;
	dheader = 1;
	DumpVnodes = 1;
    } else
	dsizeOnly = 0;
    if (as->parms[9].items) {
	fixheader = 1;
    } else
	fixheader = 0;
    if (as->parms[10].items) {
	saveinodes = 1;
	dheader = 1;
	DumpVnodes = 1;
    } else
	saveinodes = 0;
    if (as->parms[11].items) {
	orphaned = 1;
	DumpVnodes = 1;
    } else
#if defined(AFS_NAMEI_ENV)
    if (as->parms[12].items) {
	PrintFileNames = 1;
	DumpVnodes = 1;
    } else
#endif
	orphaned = 0;

    DInit(10);

    err = VAttachPartitions();
    if (err) {
	printf("%d partitions had errors during attach.\n", err);
    }

    if (partName) {
	partP = VGetPartition(partName, 0);
	if (!partP) {
	    printf("%s is not an AFS partition name on this server.\n",
		   partName);
	    exit(1);
	}
    }

    if (!volumeId) {
	if (!partP) {
	    HandleAllPart();
	} else {
	    HandlePart(partP);
	}
    } else {
	char name1[128];

	if (!partP) {
	    partP = FindCurrentPartition();
	    if (!partP) {
		printf("Current partition is not a vice partition.\n");
		exit(1);
	    }
	}
	(void)afs_snprintf(name1, sizeof name1, VFORMAT,
			   afs_printable_uint32_lu(volumeId));
	if (dsizeOnly && !saveinodes)
	    printf
		("Volume-Id\t  Volsize  Auxsize Inodesize  AVolsize SizeDiff                (VolName)\n");
	HandleVolume(partP, name1);
    }
    return 0;
}
Exemple #14
0
/**
 * verify that the fileserver still thinks we have a volume checked out.
 *
 * In DAFS, a non-fileserver program accesses a volume by checking it out from
 * the fileserver (FSYNC_VOL_OFF or FSYNC_VOL_NEEDVOLUME), and then locks the
 * volume. There is a possibility that the fileserver crashes or restarts for
 * some reason between volume checkout and locking; if this happens, the
 * fileserver could attach the volume before we had a chance to lock it. This
 * function serves to detect if this has happened; it must be called after
 * volume checkout and locking to make sure the fileserver still thinks we
 * have the volume. (If it doesn't, we should try to check it out again.)
 *
 * @param[in] volume    volume ID
 * @param[in] partition partition name string
 * @param[in] command   the command that was used to checkout the volume
 * @param[in] reason    the reason code used to checkout the volume
 *
 * @return operation status
 *  @retval SYNC_OK the fileserver could not have attached the volume since
 *                  it was checked out (either it thinks it is still checked
 *                  out, or it doesn't know about the volume)
 *  @retval SYNC_DENIED fileserver may have restarted since checkout; checkout
 *                      should be reattempted
 *  @retval SYNC_COM_ERROR internal/fatal error
 */
afs_int32
FSYNC_VerifyCheckout(VolumeId volume, char * partition,
                     afs_int32 command, afs_int32 reason)
{
    SYNC_response res;
    FSSYNC_VolOp_info vop;
    afs_int32 code;
    afs_int32 pid;

    res.hdr.response_len = sizeof(res.hdr);
    res.payload.buf = &vop;
    res.payload.len = sizeof(vop);

    code = FSYNC_VolOp(volume, partition, FSYNC_VOL_QUERY_VOP, FSYNC_WHATEVER, &res);
    if (code != SYNC_OK) {
        if (res.hdr.reason == FSYNC_NO_PENDING_VOL_OP) {
            Log("FSYNC_VerifyCheckout: fileserver claims no vop for vol %lu "
                "part %s; fileserver may have restarted since checkout\n",
                afs_printable_uint32_lu(volume), partition);
            return SYNC_DENIED;
        }

        if (res.hdr.reason == FSYNC_UNKNOWN_VOLID ||
                res.hdr.reason == FSYNC_WRONG_PART) {
            /* if the fileserver does not know about this volume on this
             * partition, there's no way it could have attached it, so we're
             * fine */
            return SYNC_OK;
        }

        Log("FSYNC_VerifyCheckout: FSYNC_VOL_QUERY_VOP failed for vol %lu "
            "part %s with code %ld reason %ld\n",
            afs_printable_uint32_lu(volume), partition,
            afs_printable_int32_ld(code),
            afs_printable_int32_ld(res.hdr.reason));
        return SYNC_COM_ERROR;
    }

    pid = getpid();

    /* Check if the current vol op is us. Checking pid is probably enough, but
     * be a little bit paranoid. We could also probably check tid, but I'm not
     * completely confident of its reliability on all platforms (on pthread
     * envs, we coerce a pthread_t to an afs_int32, which is not guaranteed
     * to mean anything significant). */

    if (vop.com.programType == programType && vop.com.pid == pid &&
            vop.com.command == command && vop.com.reason == reason) {

        /* looks like the current pending vol op is the same one as the one
         * with which we checked it out. success. */
        return SYNC_OK;
    }

    Log("FSYNC_VerifyCheckout: vop for vol %lu part %s does not match "
        "expectations (got pt %ld pid %ld cmd %ld reason %ld, but expected "
        "pt %ld pid %ld cmd %ld reason %ld); fileserver may have restarted "
        "since checkout\n", afs_printable_uint32_lu(volume), partition,
        afs_printable_int32_ld(vop.com.programType),
        afs_printable_int32_ld(vop.com.pid),
        afs_printable_int32_ld(vop.com.command),
        afs_printable_int32_ld(vop.com.reason),
        afs_printable_int32_ld(programType),
        afs_printable_int32_ld(pid),
        afs_printable_int32_ld(command),
        afs_printable_int32_ld(reason));

    return SYNC_DENIED;
}
Exemple #15
0
struct afscp_volume *
afscp_VolumeByName(struct afscp_cell *cell, const char *vname,
                   afs_int32 intype)
{
    union allvldbentry u;
    struct afscp_volume *ret, key;
    struct afscp_server *server;
    afs_int32 code, vtype, type, srv;
    void *s;
#ifdef AFSCP_DEBUG
    struct in_addr i;
#endif
    if (intype == RWVOL)
        vtype = VLSF_RWVOL;
    else if (intype == ROVOL)
        vtype = VLSF_ROVOL;
    else if (intype == BACKVOL)
        vtype = VLSF_BACKVOL;
    else {
        afscp_errno = EINVAL;
        return NULL;
    }

    memset(&key, 0, sizeof(key));
    strlcpy(key.name, vname, sizeof(key.name));
    key.voltype = vtype;
    s = tfind(&key, &cell->volsbyname, ncompare);
    if (s) {
        ret = *(struct afscp_volume **)s;
        return ret;
    }

    type = 0;
    code = ubik_VL_GetEntryByNameU(cell->vlservers, 0, (char *)vname, &u.u);
    if (code == RXGEN_OPCODE) {
        type = 1;
        code =
            ubik_VL_GetEntryByNameN(cell->vlservers, 0, (char *)vname, &u.n);
        if (code == RXGEN_OPCODE) {
            type = 2;
            code = ubik_VL_GetEntryByNameO(cell->vlservers, 0, (char *)vname,
                                           &u.o);
        }
    }
    if (code != 0) {
        afscp_errno = code;
        return NULL;
    }
    ret = calloc(1, sizeof(struct afscp_volume));
    if (ret == NULL) {
        afscp_errno = ENOMEM;
        return NULL;
    }
    strlcpy(ret->name, u.u.name, sizeof(ret->name));
    ret->nservers = 0;
    ret->cell = cell;
    switch (type) {
    case 0:
        ret->id = u.u.volumeId[intype];
        for (srv = 0; srv < u.u.nServers; srv++) {
            if ((u.u.serverFlags[srv] & vtype) == 0)
                continue;
            afs_dprintf(("uvldbentry server %d flags: %x\n", srv,
                         u.u.serverFlags[srv]));

            if ((u.u.serverFlags[srv] & VLSF_UUID) == 0)
                server =
                    afscp_ServerByAddr(cell, u.u.serverNumber[srv].time_low);
            else
                server = afscp_ServerById(cell, &u.u.serverNumber[srv]);
            if (!server)
                continue;
            ret->servers[ret->nservers++] = server->index;
        }
        break;
    case 1:
        ret->id = u.n.volumeId[intype];
        for (srv = 0; srv < u.n.nServers; srv++) {
            if ((u.n.serverFlags[srv] & vtype) == 0)
                continue;
            server = afscp_ServerByAddr(cell, u.n.serverNumber[srv]);
            if (!server)
                continue;
            ret->servers[ret->nservers++] = server->index;
        }
        break;
    case 2:
        ret->id = u.o.volumeId[intype];
        for (srv = 0; srv < u.o.nServers; srv++) {
            if ((u.o.serverFlags[srv] & vtype) == 0)
                continue;
            server = afscp_ServerByAddr(cell, u.o.serverNumber[srv]);
            if (!server)
                continue;
            ret->servers[ret->nservers++] = server->index;
        }
        break;
    }
    if (!ret->nservers || !ret->id) {
        free(ret);
        return NULL;
    }

    ret->voltype = intype;
#ifdef AFSCP_DEBUG
    server = afscp_ServerByIndex(ret->servers[0]);
    if (server != NULL)
        i.s_addr = server->addrs[0];
    else
        i.s_addr = 0;
#endif
    afs_dprintf(("New volume BYNAME %s (%lu) on %s (%d)\n", ret->name,
                 afs_printable_uint32_lu(ret->id),
                 inet_ntoa(i), ret->servers[0]));
    s = tsearch(&key, &cell->volsbyname, ncompare);
    if (s)
        *(struct afscp_volume **)s = ret;
    key.id = ret->id;
    s = tsearch(&key, &cell->volsbyid, icompare);
    if (s)
        *(struct afscp_volume **)s = ret;
    return ret;
}
Exemple #16
0
struct afscp_volume *
afscp_VolumeById(struct afscp_cell *cell, afs_uint32 id)
{
    union allvldbentry u;
    struct afscp_volume *ret, key;
    struct afscp_server *server;
    afs_int32 code, vtype, type, srv;
    int voltype = -1;
    char idbuffer[16];
    void *s;
#ifdef AFSCP_DEBUG
    struct in_addr i;
#endif

    memset(&key, 0, sizeof(key));
    key.id = id;
    s = tfind(&key, &cell->volsbyid, icompare);
    if (s) {
        ret = *(struct afscp_volume **)s;
        return ret;
    }

    snprintf(idbuffer, sizeof(idbuffer), "%lu", afs_printable_uint32_lu(id));
    type = 0;
    code = ubik_VL_GetEntryByNameU(cell->vlservers, 0, idbuffer, &u.u);
    if (code == RXGEN_OPCODE) {
        type = 1;
        code = ubik_VL_GetEntryByIDN(cell->vlservers, 0, id, -1, &u.n);
        if (code == RXGEN_OPCODE) {
            type = 2;
            code = ubik_VL_GetEntryByID(cell->vlservers, 0, id, -1, &u.o);
        }
    }
    if (code != 0) {
        afscp_errno = code;
        return NULL;
    }
    ret = calloc(1, sizeof(struct afscp_volume));
    if (ret == NULL) {
        afscp_errno = ENOMEM;
        return NULL;
    }
    strlcpy(ret->name, u.u.name, sizeof(ret->name));
    ret->nservers = 0;
    ret->cell = cell;

    switch (type) {
    case 0:
        if (id == u.u.volumeId[RWVOL]) {
            vtype = VLSF_RWVOL;
            voltype = RWVOL;
        } else if (id == u.u.volumeId[ROVOL]) {
            vtype = VLSF_ROVOL;
            voltype = ROVOL;
        } else if (id == u.u.volumeId[BACKVOL]) {
            vtype = VLSF_BACKVOL;
            voltype = BACKVOL;
        } else {
            vtype = 0;
            voltype = -1;
        }
        for (srv = 0; srv < u.u.nServers; srv++) {
            if ((u.u.serverFlags[srv] & vtype) == 0)
                continue;
            if ((u.u.serverFlags[srv] & VLSF_UUID) == 0)
                server =
                    afscp_ServerByAddr(cell, u.u.serverNumber[srv].time_low);
            else
                server = afscp_ServerById(cell, &u.u.serverNumber[srv]);
            if (!server)
                continue;
            ret->servers[ret->nservers++] = server->index;
        }
        break;
    case 1:
        if (id == u.n.volumeId[RWVOL]) {
            vtype = VLSF_RWVOL;
            voltype = RWVOL;
        } else if (id == u.n.volumeId[ROVOL]) {
            vtype = VLSF_ROVOL;
            voltype = ROVOL;
        } else if (id == u.n.volumeId[BACKVOL]) {
            vtype = VLSF_BACKVOL;
            voltype = BACKVOL;
        } else {
            vtype = 0;
            voltype = -1;
        }
        for (srv = 0; srv < u.n.nServers; srv++) {
            if ((u.n.serverFlags[srv] & vtype) == 0)
                continue;
            server = afscp_ServerByAddr(cell, u.n.serverNumber[srv]);
            if (server == NULL)
                continue;
            ret->servers[ret->nservers++] = server->index;
        }
        break;
    case 2:
        if (id == u.o.volumeId[RWVOL]) {
            vtype = VLSF_RWVOL;
            voltype = RWVOL;
        } else if (id == u.o.volumeId[ROVOL]) {
            vtype = VLSF_ROVOL;
            voltype = ROVOL;
        } else if (id == u.o.volumeId[BACKVOL]) {
            vtype = VLSF_BACKVOL;
            voltype = BACKVOL;
        } else {
            vtype = 0;
            voltype = -1;
        }
        for (srv = 0; srv < u.o.nServers; srv++) {
            if ((u.o.serverFlags[srv] & vtype) == 0)
                continue;
            server = afscp_ServerByAddr(cell, u.o.serverNumber[srv]);
            if (server == NULL)
                continue;
            ret->servers[ret->nservers++] = server->index;
        }
        break;
    }
    ret->voltype = voltype;
#ifdef AFSCP_DEBUG
    server = afscp_ServerByIndex(ret->servers[0]);
    if (server)
        i.s_addr = server->addrs[0];
    else
        i.s_addr = 0;
#endif
    afs_dprintf(("New volume BYID %s (%lu) on %s (%d)\n", ret->name,
                 afs_printable_uint32_lu(ret->id), inet_ntoa(i),
                 ret->servers[0]));
    s = tsearch(&key, &cell->volsbyid, icompare);
    if (s)
        *(struct afscp_volume **)s = ret;
    strlcpy(key.name, ret->name, sizeof(key.name));
    s = tsearch(&key, &cell->volsbyname, ncompare);
    if (s)
        *(struct afscp_volume **)s = ret;
    return ret;
}
Exemple #17
0
/**
 * write an existing volume disk header.
 *
 * @param[in] hdr   volume disk header
 * @param[in] dp    disk partition object
 *
 * @return operation status
 *    @retval 0 success
 *    @retval ENOENT volume header doesn't exist
 *    @retval EIO failed to write volume header
 */
afs_int32
VWriteVolumeDiskHeader(VolumeDiskHeader_t * hdr,
		       struct DiskPartition64 * dp)
{
    afs_int32 code;

#ifdef AFS_DEMAND_ATTACH_FS
    VolumeDiskHeader_t oldhdr;
    int delvgc = 0, addvgc = 0;
    SYNC_response res;

    /* first, see if anything with the volume IDs have changed; if so, we
     * need to update the VGC */

    code = VReadVolumeDiskHeader(hdr->id, dp, &oldhdr);
    if (code == 0 && (oldhdr.id != hdr->id || oldhdr.parent != hdr->parent)) {
	/* the vol id or parent vol id changed; need to delete the VGC entry
	 * for the old vol id/parent, and add the new one */
	delvgc = 1;
	addvgc = 1;

    } else if (code) {
	/* couldn't get the old header info; add the new header info to the
	 * VGC in case it hasn't been added yet */
	addvgc = 1;
    }

#endif /* AFS_DEMAND_ATTACH_FS */

    code = _VWriteVolumeDiskHeader(hdr, dp, 0);
    if (code) {
	goto done;
    }

#ifdef AFS_DEMAND_ATTACH_FS
    if (delvgc) {
	memset(&res, 0, sizeof(res));
	code = FSYNC_VGCDel(dp->name, oldhdr.parent, oldhdr.id, FSYNC_WHATEVER, &res);

	/* unknown vol id is okay; it just further suggests the old header
	 * data was bogus, which is fine since we're trying to fix it */
	if (code && res.hdr.reason != FSYNC_UNKNOWN_VOLID) {
	    Log("VWriteVolumeDiskHeader: FSYNC_VGCDel(%s, %lu, %lu) "
	        "failed with code %ld reason %ld\n", dp->name,
	        afs_printable_uint32_lu(oldhdr.parent),
	        afs_printable_uint32_lu(oldhdr.id),
	        afs_printable_int32_ld(code),
	        afs_printable_int32_ld(res.hdr.reason));
	}

    }
    if (addvgc) {
	memset(&res, 0, sizeof(res));
	code = FSYNC_VGCAdd(dp->name, hdr->parent, hdr->id, FSYNC_WHATEVER, &res);
	if (code) {
	    Log("VWriteVolumeDiskHeader: FSYNC_VGCAdd(%s, %lu, %lu) "
	        "failed with code %ld reason %ld\n", dp->name,
	        afs_printable_uint32_lu(hdr->parent),
	        afs_printable_uint32_lu(hdr->id),
	        afs_printable_int32_ld(code),
		afs_printable_int32_ld(res.hdr.reason));
	}
    }

#endif /* AFS_DEMAND_ATTACH_FS */

 done:
    return code;
}
Exemple #18
0
Volume *
VCreateVolume_r(Error * ec, char *partname, VolumeId volumeId, VolumeId parentId)
{				/* Should be the same as volumeId if there is
				 * no parent */
    VolumeDiskData vol;
    int i, rc;
    char headerName[VMAXPATHLEN], volumePath[VMAXPATHLEN];
    Device device;
    struct DiskPartition64 *partition;
    struct VolumeDiskHeader diskHeader;
    IHandle_t *handle;
    FdHandle_t *fdP;
    Inode nearInode AFS_UNUSED = 0;
    char *part, *name;
    struct stat st;
    struct VolumeHeader tempHeader;
    struct afs_inode_info stuff[MAXINODETYPE];
    afs_ino_str_t stmp;
# ifdef AFS_DEMAND_ATTACH_FS
    int locktype = 0;
# endif /* AFS_DEMAND_ATTACH_FS */

    init_inode_info(&tempHeader, stuff);

    *ec = 0;
    memset(&vol, 0, sizeof(vol));
    vol.id = volumeId;
    vol.parentId = parentId;
    vol.copyDate = time(0);	/* The only date which really means when this
				 * @i(instance) of this volume was created.
				 * Creation date does not mean this */

    /* Initialize handle for error case below. */
    handle = NULL;

    /* Verify that the parition is valid before writing to it. */
    if (!(partition = VGetPartition_r(partname, 0))) {
	Log("VCreateVolume: partition %s is not in service.\n", partname);
	*ec = VNOVOL;
	return NULL;
    }
#if	defined(NEARINODE_HINT)
    nearInodeHash(volumeId, nearInode);
    nearInode %= partition->f_files;
#endif
    VGetVolumePath(ec, vol.id, &part, &name);
    if (*ec == VNOVOL || !strcmp(partition->name, part)) {
	/* this case is ok */
    } else {
	/* return EXDEV if it's a clone to an alternate partition
	 * otherwise assume it's a move */
	if (vol.parentId != vol.id) {
	    *ec = EXDEV;
	    return NULL;
	}
    }
    *ec = 0;

# ifdef AFS_DEMAND_ATTACH_FS
    /* volume doesn't exist yet, but we must lock it to try to prevent something
     * else from reading it when we're e.g. half way through creating it (or
     * something tries to create the same volume at the same time) */
    locktype = VVolLockType(V_VOLUPD, 1);
    rc = VLockVolumeByIdNB(volumeId, partition, locktype);
    if (rc) {
	Log("VCreateVolume: vol %lu already locked by someone else\n",
	    afs_printable_uint32_lu(volumeId));
	*ec = VNOVOL;
	return NULL;
    }
# else /* AFS_DEMAND_ATTACH_FS */
    VLockPartition_r(partname);
# endif /* !AFS_DEMAND_ATTACH_FS */

    memset(&tempHeader, 0, sizeof(tempHeader));
    tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
    tempHeader.stamp.version = VOLUMEHEADERVERSION;
    tempHeader.id = vol.id;
    tempHeader.parent = vol.parentId;
    vol.stamp.magic = VOLUMEINFOMAGIC;
    vol.stamp.version = VOLUMEINFOVERSION;
    vol.destroyMe = DESTROY_ME;
    snprintf(headerName, sizeof headerName, VFORMAT,
	     afs_printable_VolumeId_lu(vol.id));
    snprintf(volumePath, sizeof volumePath, "%s" OS_DIRSEP "%s",
	     VPartitionPath(partition), headerName);
    rc = stat(volumePath, &st);
    if (rc == 0 || errno != ENOENT) {
	if (rc == 0) {
	    Log("VCreateVolume: Header file %s already exists!\n",
		volumePath);
	    *ec = VVOLEXISTS;
	} else {
	    Log("VCreateVolume: Error %d trying to stat header file %s\n",
	        errno, volumePath);
	    *ec = VNOVOL;
	}
	goto bad_noheader;
    }
    device = partition->device;

    for (i = 0; i < MAXINODETYPE; i++) {
	struct afs_inode_info *p = &stuff[i];
	if (p->obsolete)
	    continue;
#ifdef AFS_NAMEI_ENV
	*(p->inode) =
	    IH_CREATE(NULL, device, VPartitionPath(partition), nearInode,
		      (p->inodeType == VI_LINKTABLE) ? vol.parentId : vol.id,
		      INODESPECIAL, p->inodeType, vol.parentId);
	if (!(VALID_INO(*(p->inode)))) {
	    if (errno == EEXIST && (p->inodeType == VI_LINKTABLE)) {
		/* Increment the reference count instead. */
		IHandle_t *lh;
		int code;

		*(p->inode) = namei_MakeSpecIno(vol.parentId, VI_LINKTABLE);
		IH_INIT(lh, device, parentId, *(p->inode));
		fdP = IH_OPEN(lh);
		if (fdP == NULL) {
		    IH_RELEASE(lh);
		    goto bad;
		}
		code = IH_INC(lh, *(p->inode), parentId);
		FDH_REALLYCLOSE(fdP);
		IH_RELEASE(lh);
		if (code < 0)
		    goto bad;
		continue;
	    }
	}
#else
	*(p->inode) =
	    IH_CREATE(NULL, device, VPartitionPath(partition), nearInode,
		      vol.id, INODESPECIAL, p->inodeType, vol.parentId);
#endif

	if (!VALID_INO(*(p->inode))) {
	    Log("VCreateVolume:  Problem creating %s file associated with volume header %s\n", p->description, volumePath);
	  bad:
	    if (handle)
		IH_RELEASE(handle);
	    RemoveInodes(stuff, device, vol.parentId, vol.id);
	    if (!*ec) {
		*ec = VNOVOL;
	    }
	    VDestroyVolumeDiskHeader(partition, volumeId, parentId);
	  bad_noheader:
# ifdef AFS_DEMAND_ATTACH_FS
	    if (locktype) {
		VUnlockVolumeById(volumeId, partition);
	    }
# endif /* AFS_DEMAND_ATTACH_FS */
	    return NULL;
	}
	IH_INIT(handle, device, vol.parentId, *(p->inode));
	fdP = IH_OPEN(handle);
	if (fdP == NULL) {
	    Log("VCreateVolume:  Problem iopen inode %s (err=%d)\n",
		PrintInode(stmp, *(p->inode)), errno);
	    goto bad;
	}
	if (FDH_PWRITE(fdP, (char *)&p->stamp, sizeof(p->stamp), 0) !=
	    sizeof(p->stamp)) {
	    Log("VCreateVolume:  Problem writing to inode %s (err=%d)\n",
		PrintInode(stmp, *(p->inode)), errno);
	    FDH_REALLYCLOSE(fdP);
	    goto bad;
	}
	FDH_REALLYCLOSE(fdP);
	IH_RELEASE(handle);
	nearInode = *(p->inode);
    }

    IH_INIT(handle, device, vol.parentId, tempHeader.volumeInfo);
    fdP = IH_OPEN(handle);
    if (fdP == NULL) {
	Log("VCreateVolume:  Problem iopen inode %s (err=%d)\n",
	    PrintInode(stmp, tempHeader.volumeInfo), errno);
	goto bad;
    }
    if (FDH_PWRITE(fdP, (char *)&vol, sizeof(vol), 0) != sizeof(vol)) {
	Log("VCreateVolume:  Problem writing to  inode %s (err=%d)\n",
	    PrintInode(stmp, tempHeader.volumeInfo), errno);
	FDH_REALLYCLOSE(fdP);
	goto bad;
    }
    FDH_CLOSE(fdP);
    IH_RELEASE(handle);

    VolumeHeaderToDisk(&diskHeader, &tempHeader);
    rc = VCreateVolumeDiskHeader(&diskHeader, partition);
    if (rc) {
	Log("VCreateVolume: Error %d trying to write volume header for "
	    "volume %" AFS_VOLID_FMT " on partition %s; volume not created\n", rc,
	    afs_printable_VolumeId_lu(vol.id), VPartitionPath(partition));
	if (rc == EEXIST) {
	    *ec = VVOLEXISTS;
	}
	goto bad;
    }

# ifdef AFS_DEMAND_ATTACH_FS
    if (locktype) {
	VUnlockVolumeById(volumeId, partition);
    }
# endif /* AFS_DEMAND_ATTACH_FS */
    return (VAttachVolumeByName_r(ec, partname, headerName, V_SECRETLY));
}
Exemple #19
0
/**
 * add an entry to the volume group cache.
 *
 * @param[in] dp       disk partition object
 * @param[in] parent   parent volume id
 * @param[in] child    child volume id
 * @param[out] newvg   if non-NULL, *newvg is 1 if adding this added a
 *                     new VG, 0 if we added to an existing VG
 *
 * @pre VOL_LOCK held
 *
 * @return operation status
 *    @retval 0 success
 *    @retval -1 parent and child are already registered in
 *               different VGs
 */
int
VVGCache_entry_add_r(struct DiskPartition64 * dp,
		     VolumeId parent,
		     VolumeId child,
		     afs_int32 *newvg)
{
    int code = 0, res;
    VVGCache_entry_t * child_ent, * parent_ent;

    if (newvg) {
	*newvg = 0;
    }

    /* check for existing entries */
    res = _VVGC_lookup(dp, child, &child_ent, NULL);
    if (res && res != ENOENT) {
	code = res;
	goto done;
    }

    res = _VVGC_lookup(dp, parent, &parent_ent, NULL);
    if (res && res != ENOENT) {
	code = res;
	goto done;
    }

    /*
     * branch based upon existence of parent and child nodes
     */
    if (parent_ent && child_ent) {
	/* both exist.  we're done.
	 * if they point different places, then report the error. */
	if (child_ent != parent_ent) {
	    code = -1;
	}
	if (parent == child) {
	    /* if we're adding the RW entry as a child, the RW id may
	     * not be in the child array yet, so make sure not to skip
	     * over that */
	    goto cladd;
	}
	goto done;
    } else if (!parent_ent && child_ent) {
	/* child exists.
	 * update vg root volid, and add hash entry. */
	parent_ent = child_ent;
	parent_ent->rw = parent;

	code = _VVGC_hash_entry_add(dp,
				    parent,
				    parent_ent,
				    NULL);
	goto done;
    } else if (!child_ent && !parent_ent) {
	code = _VVGC_entry_add(dp,
			       parent,
			       &parent_ent,
			       NULL);
	if (code) {
	    goto done;
	}
	if (newvg) {
	    *newvg = 1;
	}
	if (child == parent) {
	    /* if we're the RW, skip over adding the child hash entry;
	     * we already added the hash entry when creating the entry */
	    child_ent = parent_ent;
	    goto cladd;
	}
    }

    osi_Assert(!child_ent);
    child_ent = parent_ent;
    code = _VVGC_hash_entry_add(dp,
				child,
				child_ent,
				NULL);
    if (code) {
	goto done;
    }

 cladd:
    code = _VVGC_entry_cl_add(child_ent, child);

 done:
    if (code && code != EINVAL) {
	ViceLog(0, ("VVGCache_entry_add: error %d trying to add vol %lu to VG"
	    " %lu on partition %s", code, afs_printable_uint32_lu(child),
	    afs_printable_uint32_lu(parent), VPartitionPath(dp)));
    }

    if (code == 0 && VVGCache.part[dp->index].state == VVGC_PART_STATE_UPDATING) {
	/* we successfully added the entry; make sure it's not on the
	 * to-delete list, so it doesn't get deleted later */
	code = _VVGC_dlist_del_r(dp, parent, child);
	if (code && code != ENOENT) {
	    ViceLog(0, ("VVGCache_entry_add: error %d trying to remove vol "
	                "%lu (parent %lu) from the to-delete list for part "
	                "%s.\n", code, afs_printable_uint32_lu(child),
	                afs_printable_uint32_lu(parent),
	                VPartitionPath(dp)));
	} else {
	    code = 0;
	}
    }

    return code;
}