2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
10 #include <afsconfig.h>
11 #include <afs/param.h>
20 #include "vlserver_internal.h"
22 struct vlheader xheader
;
23 extern int maxnservers
;
24 extern afs_uint32 rd_HostAddress
[MAXSERVERID
+ 1];
25 extern afs_uint32 wr_HostAddress
[MAXSERVERID
+ 1];
26 struct extentaddr
*rd_ex_addr
[VL_MAX_ADDREXTBLKS
] = { 0, 0, 0, 0 };
27 struct extentaddr
*wr_ex_addr
[VL_MAX_ADDREXTBLKS
] = { 0, 0, 0, 0 };
28 struct vlheader rd_cheader
; /* kept in network byte order */
29 struct vlheader wr_cheader
;
32 static int index_OK(struct vl_ctx
*ctx
, afs_int32 blockindex
);
34 #define ERROR_EXIT(code) do { \
39 /* Hashing algorithm based on the volume id; HASHSIZE must be prime */
41 IDHash(afs_int32 volumeid
)
43 return ((abs(volumeid
)) % HASHSIZE
);
47 /* Hashing algorithm based on the volume name; name's size is implicit (64 chars) and if changed it should be reflected here. */
49 NameHash(char *volumename
)
55 for (i
= strlen(volumename
), volumename
+= i
- 1; i
--; volumename
--)
56 hash
= (hash
* 63) + (*((unsigned char *)volumename
) - 63);
57 return (hash
% HASHSIZE
);
61 /* package up seek and write into one procedure for ease of use */
63 vlwrite(struct ubik_trans
*trans
, afs_int32 offset
, void *buffer
,
68 if ((errorcode
= ubik_Seek(trans
, 0, offset
)))
70 return (ubik_Write(trans
, buffer
, length
));
74 /* Package up seek and read into one procedure for ease of use */
76 vlread(struct ubik_trans
*trans
, afs_int32 offset
, char *buffer
,
81 if ((errorcode
= ubik_Seek(trans
, 0, offset
)))
83 return (ubik_Read(trans
, buffer
, length
));
87 /* take entry and convert to network order and write to disk */
89 vlentrywrite(struct ubik_trans
*trans
, afs_int32 offset
, void *buffer
,
92 struct vlentry oentry
;
93 struct nvlentry nentry
, *nep
;
97 if (length
!= sizeof(oentry
))
99 if (maxnservers
== 13) {
100 nep
= (struct nvlentry
*)buffer
;
101 for (i
= 0; i
< MAXTYPES
; i
++)
102 nentry
.volumeId
[i
] = htonl(nep
->volumeId
[i
]);
103 nentry
.flags
= htonl(nep
->flags
);
104 nentry
.LockAfsId
= htonl(nep
->LockAfsId
);
105 nentry
.LockTimestamp
= htonl(nep
->LockTimestamp
);
106 nentry
.cloneId
= htonl(nep
->cloneId
);
107 for (i
= 0; i
< MAXTYPES
; i
++)
108 nentry
.nextIdHash
[i
] = htonl(nep
->nextIdHash
[i
]);
109 nentry
.nextNameHash
= htonl(nep
->nextNameHash
);
110 memcpy(nentry
.name
, nep
->name
, VL_MAXNAMELEN
);
111 memcpy(nentry
.serverNumber
, nep
->serverNumber
, NMAXNSERVERS
);
112 memcpy(nentry
.serverPartition
, nep
->serverPartition
, NMAXNSERVERS
);
113 memcpy(nentry
.serverFlags
, nep
->serverFlags
, NMAXNSERVERS
);
114 bufp
= (char *)&nentry
;
116 memset(&oentry
, 0, sizeof(struct vlentry
));
117 nep
= (struct nvlentry
*)buffer
;
118 for (i
= 0; i
< MAXTYPES
; i
++)
119 oentry
.volumeId
[i
] = htonl(nep
->volumeId
[i
]);
120 oentry
.flags
= htonl(nep
->flags
);
121 oentry
.LockAfsId
= htonl(nep
->LockAfsId
);
122 oentry
.LockTimestamp
= htonl(nep
->LockTimestamp
);
123 oentry
.cloneId
= htonl(nep
->cloneId
);
124 for (i
= 0; i
< MAXTYPES
; i
++)
125 oentry
.nextIdHash
[i
] = htonl(nep
->nextIdHash
[i
]);
126 oentry
.nextNameHash
= htonl(nep
->nextNameHash
);
127 memcpy(oentry
.name
, nep
->name
, VL_MAXNAMELEN
);
128 memcpy(oentry
.serverNumber
, nep
->serverNumber
, OMAXNSERVERS
);
129 memcpy(oentry
.serverPartition
, nep
->serverPartition
, OMAXNSERVERS
);
130 memcpy(oentry
.serverFlags
, nep
->serverFlags
, OMAXNSERVERS
);
131 bufp
= (char *)&oentry
;
133 return vlwrite(trans
, offset
, bufp
, length
);
136 /* read entry and convert to host order and write to disk */
138 vlentryread(struct ubik_trans
*trans
, afs_int32 offset
, char *buffer
,
141 struct vlentry
*oep
, tentry
;
142 struct nvlentry
*nep
, *nbufp
;
143 char *bufp
= (char *)&tentry
;
146 if (length
!= sizeof(vlentry
))
148 i
= vlread(trans
, offset
, bufp
, length
);
151 if (maxnservers
== 13) {
152 nep
= (struct nvlentry
*)bufp
;
153 nbufp
= (struct nvlentry
*)buffer
;
154 for (i
= 0; i
< MAXTYPES
; i
++)
155 nbufp
->volumeId
[i
] = ntohl(nep
->volumeId
[i
]);
156 nbufp
->flags
= ntohl(nep
->flags
);
157 nbufp
->LockAfsId
= ntohl(nep
->LockAfsId
);
158 nbufp
->LockTimestamp
= ntohl(nep
->LockTimestamp
);
159 nbufp
->cloneId
= ntohl(nep
->cloneId
);
160 for (i
= 0; i
< MAXTYPES
; i
++)
161 nbufp
->nextIdHash
[i
] = ntohl(nep
->nextIdHash
[i
]);
162 nbufp
->nextNameHash
= ntohl(nep
->nextNameHash
);
163 memcpy(nbufp
->name
, nep
->name
, VL_MAXNAMELEN
);
164 memcpy(nbufp
->serverNumber
, nep
->serverNumber
, NMAXNSERVERS
);
165 memcpy(nbufp
->serverPartition
, nep
->serverPartition
, NMAXNSERVERS
);
166 memcpy(nbufp
->serverFlags
, nep
->serverFlags
, NMAXNSERVERS
);
168 oep
= (struct vlentry
*)bufp
;
169 nbufp
= (struct nvlentry
*)buffer
;
170 memset(nbufp
, 0, sizeof(struct nvlentry
));
171 for (i
= 0; i
< MAXTYPES
; i
++)
172 nbufp
->volumeId
[i
] = ntohl(oep
->volumeId
[i
]);
173 nbufp
->flags
= ntohl(oep
->flags
);
174 nbufp
->LockAfsId
= ntohl(oep
->LockAfsId
);
175 nbufp
->LockTimestamp
= ntohl(oep
->LockTimestamp
);
176 nbufp
->cloneId
= ntohl(oep
->cloneId
);
177 for (i
= 0; i
< MAXTYPES
; i
++)
178 nbufp
->nextIdHash
[i
] = ntohl(oep
->nextIdHash
[i
]);
179 nbufp
->nextNameHash
= ntohl(oep
->nextNameHash
);
180 memcpy(nbufp
->name
, oep
->name
, VL_MAXNAMELEN
);
181 memcpy(nbufp
->serverNumber
, oep
->serverNumber
, NMAXNSERVERS
);
182 memcpy(nbufp
->serverPartition
, oep
->serverPartition
, NMAXNSERVERS
);
183 memcpy(nbufp
->serverFlags
, oep
->serverFlags
, NMAXNSERVERS
);
188 /* Convenient write of small critical vldb header info to the database. */
190 write_vital_vlheader(struct vl_ctx
*ctx
)
193 (ctx
->trans
, 0, (char *)&ctx
->cheader
->vital_header
, sizeof(vital_vlheader
)))
201 /* This routine reads in the extent blocks for multi-homed servers.
202 * There used to be an initialization bug that would cause the contaddrs
203 * pointers in the first extent block to be bad. Here we will check the
204 * pointers and zero them in the in-memory copy if we find them bad. We
205 * also try to write the extent blocks back out. If we can't, then we
206 * will wait until the next write transaction to write them out
207 * (extent_mod tells us the on-disk copy is bad).
210 readExtents(struct ubik_trans
*trans
)
212 afs_uint32 extentAddr
;
213 afs_int32 error
= 0, code
;
217 extentAddr
= ntohl(rd_cheader
.SIT
);
221 /* Read the first extension block */
222 if (!rd_ex_addr
[0]) {
223 rd_ex_addr
[0] = malloc(VL_ADDREXTBLK_SIZE
);
225 ERROR_EXIT(VL_NOMEM
);
227 code
= vlread(trans
, extentAddr
, (char *)rd_ex_addr
[0], VL_ADDREXTBLK_SIZE
);
229 free(rd_ex_addr
[0]); /* Not the place to create it */
234 /* In case more that 64 mh servers are in use they're kept in these
235 * continuation blocks
237 for (i
= 1; i
< VL_MAX_ADDREXTBLKS
; i
++) {
238 if (!rd_ex_addr
[0]->ex_contaddrs
[i
])
241 /* Before reading it in, check to see if the address is good */
242 if ((ntohl(rd_ex_addr
[0]->ex_contaddrs
[i
]) <
243 ntohl(rd_ex_addr
[0]->ex_contaddrs
[i
- 1]) + VL_ADDREXTBLK_SIZE
)
244 || (ntohl(rd_ex_addr
[0]->ex_contaddrs
[i
]) >
245 ntohl(rd_cheader
.vital_header
.eofPtr
) - VL_ADDREXTBLK_SIZE
)) {
247 rd_ex_addr
[0]->ex_contaddrs
[i
] = 0;
252 /* Read the continuation block */
253 if (!rd_ex_addr
[i
]) {
254 rd_ex_addr
[i
] = malloc(VL_ADDREXTBLK_SIZE
);
256 ERROR_EXIT(VL_NOMEM
);
259 vlread(trans
, ntohl(rd_ex_addr
[0]->ex_contaddrs
[i
]),
260 (char *)rd_ex_addr
[i
], VL_ADDREXTBLK_SIZE
);
262 free(rd_ex_addr
[i
]); /* Not the place to create it */
267 /* After reading it in, check to see if its a real continuation block */
268 if (ntohl(rd_ex_addr
[i
]->ex_hdrflags
) != VLCONTBLOCK
) {
270 rd_ex_addr
[0]->ex_contaddrs
[i
] = 0;
271 free(rd_ex_addr
[i
]); /* Not the place to create it */
278 code
= vlwrite(trans
, extentAddr
, rd_ex_addr
[0], VL_ADDREXTBLK_SIZE
);
280 VLog(0, ("Multihome server support modification\n"));
282 /* Keep extent_mod true in-case the transaction aborts */
283 /* Don't return error so we don't abort transaction */
290 /* Check that the database has been initialized. Be careful to fail in a safe
291 manner, to avoid bogusly reinitializing the db. */
293 * reads in db cache from ubik.
295 * @param[in] ut ubik transaction
296 * @param[in] rock opaque pointer to an int*; if 1, we should rebuild the db
297 * if it appears empty, if 0 we should return an error if the
300 * @return operation status
304 UpdateCache(struct ubik_trans
*trans
, void *rock
)
306 int *builddb_rock
= rock
;
307 int builddb
= *builddb_rock
;
308 afs_int32 error
= 0, i
, code
, ubcode
;
310 /* if version changed (or first call), read the header */
311 ubcode
= vlread(trans
, 0, (char *)&rd_cheader
, sizeof(rd_cheader
));
312 vldbversion
= ntohl(rd_cheader
.vital_header
.vldbversion
);
314 if (!ubcode
&& (vldbversion
!= 0)) {
315 memcpy(rd_HostAddress
, rd_cheader
.IpMappedAddr
, sizeof(rd_cheader
.IpMappedAddr
));
316 for (i
= 0; i
< MAXSERVERID
+ 1; i
++) { /* cvt HostAddress to host order */
317 rd_HostAddress
[i
] = ntohl(rd_HostAddress
[i
]);
320 code
= readExtents(trans
);
325 /* now, if can't read, or header is wrong, write a new header */
326 if (ubcode
|| vldbversion
== 0) {
328 VLog(0, ("Can't read VLDB header, re-initialising...\n"));
330 /* try to write a good header */
331 /* The read cache will be sync'ed to this new header
332 * when the ubik transaction is ended by vlsynccache(). */
333 memset(&wr_cheader
, 0, sizeof(wr_cheader
));
334 wr_cheader
.vital_header
.vldbversion
= htonl(VLDBVERSION
);
335 wr_cheader
.vital_header
.headersize
= htonl(sizeof(wr_cheader
));
336 /* DANGER: Must get this from a master place!! */
337 wr_cheader
.vital_header
.MaxVolumeId
= htonl(0x20000000);
338 wr_cheader
.vital_header
.eofPtr
= htonl(sizeof(wr_cheader
));
339 for (i
= 0; i
< MAXSERVERID
+ 1; i
++) {
340 wr_cheader
.IpMappedAddr
[i
] = 0;
341 wr_HostAddress
[i
] = 0;
343 code
= vlwrite(trans
, 0, (char *)&wr_cheader
, sizeof(wr_cheader
));
345 VLog(0, ("Can't write VLDB header (error = %d)\n", code
));
348 vldbversion
= ntohl(wr_cheader
.vital_header
.vldbversion
);
350 VLog(1, ("Unable to read VLDB header.\n"));
351 ERROR_EXIT(VL_EMPTY
);
355 if ((vldbversion
!= VLDBVERSION
) && (vldbversion
!= OVLDBVERSION
)
356 && (vldbversion
!= VLDBVERSION_4
)) {
358 ("VLDB version %d doesn't match this software version(%d, %d or %d), quitting!\n",
359 vldbversion
, VLDBVERSION_4
, VLDBVERSION
, OVLDBVERSION
));
360 ERROR_EXIT(VL_BADVERSION
);
363 maxnservers
= ((vldbversion
== 3 || vldbversion
== 4) ? 13 : 8);
371 CheckInit(struct ubik_trans
*trans
, int builddb
)
375 code
= ubik_CheckCache(trans
, UpdateCache
, &builddb
);
380 /* these next two cases shouldn't happen (UpdateCache should either
381 * rebuild the db or return an error if these cases occur), but just to
382 * be on the safe side... */
383 if (vldbversion
== 0) {
386 if ((vldbversion
!= VLDBVERSION
) && (vldbversion
!= OVLDBVERSION
)
387 && (vldbversion
!= VLDBVERSION_4
)) {
388 return VL_BADVERSION
;
396 GetExtentBlock(struct vl_ctx
*ctx
, register afs_int32 base
)
398 afs_int32 blockindex
, code
, error
= 0;
400 /* Base 0 must exist before any other can be created */
401 if ((base
!= 0) && !ctx
->ex_addr
[0])
402 ERROR_EXIT(VL_CREATEFAIL
); /* internal error */
404 if (!ctx
->ex_addr
[0] || !ctx
->ex_addr
[0]->ex_contaddrs
[base
]) {
405 /* Create a new extension block */
406 if (!ctx
->ex_addr
[base
]) {
407 ctx
->ex_addr
[base
] = malloc(VL_ADDREXTBLK_SIZE
);
408 if (!ctx
->ex_addr
[base
])
409 ERROR_EXIT(VL_NOMEM
);
411 memset(ctx
->ex_addr
[base
], 0, VL_ADDREXTBLK_SIZE
);
413 /* Write the full extension block at end of vldb */
414 ctx
->ex_addr
[base
]->ex_hdrflags
= htonl(VLCONTBLOCK
);
415 blockindex
= ntohl(ctx
->cheader
->vital_header
.eofPtr
);
417 vlwrite(ctx
->trans
, blockindex
, (char *)ctx
->ex_addr
[base
],
422 /* Update the cheader.vitalheader structure on disk */
423 ctx
->cheader
->vital_header
.eofPtr
= blockindex
+ VL_ADDREXTBLK_SIZE
;
424 ctx
->cheader
->vital_header
.eofPtr
= htonl(ctx
->cheader
->vital_header
.eofPtr
);
425 code
= write_vital_vlheader(ctx
);
429 /* Write the address of the base extension block in the vldb header */
431 ctx
->cheader
->SIT
= htonl(blockindex
);
433 vlwrite(ctx
->trans
, DOFFSET(0, ctx
->cheader
, &ctx
->cheader
->SIT
),
434 (char *)&ctx
->cheader
->SIT
, sizeof(ctx
->cheader
->SIT
));
439 /* Write the address of this extension block into the base extension block */
440 ctx
->ex_addr
[0]->ex_contaddrs
[base
] = htonl(blockindex
);
442 vlwrite(ctx
->trans
, ntohl(ctx
->cheader
->SIT
), ctx
->ex_addr
[0],
443 sizeof(struct extentaddr
));
454 FindExtentBlock(struct vl_ctx
*ctx
, afsUUID
*uuidp
,
455 afs_int32 createit
, afs_int32 hostslot
,
456 struct extentaddr
**expp
, afs_int32
*basep
)
459 struct extentaddr
*exp
;
460 afs_int32 i
, j
, code
, base
, index
, error
= 0;
465 /* Create the first extension block if it does not exist */
466 if (!ctx
->cheader
->SIT
) {
467 code
= GetExtentBlock(ctx
, 0);
472 for (i
= 0; i
< MAXSERVERID
+ 1; i
++) {
473 if ((ctx
->hostaddress
[i
] & 0xff000000) == 0xff000000) {
474 if ((base
= (ctx
->hostaddress
[i
] >> 16) & 0xff) > VL_MAX_ADDREXTBLKS
) {
475 ERROR_EXIT(VL_INDEXERANGE
);
477 if ((index
= ctx
->hostaddress
[i
] & 0x0000ffff) > VL_MHSRV_PERBLK
) {
478 ERROR_EXIT(VL_INDEXERANGE
);
480 exp
= &ctx
->ex_addr
[base
][index
];
481 tuuid
= exp
->ex_hostuuid
;
482 afs_ntohuuid(&tuuid
);
483 if (afs_uuid_equal(uuidp
, &tuuid
)) {
492 if (hostslot
== -1) {
493 for (i
= 0; i
< MAXSERVERID
+ 1; i
++) {
494 if (!ctx
->hostaddress
[i
])
498 ERROR_EXIT(VL_REPSFULL
);
503 for (base
= 0; base
< VL_MAX_ADDREXTBLKS
; base
++) {
504 if (!ctx
->ex_addr
[0]->ex_contaddrs
[base
]) {
505 code
= GetExtentBlock(ctx
, base
);
509 for (j
= 1; j
< VL_MHSRV_PERBLK
; j
++) {
510 exp
= &ctx
->ex_addr
[base
][j
];
511 tuuid
= exp
->ex_hostuuid
;
512 afs_ntohuuid(&tuuid
);
513 if (afs_uuid_is_nil(&tuuid
)) {
515 afs_htonuuid(&tuuid
);
516 exp
->ex_hostuuid
= tuuid
;
519 DOFFSET(ntohl(ctx
->ex_addr
[0]->ex_contaddrs
[base
]),
520 (char *)ctx
->ex_addr
[base
], (char *)exp
),
521 (char *)&tuuid
, sizeof(tuuid
));
524 ctx
->hostaddress
[i
] =
525 0xff000000 | ((base
<< 16) & 0xff0000) | (j
& 0xffff);
528 if (vldbversion
!= VLDBVERSION_4
) {
529 ctx
->cheader
->vital_header
.vldbversion
=
530 htonl(VLDBVERSION_4
);
531 code
= write_vital_vlheader(ctx
);
535 ctx
->cheader
->IpMappedAddr
[i
] = htonl(ctx
->hostaddress
[i
]);
538 DOFFSET(0, ctx
->cheader
,
539 &ctx
->cheader
->IpMappedAddr
[i
]),
540 (char *)&ctx
->cheader
->IpMappedAddr
[i
],
548 ERROR_EXIT(VL_REPSFULL
); /* No reason to utilize a new error code */
555 /* Allocate a free block of storage for entry, returning address of a new
556 zeroed entry (or zero if something is wrong). */
558 AllocBlock(struct vl_ctx
*ctx
, struct nvlentry
*tentry
)
560 afs_int32 blockindex
;
562 if (ctx
->cheader
->vital_header
.freePtr
) {
563 /* allocate this dude */
564 blockindex
= ntohl(ctx
->cheader
->vital_header
.freePtr
);
565 if (vlentryread(ctx
->trans
, blockindex
, (char *)tentry
, sizeof(vlentry
)))
567 ctx
->cheader
->vital_header
.freePtr
= htonl(tentry
->nextIdHash
[0]);
569 /* hosed, nothing on free list, grow file */
570 blockindex
= ntohl(ctx
->cheader
->vital_header
.eofPtr
); /* remember this guy */
571 ctx
->cheader
->vital_header
.eofPtr
= htonl(blockindex
+ sizeof(vlentry
));
573 ctx
->cheader
->vital_header
.allocs
++;
574 if (write_vital_vlheader(ctx
))
576 memset(tentry
, 0, sizeof(nvlentry
)); /* zero new entry */
581 /* Free a block given its index. It must already have been unthreaded. Returns zero for success or an error code on failure. */
583 FreeBlock(struct vl_ctx
*ctx
, afs_int32 blockindex
)
585 struct nvlentry tentry
;
587 /* check validity of blockindex just to be on the safe side */
588 if (!index_OK(ctx
, blockindex
))
590 memset(&tentry
, 0, sizeof(nvlentry
));
591 tentry
.nextIdHash
[0] = ctx
->cheader
->vital_header
.freePtr
; /* already in network order */
592 tentry
.flags
= htonl(VLFREE
);
593 ctx
->cheader
->vital_header
.freePtr
= htonl(blockindex
);
594 if (vlwrite(ctx
->trans
, blockindex
, (char *)&tentry
, sizeof(nvlentry
)))
596 ctx
->cheader
->vital_header
.frees
++;
597 if (write_vital_vlheader(ctx
))
603 /* Look for a block by volid and voltype (if not known use -1 which searches
604 * all 3 volid hash lists. Note that the linked lists are read in first from
605 * the database header. If found read the block's contents into the area
606 * pointed to by tentry and return the block's index. If not found return 0.
609 FindByID(struct vl_ctx
*ctx
, afs_uint32 volid
, afs_int32 voltype
,
610 struct nvlentry
*tentry
, afs_int32
*error
)
612 afs_int32 typeindex
, hashindex
, blockindex
;
615 hashindex
= IDHash(volid
);
617 /* Should we have one big hash table for volids as opposed to the three ones? */
618 for (typeindex
= 0; typeindex
< MAXTYPES
; typeindex
++) {
619 for (blockindex
= ntohl(ctx
->cheader
->VolidHash
[typeindex
][hashindex
]);
621 blockindex
= tentry
->nextIdHash
[typeindex
]) {
623 (ctx
->trans
, blockindex
, (char *)tentry
, sizeof(nvlentry
))) {
627 if (volid
== tentry
->volumeId
[typeindex
])
632 for (blockindex
= ntohl(ctx
->cheader
->VolidHash
[voltype
][hashindex
]);
633 blockindex
!= NULLO
; blockindex
= tentry
->nextIdHash
[voltype
]) {
635 (ctx
->trans
, blockindex
, (char *)tentry
, sizeof(nvlentry
))) {
639 if (volid
== tentry
->volumeId
[voltype
])
643 return 0; /* no such entry */
647 /* Look for a block by volume name. If found read the block's contents into
648 * the area pointed to by tentry and return the block's index. If not
652 FindByName(struct vl_ctx
*ctx
, char *volname
, struct nvlentry
*tentry
,
656 afs_int32 blockindex
;
657 char tname
[VL_MAXNAMELEN
];
659 /* remove .backup or .readonly extensions for stupid backwards
662 hashindex
= strlen(volname
); /* really string length */
663 if (hashindex
>= 8 && strcmp(volname
+ hashindex
- 7, ".backup") == 0) {
664 /* this is a backup volume */
665 strcpy(tname
, volname
);
666 tname
[hashindex
- 7] = 0; /* zap extension */
667 } else if (hashindex
>= 10
668 && strcmp(volname
+ hashindex
- 9, ".readonly") == 0) {
669 /* this is a readonly volume */
670 strcpy(tname
, volname
);
671 tname
[hashindex
- 9] = 0; /* zap extension */
673 strcpy(tname
, volname
);
676 hashindex
= NameHash(tname
);
677 for (blockindex
= ntohl(ctx
->cheader
->VolnameHash
[hashindex
]);
678 blockindex
!= NULLO
; blockindex
= tentry
->nextNameHash
) {
679 if (vlentryread(ctx
->trans
, blockindex
, (char *)tentry
, sizeof(nvlentry
))) {
683 if (!strcmp(tname
, tentry
->name
))
686 return 0; /* no such entry */
690 * Returns whether or not any of the supplied volume IDs already exist
693 * @param ctx transaction context
694 * @param ids an array of volume IDs
695 * @param ids_len the number of elements in the 'ids' array
696 * @param error filled in with an error code in case of error
698 * @return whether any of the volume IDs are already used
699 * @retval 1 at least one of the volume IDs is already used
700 * @retval 0 none of the volume IDs are used, or an error occurred
703 EntryIDExists(struct vl_ctx
*ctx
, const afs_uint32
*ids
,
704 afs_int32 ids_len
, afs_int32
*error
)
707 struct nvlentry tentry
;
711 for (typeindex
= 0; typeindex
< ids_len
; typeindex
++) {
713 && FindByID(ctx
, ids
[typeindex
], -1, &tentry
, error
)) {
725 * Finds the next range of unused volume IDs in the vldb.
727 * @param ctx transaction context
728 * @param maxvolid the current max vol ID, and where to start looking
729 * for an unused volume ID range
730 * @param bump how many volume IDs we need to be unused
731 * @param error filled in with an error code in case of error
733 * @return the next volume ID 'volid' such that the range
734 * [volid, volid+bump) of volume IDs is unused, or 0 if there's
738 NextUnusedID(struct vl_ctx
*ctx
, afs_uint32 maxvolid
, afs_uint32 bump
,
741 struct nvlentry tentry
;
747 /* we simply start at the given maxvolid, keep a running tally of
748 * how many free volume IDs we've seen in a row, and return when
749 * we've seen 'bump' unused IDs in a row */
750 for (id
= maxvolid
, nfree
= 0; nfree
< bump
; ++id
) {
751 if (FindByID(ctx
, id
, -1, &tentry
, error
)) {
760 /* 'id' is now at the end of the [maxvolid,maxvolid+bump) range,
761 * but we need to return the first unused id, so subtract the
762 * number of current running free IDs to get the beginning */
767 HashNDump(struct vl_ctx
*ctx
, int hashindex
)
771 struct nvlentry tentry
;
773 for (blockindex
= ntohl(ctx
->cheader
->VolnameHash
[hashindex
]);
774 blockindex
!= NULLO
; blockindex
= tentry
.nextNameHash
) {
775 if (vlentryread(ctx
->trans
, blockindex
, (char *)&tentry
, sizeof(nvlentry
)))
779 ("[%d]#%d: %10d %d %d (%s)\n", hashindex
, i
, tentry
.volumeId
[0],
780 tentry
.nextIdHash
[0], tentry
.nextNameHash
, tentry
.name
));
787 HashIdDump(struct vl_ctx
*ctx
, int hashindex
)
791 struct nvlentry tentry
;
793 for (blockindex
= ntohl(ctx
->cheader
->VolidHash
[0][hashindex
]);
794 blockindex
!= NULLO
; blockindex
= tentry
.nextIdHash
[0]) {
795 if (vlentryread(ctx
->trans
, blockindex
, (char *)&tentry
, sizeof(nvlentry
)))
799 ("[%d]#%d: %10d %d %d (%s)\n", hashindex
, i
, tentry
.volumeId
[0],
800 tentry
.nextIdHash
[0], tentry
.nextNameHash
, tentry
.name
));
806 /* Add a block to the hash table given a pointer to the block and its index.
807 * The block is threaded onto both hash tables and written to disk. The
808 * routine returns zero if there were no errors.
811 ThreadVLentry(struct vl_ctx
*ctx
, afs_int32 blockindex
,
812 struct nvlentry
*tentry
)
816 if (!index_OK(ctx
, blockindex
))
818 /* Insert into volid's hash linked list */
819 if ((errorcode
= HashVolid(ctx
, RWVOL
, blockindex
, tentry
)))
822 /* For rw entries we also enter the RO and BACK volume ids (if they
823 * exist) in the hash tables; note all there volids (RW, RO, BACK)
824 * should not be hashed yet! */
825 if (tentry
->volumeId
[ROVOL
]) {
826 if ((errorcode
= HashVolid(ctx
, ROVOL
, blockindex
, tentry
)))
829 if (tentry
->volumeId
[BACKVOL
]) {
830 if ((errorcode
= HashVolid(ctx
, BACKVOL
, blockindex
, tentry
)))
834 /* Insert into volname's hash linked list */
835 HashVolname(ctx
, blockindex
, tentry
);
837 /* Update cheader entry */
838 if (write_vital_vlheader(ctx
))
841 /* Update hash list pointers in the entry itself */
842 if (vlentrywrite(ctx
->trans
, blockindex
, (char *)tentry
, sizeof(nvlentry
)))
848 /* Remove a block from both the hash tables. If success return 0, else
849 * return an error code. */
851 UnthreadVLentry(struct vl_ctx
*ctx
, afs_int32 blockindex
,
852 struct nvlentry
*aentry
)
854 afs_int32 errorcode
, typeindex
;
856 if (!index_OK(ctx
, blockindex
))
858 if ((errorcode
= UnhashVolid(ctx
, RWVOL
, blockindex
, aentry
)))
861 /* Take the RO/RW entries of their respective hash linked lists. */
862 for (typeindex
= ROVOL
; typeindex
<= BACKVOL
; typeindex
++) {
863 if ((errorcode
= UnhashVolid(ctx
, typeindex
, blockindex
, aentry
)))
867 /* Take it out of the Volname hash list */
868 if ((errorcode
= UnhashVolname(ctx
, blockindex
, aentry
)))
871 /* Update cheader entry */
872 write_vital_vlheader(ctx
);
877 /* cheader must have be read before this routine is called. */
879 HashVolid(struct vl_ctx
*ctx
, afs_int32 voltype
, afs_int32 blockindex
,
880 struct nvlentry
*tentry
)
882 afs_int32 hashindex
, errorcode
;
883 struct nvlentry ventry
;
886 (ctx
, tentry
->volumeId
[voltype
], voltype
, &ventry
, &errorcode
))
887 return VL_IDALREADYHASHED
;
890 hashindex
= IDHash(tentry
->volumeId
[voltype
]);
891 tentry
->nextIdHash
[voltype
] =
892 ntohl(ctx
->cheader
->VolidHash
[voltype
][hashindex
]);
893 ctx
->cheader
->VolidHash
[voltype
][hashindex
] = htonl(blockindex
);
895 (ctx
->trans
, DOFFSET(0, ctx
->cheader
, &ctx
->cheader
->VolidHash
[voltype
][hashindex
]),
896 (char *)&ctx
->cheader
->VolidHash
[voltype
][hashindex
], sizeof(afs_int32
)))
902 /* cheader must have be read before this routine is called. */
904 UnhashVolid(struct vl_ctx
*ctx
, afs_int32 voltype
, afs_int32 blockindex
,
905 struct nvlentry
*aentry
)
907 int hashindex
, nextblockindex
, prevblockindex
;
908 struct nvlentry tentry
;
912 if (aentry
->volumeId
[voltype
] == NULLO
) /* Assume no volume id */
914 /* Take it out of the VolId[voltype] hash list */
915 hashindex
= IDHash(aentry
->volumeId
[voltype
]);
916 nextblockindex
= ntohl(ctx
->cheader
->VolidHash
[voltype
][hashindex
]);
917 if (nextblockindex
== blockindex
) {
918 /* First on the hash list; just adjust pointers */
919 ctx
->cheader
->VolidHash
[voltype
][hashindex
] =
920 htonl(aentry
->nextIdHash
[voltype
]);
923 DOFFSET(0, ctx
->cheader
,
924 &ctx
->cheader
->VolidHash
[voltype
][hashindex
]),
925 (char *)&ctx
->cheader
->VolidHash
[voltype
][hashindex
],
930 while (nextblockindex
!= blockindex
) {
931 prevblockindex
= nextblockindex
; /* always done once */
933 (ctx
->trans
, nextblockindex
, (char *)&tentry
, sizeof(nvlentry
)))
935 if ((nextblockindex
= tentry
.nextIdHash
[voltype
]) == NULLO
)
938 temp
= tentry
.nextIdHash
[voltype
] = aentry
->nextIdHash
[voltype
];
939 temp
= htonl(temp
); /* convert to network byte order before writing */
942 DOFFSET(prevblockindex
, &tentry
, &tentry
.nextIdHash
[voltype
]),
943 (char *)&temp
, sizeof(afs_int32
)))
946 aentry
->nextIdHash
[voltype
] = 0;
952 HashVolname(struct vl_ctx
*ctx
, afs_int32 blockindex
,
953 struct nvlentry
*aentry
)
958 /* Insert into volname's hash linked list */
959 hashindex
= NameHash(aentry
->name
);
960 aentry
->nextNameHash
= ntohl(ctx
->cheader
->VolnameHash
[hashindex
]);
961 ctx
->cheader
->VolnameHash
[hashindex
] = htonl(blockindex
);
963 vlwrite(ctx
->trans
, DOFFSET(0, ctx
->cheader
, &ctx
->cheader
->VolnameHash
[hashindex
]),
964 (char *)&ctx
->cheader
->VolnameHash
[hashindex
], sizeof(afs_int32
));
972 UnhashVolname(struct vl_ctx
*ctx
, afs_int32 blockindex
,
973 struct nvlentry
*aentry
)
975 afs_int32 hashindex
, nextblockindex
, prevblockindex
;
976 struct nvlentry tentry
;
979 /* Take it out of the Volname hash list */
980 hashindex
= NameHash(aentry
->name
);
981 nextblockindex
= ntohl(ctx
->cheader
->VolnameHash
[hashindex
]);
982 if (nextblockindex
== blockindex
) {
983 /* First on the hash list; just adjust pointers */
984 ctx
->cheader
->VolnameHash
[hashindex
] = htonl(aentry
->nextNameHash
);
986 (ctx
->trans
, DOFFSET(0, ctx
->cheader
, &ctx
->cheader
->VolnameHash
[hashindex
]),
987 (char *)&ctx
->cheader
->VolnameHash
[hashindex
], sizeof(afs_int32
)))
990 while (nextblockindex
!= blockindex
) {
991 prevblockindex
= nextblockindex
; /* always done at least once */
993 (ctx
->trans
, nextblockindex
, (char *)&tentry
, sizeof(nvlentry
)))
995 if ((nextblockindex
= tentry
.nextNameHash
) == NULLO
)
998 tentry
.nextNameHash
= aentry
->nextNameHash
;
999 temp
= htonl(tentry
.nextNameHash
);
1001 (ctx
->trans
, DOFFSET(prevblockindex
, &tentry
, &tentry
.nextNameHash
),
1002 (char *)&temp
, sizeof(afs_int32
)))
1005 aentry
->nextNameHash
= 0;
1010 /* Returns the vldb entry tentry at offset index; remaining is the number of
1011 * entries left; the routine also returns the index of the next sequential
1016 NextEntry(struct vl_ctx
*ctx
, afs_int32 blockindex
,
1017 struct nvlentry
*tentry
, afs_int32
*remaining
)
1019 afs_int32 lastblockindex
;
1021 if (blockindex
== 0) /* get first one */
1022 blockindex
= sizeof(*ctx
->cheader
);
1024 if (!index_OK(ctx
, blockindex
)) {
1025 *remaining
= -1; /* error */
1028 blockindex
+= sizeof(nvlentry
);
1030 /* now search for the first entry that isn't free */
1031 for (lastblockindex
= ntohl(ctx
->cheader
->vital_header
.eofPtr
);
1032 blockindex
< lastblockindex
;) {
1033 if (vlentryread(ctx
->trans
, blockindex
, (char *)tentry
, sizeof(nvlentry
))) {
1037 if (tentry
->flags
== VLCONTBLOCK
) {
1039 * This is a special mh extension block just simply skip over it
1041 blockindex
+= VL_ADDREXTBLK_SIZE
;
1043 if (tentry
->flags
!= VLFREE
) {
1044 /* estimate remaining number of entries, not including this one */
1046 (lastblockindex
- blockindex
) / sizeof(nvlentry
) - 1;
1049 blockindex
+= sizeof(nvlentry
);
1052 *remaining
= 0; /* no more entries */
1057 /* Routine to verify that index is a legal offset to a vldb entry in the
1061 index_OK(struct vl_ctx
*ctx
, afs_int32 blockindex
)
1063 if ((blockindex
< sizeof(*ctx
->cheader
))
1064 || (blockindex
>= ntohl(ctx
->cheader
->vital_header
.eofPtr
)))
1069 /* makes a deep copy of src_ex into dst_ex */
1071 vlexcpy(struct extentaddr
**dst_ex
, struct extentaddr
**src_ex
)
1074 for (i
= 0; i
< VL_MAX_ADDREXTBLKS
; i
++) {
1077 dst_ex
[i
] = malloc(VL_ADDREXTBLK_SIZE
);
1082 memcpy(dst_ex
[i
], src_ex
[i
], VL_ADDREXTBLK_SIZE
);
1084 } else if (dst_ex
[i
]) {
1085 /* we have no src, but we have a dst... meaning, this block
1095 vlsetcache(struct vl_ctx
*ctx
, int locktype
)
1097 if (locktype
== LOCKREAD
) {
1098 ctx
->hostaddress
= rd_HostAddress
;
1099 ctx
->ex_addr
= rd_ex_addr
;
1100 ctx
->cheader
= &rd_cheader
;
1103 memcpy(wr_HostAddress
, rd_HostAddress
, sizeof(wr_HostAddress
));
1104 memcpy(&wr_cheader
, &rd_cheader
, sizeof(wr_cheader
));
1106 ctx
->hostaddress
= wr_HostAddress
;
1107 ctx
->ex_addr
= wr_ex_addr
;
1108 ctx
->cheader
= &wr_cheader
;
1110 return vlexcpy(wr_ex_addr
, rd_ex_addr
);
1117 memcpy(rd_HostAddress
, wr_HostAddress
, sizeof(rd_HostAddress
));
1118 memcpy(&rd_cheader
, &wr_cheader
, sizeof(rd_cheader
));
1119 return vlexcpy(rd_ex_addr
, wr_ex_addr
);