1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1999,2008 Oracle.  All rights reserved.
5 *
6 * $Id: qam_files.c,v 12.37 2008/03/13 15:44:50 mbrey Exp $
7 */
8
9#include "db_config.h"
10
11#include "db_int.h"
12#include "dbinc/db_page.h"
13#include "dbinc/db_am.h"
14#include "dbinc/log.h"
15#include "dbinc/fop.h"
16#include "dbinc/mp.h"
17#include "dbinc/qam.h"
18
19#define	QAM_EXNAME(Q, I, B, L)						\
20	snprintf((B), (L),						\
21	    QUEUE_EXTENT, (Q)->dir, PATH_SEPARATOR[0], (Q)->name, (I))
22
23/*
24 * __qam_fprobe -- calculate and open extent
25 *
26 * Calculate which extent the page is in, open and create if necessary.
27 *
28 * PUBLIC: int __qam_fprobe __P((DBC *, db_pgno_t,
29 * PUBLIC:     void *, qam_probe_mode, DB_CACHE_PRIORITY, u_int32_t));
30 */
31int
32__qam_fprobe(dbc, pgno, addrp, mode, priority, flags)
33	DBC *dbc;
34	db_pgno_t pgno;
35	void *addrp;
36	qam_probe_mode mode;
37	DB_CACHE_PRIORITY priority;
38	u_int32_t flags;
39{
40	DB *dbp;
41	DB_MPOOLFILE *mpf;
42	ENV *env;
43	MPFARRAY *array;
44	QUEUE *qp;
45	u_int8_t fid[DB_FILE_ID_LEN];
46	u_int32_t i, extid, maxext, numext, lflags, offset, oldext, openflags;
47	char buf[DB_MAXPATHLEN];
48	int ftype, less, ret, t_ret;
49
50	dbp = dbc->dbp;
51	env = dbp->env;
52	qp = (QUEUE *)dbp->q_internal;
53	ret = 0;
54
55	if (qp->page_ext == 0) {
56		mpf = dbp->mpf;
57		switch (mode) {
58		case QAM_PROBE_GET:
59			return (__memp_fget(mpf, &pgno,
60			    dbc->thread_info, dbc->txn, flags, addrp));
61		case QAM_PROBE_PUT:
62			return (__memp_fput(mpf,
63			    dbc->thread_info, addrp, priority));
64		case QAM_PROBE_DIRTY:
65			return (__memp_dirty(mpf, addrp,
66			    dbc->thread_info, dbc->txn, priority, flags));
67		case QAM_PROBE_MPF:
68			*(DB_MPOOLFILE **)addrp = mpf;
69			return (0);
70		}
71	}
72
73	mpf = NULL;
74
75	/*
76	 * Need to lock long enough to find the mpf or create the file.
77	 * The file cannot go away because we must have a record locked
78	 * in that file.
79	 */
80	MUTEX_LOCK(env, dbp->mutex);
81	extid = QAM_PAGE_EXTENT(dbp, pgno);
82
83	/* Array1 will always be in use if array2 is in use. */
84	array = &qp->array1;
85	if (array->n_extent == 0) {
86		/* Start with 4 extents */
87		array->n_extent = 4;
88		array->low_extent = extid;
89		numext = offset = oldext = 0;
90		less = 0;
91		goto alloc;
92	}
93
94retry:
95	if (extid < array->low_extent) {
96		less = 1;
97		offset = array->low_extent - extid;
98	} else {
99		less = 0;
100		offset = extid - array->low_extent;
101	}
102	if (qp->array2.n_extent != 0 &&
103	    (extid >= qp->array2.low_extent ?
104	    offset > extid - qp->array2.low_extent :
105	    offset > qp->array2.low_extent - extid)) {
106		array = &qp->array2;
107		if (extid < array->low_extent) {
108			less = 1;
109			offset = array->low_extent - extid;
110		} else {
111			less = 0;
112			offset = extid - array->low_extent;
113		}
114	}
115
116	/*
117	 * Check to see if the requested extent is outside the range of
118	 * extents in the array.  This is true by default if there are
119	 * no extents here yet.
120	 */
121	if (less == 1 || offset >= array->n_extent) {
122		oldext = array->n_extent;
123		numext = (array->hi_extent - array->low_extent) + 1;
124		if (less == 1 && offset + numext <= array->n_extent) {
125			/*
126			 * If we can fit this one into the existing array by
127			 * shifting the existing entries then we do not have
128			 * to allocate.
129			 */
130			memmove(&array->mpfarray[offset],
131			    array->mpfarray, numext
132			    * sizeof(array->mpfarray[0]));
133			memset(array->mpfarray, 0, offset
134			    * sizeof(array->mpfarray[0]));
135			offset = 0;
136		} else if (less == 0 && offset == array->n_extent &&
137		    (mode == QAM_PROBE_GET || mode == QAM_PROBE_PUT) &&
138		    array->mpfarray[0].pinref == 0) {
139			/*
140			 * If this is at the end of the array and the file at
141			 * the beginning has a zero pin count we can close
142			 * the bottom extent and put this one at the end.
143			 */
144			mpf = array->mpfarray[0].mpf;
145			if (mpf != NULL && (ret = __memp_fclose(mpf, 0)) != 0)
146				goto err;
147			memmove(&array->mpfarray[0], &array->mpfarray[1],
148			    (array->n_extent - 1) * sizeof(array->mpfarray[0]));
149			array->low_extent++;
150			array->hi_extent++;
151			offset--;
152			array->mpfarray[offset].mpf = NULL;
153			array->mpfarray[offset].pinref = 0;
154		} else {
155			/*
156			 * See if we have wrapped around the queue.
157			 * If it has then allocate the second array.
158			 * Otherwise just expand the one we are using.
159			 */
160			maxext = (u_int32_t) UINT32_MAX
161			    / (qp->page_ext * qp->rec_page);
162			if (offset >= maxext/2) {
163				array = &qp->array2;
164				DB_ASSERT(env, array->n_extent == 0);
165				oldext = 0;
166				array->n_extent = 4;
167				array->low_extent = extid;
168				offset = 0;
169				numext = 0;
170			} else if (array->mpfarray[0].pinref == 0) {
171				/*
172				 * Check to see if there are extents marked
173				 * for deletion at the beginning of the cache.
174				 * If so close them so they will go away.
175				 */
176				for (i = 0; i < array->n_extent; i++) {
177					if (array->mpfarray[i].pinref != 0)
178						break;
179					mpf = array->mpfarray[i].mpf;
180					if (mpf == NULL)
181						continue;
182					(void)__memp_get_flags(mpf, &lflags);
183					if (!FLD_ISSET(lflags, DB_MPOOL_UNLINK))
184						break;
185
186					array->mpfarray[i].mpf = NULL;
187					if ((ret = __memp_fclose(mpf, 0)) != 0)
188						goto err;
189				}
190				if (i == 0)
191					goto increase;
192				memmove(&array->mpfarray[0],
193				     &array->mpfarray[i],
194				    (array->n_extent - i) *
195				    sizeof(array->mpfarray[0]));
196				memset(&array->mpfarray[array->n_extent - i],
197				     '\0', i * sizeof(array->mpfarray[0]));
198				array->low_extent += i;
199				array->hi_extent += i;
200				goto retry;
201			} else {
202				/*
203				 * Increase the size to at least include
204				 * the new one and double it.
205				 */
206increase:			array->n_extent += offset;
207				array->n_extent <<= 2;
208			}
209alloc:			if ((ret = __os_realloc(env,
210			    array->n_extent * sizeof(struct __qmpf),
211			    &array->mpfarray)) != 0)
212				goto err;
213
214			if (less == 1) {
215				/*
216				 * Move the array up and put the new one
217				 * in the first slot.
218				 */
219				memmove(&array->mpfarray[offset],
220				    array->mpfarray,
221				    numext * sizeof(array->mpfarray[0]));
222				memset(array->mpfarray, 0,
223				    offset * sizeof(array->mpfarray[0]));
224				memset(&array->mpfarray[numext + offset], 0,
225				    (array->n_extent - (numext + offset))
226				    * sizeof(array->mpfarray[0]));
227				offset = 0;
228			}
229			else
230				/* Clear the new part of the array. */
231				memset(&array->mpfarray[oldext], 0,
232				    (array->n_extent - oldext) *
233				    sizeof(array->mpfarray[0]));
234		}
235	}
236
237	/* Update the low and hi range of saved extents. */
238	if (extid < array->low_extent)
239		array->low_extent = extid;
240	if (extid > array->hi_extent)
241		array->hi_extent = extid;
242
243	/* If the extent file is not yet open, open it. */
244	if (array->mpfarray[offset].mpf == NULL) {
245		QAM_EXNAME(qp, extid, buf, sizeof(buf));
246		if ((ret = __memp_fcreate(
247		    env, &array->mpfarray[offset].mpf)) != 0)
248			goto err;
249		mpf = array->mpfarray[offset].mpf;
250		(void)__memp_set_lsn_offset(mpf, 0);
251		(void)__memp_set_pgcookie(mpf, &qp->pgcookie);
252		(void)__memp_get_ftype(dbp->mpf, &ftype);
253		(void)__memp_set_ftype(mpf, ftype);
254		(void)__memp_set_clear_len(mpf, dbp->pgsize);
255
256		/* Set up the fileid for this extent. */
257		__qam_exid(dbp, fid, extid);
258		(void)__memp_set_fileid(mpf, fid);
259		openflags = DB_EXTENT;
260		if (LF_ISSET(DB_MPOOL_CREATE))
261			openflags |= DB_CREATE;
262		if (F_ISSET(dbp, DB_AM_RDONLY))
263			openflags |= DB_RDONLY;
264		if (F_ISSET(env->dbenv, DB_ENV_DIRECT_DB))
265			openflags |= DB_DIRECT;
266		if ((ret = __memp_fopen(
267		    mpf, NULL, buf, openflags, qp->mode, dbp->pgsize)) != 0) {
268			array->mpfarray[offset].mpf = NULL;
269			(void)__memp_fclose(mpf, 0);
270			goto err;
271		}
272	}
273
274	/*
275	 * We have found the right file.  Update its ref count
276	 * before dropping the dbp mutex so it does not go away.
277	 */
278	mpf = array->mpfarray[offset].mpf;
279	if (mode == QAM_PROBE_GET)
280		array->mpfarray[offset].pinref++;
281
282	/*
283	 * If we may create the page, then we are writing,
284	 * the file may nolonger be empty after this operation
285	 * so we clear the UNLINK flag.
286	 */
287	if (LF_ISSET(DB_MPOOL_CREATE))
288		(void)__memp_set_flags(mpf, DB_MPOOL_UNLINK, 0);
289
290err:
291	MUTEX_UNLOCK(env, dbp->mutex);
292
293	if (ret == 0) {
294		pgno--;
295		pgno %= qp->page_ext;
296		switch (mode) {
297		case QAM_PROBE_GET:
298			ret = __memp_fget(mpf, &pgno,
299			    dbc->thread_info, dbc->txn, flags, addrp);
300			if (ret == 0)
301				return (0);
302			break;
303		case QAM_PROBE_PUT:
304			ret = __memp_fput(mpf,
305			    dbc->thread_info, addrp, dbp->priority);
306			break;
307		case QAM_PROBE_DIRTY:
308			return (__memp_dirty(mpf, addrp,
309			    dbc->thread_info, dbc->txn, dbp->priority, flags));
310		case QAM_PROBE_MPF:
311			*(DB_MPOOLFILE **)addrp = mpf;
312			return (0);
313		}
314
315		MUTEX_LOCK(env, dbp->mutex);
316		/* Recalculate because we dropped the lock. */
317		offset = extid - array->low_extent;
318		DB_ASSERT(env, array->mpfarray[offset].pinref > 0);
319		if (--array->mpfarray[offset].pinref == 0 &&
320		    (mode == QAM_PROBE_GET || ret == 0)) {
321			/* Check to see if this file will be unlinked. */
322			(void)__memp_get_flags(mpf, &flags);
323			if (LF_ISSET(DB_MPOOL_UNLINK)) {
324				array->mpfarray[offset].mpf = NULL;
325				if ((t_ret =
326				    __memp_fclose(mpf, 0)) != 0 && ret == 0)
327					ret = t_ret;
328			}
329		}
330		MUTEX_UNLOCK(env, dbp->mutex);
331	}
332	return (ret);
333}
334
335/*
336 * __qam_fclose -- close an extent.
337 *
338 * Calculate which extent the page is in and close it.
339 * We assume the mpf entry is present.
340 *
341 * PUBLIC: int __qam_fclose __P((DB *, db_pgno_t));
342 */
343int
344__qam_fclose(dbp, pgnoaddr)
345	DB *dbp;
346	db_pgno_t pgnoaddr;
347{
348	DB_MPOOLFILE *mpf;
349	ENV *env;
350	MPFARRAY *array;
351	QUEUE *qp;
352	u_int32_t extid, offset;
353	int ret;
354
355	ret = 0;
356	env = dbp->env;
357	qp = (QUEUE *)dbp->q_internal;
358
359	MUTEX_LOCK(env, dbp->mutex);
360
361	extid = QAM_PAGE_EXTENT(dbp, pgnoaddr);
362	array = &qp->array1;
363	if (array->low_extent > extid || array->hi_extent < extid)
364		array = &qp->array2;
365	offset = extid - array->low_extent;
366
367	DB_ASSERT(env,
368	    extid >= array->low_extent && offset < array->n_extent);
369
370	/* If other threads are still using this file, leave it. */
371	if (array->mpfarray[offset].pinref != 0)
372		goto done;
373
374	mpf = array->mpfarray[offset].mpf;
375	array->mpfarray[offset].mpf = NULL;
376	ret = __memp_fclose(mpf, 0);
377
378done:
379	MUTEX_UNLOCK(env, dbp->mutex);
380	return (ret);
381}
382
383/*
384 * __qam_fremove -- remove an extent.
385 *
386 * Calculate which extent the page is in and remove it.  There is no way
387 * to remove an extent without probing it first and seeing that is is empty
388 * so we assume the mpf entry is present.
389 *
390 * PUBLIC: int __qam_fremove __P((DB *, db_pgno_t));
391 */
392int
393__qam_fremove(dbp, pgnoaddr)
394	DB *dbp;
395	db_pgno_t pgnoaddr;
396{
397	DB_MPOOLFILE *mpf;
398	ENV *env;
399	MPFARRAY *array;
400	QUEUE *qp;
401	u_int32_t extid, offset;
402	int ret;
403
404	qp = (QUEUE *)dbp->q_internal;
405	env = dbp->env;
406	ret = 0;
407
408	MUTEX_LOCK(env, dbp->mutex);
409
410	extid = QAM_PAGE_EXTENT(dbp, pgnoaddr);
411	array = &qp->array1;
412	if (array->low_extent > extid || array->hi_extent < extid)
413		array = &qp->array2;
414	offset = extid - array->low_extent;
415
416	DB_ASSERT(env,
417	    extid >= array->low_extent && offset < array->n_extent);
418
419	mpf = array->mpfarray[offset].mpf;
420	/* This extent my already be marked for delete and closed. */
421	if (mpf == NULL)
422		goto err;
423
424	/*
425	 * The log must be flushed before the file is deleted.  We depend on
426	 * the log record of the last delete to recreate the file if we crash.
427	 */
428	if (LOGGING_ON(env) && (ret = __log_flush(env, NULL)) != 0)
429		goto err;
430
431	(void)__memp_set_flags(mpf, DB_MPOOL_UNLINK, 1);
432	/* Someone could be real slow, let them close it down. */
433	if (array->mpfarray[offset].pinref != 0)
434		goto err;
435	array->mpfarray[offset].mpf = NULL;
436	if ((ret = __memp_fclose(mpf, 0)) != 0)
437		goto err;
438
439	/*
440	 * If the file is at the bottom of the array
441	 * shift things down and adjust the end points.
442	 */
443	if (offset == 0) {
444		memmove(array->mpfarray, &array->mpfarray[1],
445		    (array->hi_extent - array->low_extent)
446		    * sizeof(array->mpfarray[0]));
447		array->mpfarray[
448		    array->hi_extent - array->low_extent].mpf = NULL;
449		if (array->low_extent != array->hi_extent)
450			array->low_extent++;
451	} else {
452		if (extid == array->hi_extent)
453			array->hi_extent--;
454	}
455
456err:	MUTEX_UNLOCK(env, dbp->mutex);
457
458	return (ret);
459}
460
461/*
462 * __qam_sync --
463 *	Flush the database cache.
464 *
465 * PUBLIC: int __qam_sync __P((DB *));
466 */
467int
468__qam_sync(dbp)
469	DB *dbp;
470{
471	int ret;
472	/*
473	 * We can't easily identify the extent files associated with a specific
474	 * Queue file, so flush all Queue extent files.
475	 */
476	if ((ret = __memp_fsync(dbp->mpf)) != 0)
477		return (ret);
478	if (((QUEUE *)dbp->q_internal)->page_ext != 0)
479		return (__memp_sync_int(
480		    dbp->env, NULL, 0, DB_SYNC_QUEUE_EXTENT, NULL, NULL));
481	return (0);
482}
483
484/*
485 * __qam_gen_filelist -- generate a list of extent files.
486 *	Another thread may close the handle so this should only
487 *	be used single threaded or with care.
488 *
489 * PUBLIC: int __qam_gen_filelist __P((DB *,
490 * PUBLIC:      DB_THREAD_INFO *, QUEUE_FILELIST **));
491 */
492int
493__qam_gen_filelist(dbp, ip, filelistp)
494	DB *dbp;
495	DB_THREAD_INFO *ip;
496	QUEUE_FILELIST **filelistp;
497{
498	DBC *dbc;
499	DB_MPOOLFILE *mpf;
500	ENV *env;
501	QMETA *meta;
502	QUEUE *qp;
503	size_t extent_cnt;
504	db_recno_t i, current, first, stop, rec_extent;
505	QUEUE_FILELIST *fp;
506	int ret;
507
508	env = dbp->env;
509	mpf = dbp->mpf;
510	qp = (QUEUE *)dbp->q_internal;
511	*filelistp = NULL;
512
513	if (qp->page_ext == 0)
514		return (0);
515
516	/* This may happen during metapage recovery. */
517	if (qp->name == NULL)
518		return (0);
519
520	/* Find out the first and last record numbers in the database. */
521	i = PGNO_BASE_MD;
522	if ((ret = __memp_fget(mpf, &i, ip, NULL, 0, &meta)) != 0)
523		return (ret);
524
525	current = meta->cur_recno;
526	first = meta->first_recno;
527
528	if ((ret = __memp_fput(mpf, ip, meta, dbp->priority)) != 0)
529		return (ret);
530
531	/*
532	 * Allocate the extent array.  Calculate the worst case number of
533	 * pages and convert that to a count of extents.   The count of
534	 * extents has 3 or 4 extra slots:
535	 *   roundoff at first (e.g., current record in extent);
536	 *   roundoff at current (e.g., first record in extent);
537	 *   NULL termination; and
538	 *   UINT32_MAX wraparound (the last extent can be small).
539	 */
540	rec_extent = qp->rec_page * qp->page_ext;
541	if (current >= first)
542		extent_cnt = (current - first) / rec_extent + 3;
543	else
544		extent_cnt =
545		    (current + (UINT32_MAX - first)) / rec_extent + 4;
546
547	if (extent_cnt == 0)
548		return (0);
549	if ((ret = __os_calloc(env,
550	    extent_cnt, sizeof(QUEUE_FILELIST), filelistp)) != 0)
551		return (ret);
552	fp = *filelistp;
553	if ((ret = __db_cursor(dbp, ip, NULL, &dbc, 0)) != 0)
554		return (ret);
555
556again:
557	if (current >= first)
558		stop = current;
559	else
560		stop = UINT32_MAX;
561
562	/*
563	 * Make sure that first is at the same offset in the extent as stop.
564	 * This guarantees that the stop will be reached in the loop below,
565	 * even if it is the only record in its extent.  This calculation is
566	 * safe because first won't move out of its extent.
567	 */
568	first -= first % rec_extent;
569	first += stop % rec_extent;
570
571	for (i = first; i >= first && i <= stop; i += rec_extent) {
572		if ((ret = __qam_fprobe(dbc, QAM_RECNO_PAGE(dbp, i),
573		    &fp->mpf, QAM_PROBE_MPF, dbp->priority, 0)) != 0) {
574			if (ret == ENOENT)
575				continue;
576			goto err;
577		}
578		fp->id = QAM_RECNO_EXTENT(dbp, i);
579		fp++;
580		DB_ASSERT(env, (size_t)(fp - *filelistp) < extent_cnt);
581	}
582
583	if (current < first) {
584		first = 1;
585		goto again;
586	}
587
588err:	(void)__dbc_close(dbc);
589	return (ret);
590}
591
592/*
593 * __qam_extent_names -- generate a list of extent files names.
594 *
595 * PUBLIC: int __qam_extent_names __P((ENV *, char *, char ***));
596 */
597int
598__qam_extent_names(env, name, namelistp)
599	ENV *env;
600	char *name;
601	char ***namelistp;
602{
603	DB *dbp;
604	DB_THREAD_INFO *ip;
605	QUEUE *qp;
606	QUEUE_FILELIST *filelist, *fp;
607	size_t len;
608	int cnt, ret, t_ret;
609	char buf[DB_MAXPATHLEN], **cp, *freep;
610
611	*namelistp = NULL;
612	filelist = NULL;
613	ENV_GET_THREAD_INFO(env, ip);
614	if ((ret = __db_create_internal(&dbp, env, 0)) != 0)
615		return (ret);
616	if ((ret = __db_open(dbp, ip,
617	    NULL, name, NULL, DB_QUEUE, DB_RDONLY, 0, PGNO_BASE_MD)) != 0)
618		goto done;
619	qp = dbp->q_internal;
620	if (qp->page_ext == 0)
621		goto done;
622
623	if ((ret = __qam_gen_filelist(dbp, ip, &filelist)) != 0)
624		goto done;
625
626	if (filelist == NULL)
627		goto done;
628
629	cnt = 0;
630	for (fp = filelist; fp->mpf != NULL; fp++)
631		cnt++;
632
633	/* QUEUE_EXTENT contains extra chars, but add 6 anyway for the int. */
634	len = (size_t)cnt * (sizeof(**namelistp) +
635	    strlen(QUEUE_EXTENT) + strlen(qp->dir) + strlen(qp->name) + 6);
636
637	if ((ret = __os_malloc(dbp->env, len, namelistp)) != 0)
638		goto done;
639	cp = *namelistp;
640	freep = (char *)(cp + cnt + 1);
641	for (fp = filelist; fp->mpf != NULL; fp++) {
642		QAM_EXNAME(qp, fp->id, buf, sizeof(buf));
643		len = strlen(buf);
644		*cp++ = freep;
645		(void)strcpy(freep, buf);
646		freep += len + 1;
647	}
648	*cp = NULL;
649
650done:
651	if (filelist != NULL)
652		__os_free(dbp->env, filelist);
653	if ((t_ret = __db_close(dbp, NULL, DB_NOSYNC)) != 0 && ret == 0)
654		ret = t_ret;
655
656	return (ret);
657}
658
659/*
660 * __qam_exid --
661 *	Generate a fileid for an extent based on the fileid of the main
662 * file.  Since we do not log schema creates/deletes explicitly, the log
663 * never captures the fileid of an extent file.  In order that masters and
664 * replicas have the same fileids (so they can explicitly delete them), we
665 * use computed fileids for the extent files of Queue files.
666 *
667 * An extent file id retains the low order 12 bytes of the file id and
668 * overwrites the dev/inode fields, placing a 0 in the inode field, and
669 * the extent number in the dev field.
670 *
671 * PUBLIC: void __qam_exid __P((DB *, u_int8_t *, u_int32_t));
672 */
673void
674__qam_exid(dbp, fidp, exnum)
675	DB *dbp;
676	u_int8_t *fidp;
677	u_int32_t exnum;
678{
679	int i;
680	u_int8_t *p;
681
682	/* Copy the fileid from the master. */
683	memcpy(fidp, dbp->fileid, DB_FILE_ID_LEN);
684
685	/* The first four bytes are the inode or the FileIndexLow; 0 it. */
686	for (i = sizeof(u_int32_t); i > 0; --i)
687		*fidp++ = 0;
688
689	/* The next four bytes are the dev/FileIndexHigh; insert the exnum . */
690	for (p = (u_int8_t *)&exnum, i = sizeof(u_int32_t); i > 0; --i)
691		*fidp++ = *p++;
692}
693
694/*
695 * __qam_nameop --
696 *	Remove or rename  extent files associated with a particular file.
697 * This is to remove or rename (both in mpool and the file system) any
698 * extent files associated with the given dbp.
699 * This is either called from the QUEUE remove or rename methods or
700 * when undoing a transaction that created the database.
701 *
702 * PUBLIC: int __qam_nameop __P((DB *, DB_TXN *, const char *, qam_name_op));
703 */
704int
705__qam_nameop(dbp, txn, newname, op)
706	DB *dbp;
707	DB_TXN *txn;
708	const char *newname;
709	qam_name_op op;
710{
711	ENV *env;
712	QUEUE *qp;
713	size_t exlen, fulllen, len;
714	u_int8_t fid[DB_FILE_ID_LEN];
715	u_int32_t exid;
716	int cnt, i, ret, t_ret;
717	char buf[DB_MAXPATHLEN], nbuf[DB_MAXPATHLEN], sepsave;
718	char *endname, *endpath, *exname, *fullname, **names;
719	char *ndir, *namep, *new, *cp;
720
721	env = dbp->env;
722	qp = (QUEUE *)dbp->q_internal;
723	cnt = ret = t_ret = 0;
724	namep = exname = fullname = NULL;
725	names = NULL;
726
727	/* If this isn't a queue with extents, we're done. */
728	if (qp->page_ext == 0)
729		return (0);
730
731	/*
732	 * Generate the list of all queue extents for this file (from the
733	 * file system) and then cycle through removing them and evicting
734	 * from mpool.  We have two modes of operation here.  If we are
735	 * undoing log operations, then do not write log records and try
736	 * to keep going even if we encounter failures in nameop.  If we
737	 * are in mainline code, then return as soon as we have a problem.
738	 * Memory allocation errors (__db_appname, __os_malloc) are always
739	 * considered failure.
740	 *
741	 * Set buf to : dir/__dbq.NAME.0 and fullname to HOME/dir/__dbq.NAME.0
742	 * or, in the case of an absolute path: /dir/__dbq.NAME.0
743	 */
744	QAM_EXNAME(qp, 0, buf, sizeof(buf));
745	if ((ret =
746	    __db_appname(env, DB_APP_DATA, buf, 0, NULL, &fullname)) != 0)
747		return (ret);
748
749	/* We should always have a path separator here. */
750	if ((endpath = __db_rpath(fullname)) == NULL) {
751		ret = EINVAL;
752		goto err;
753	}
754	sepsave = *endpath;
755	*endpath = '\0';
756
757	/*
758	 * Get the list of all names in the directory and restore the
759	 * path separator.
760	 */
761	if ((ret = __os_dirlist(env, fullname, 0, &names, &cnt)) != 0)
762		goto err;
763	*endpath = sepsave;
764
765	/* If there aren't any names, don't allocate any space. */
766	if (cnt == 0)
767		goto err;
768
769	/*
770	 * Now, make endpath reference the queue extent names upon which
771	 * we can match.  Then we set the end of the path to be the
772	 * beginning of the extent number, and we can compare the bytes
773	 * between endpath and endname (__dbq.NAME.).
774	 */
775	endpath++;
776	endname = strrchr(endpath, '.');
777	if (endname == NULL) {
778		ret = EINVAL;
779		goto err;
780	}
781	++endname;
782	*endname = '\0';
783	len = strlen(endpath);
784	fulllen = strlen(fullname);
785
786	/* Allocate space for a full extent name.  */
787	exlen = fulllen + 20;
788	if ((ret = __os_malloc(env, exlen, &exname)) != 0)
789		goto err;
790
791	ndir = new = NULL;
792	if (newname != NULL) {
793		if ((ret = __os_strdup(env, newname, &namep)) != 0)
794			goto err;
795		ndir = namep;
796		if ((new = __db_rpath(namep)) != NULL)
797			*new++ = '\0';
798		else {
799			new = namep;
800			ndir = PATH_DOT;
801		}
802	}
803	for (i = 0; i < cnt; i++) {
804		/* Check if this is a queue extent file. */
805		if (strncmp(names[i], endpath, len) != 0)
806			continue;
807		/* Make sure we have all numbers. foo.db vs. foo.db.0. */
808		for (cp = &names[i][len]; *cp != '\0'; cp++)
809			if (!isdigit((int)*cp))
810				break;
811		if (*cp != '\0')
812			continue;
813
814		/*
815		 * We have a queue extent file.  We need to generate its
816		 * name and its fileid.
817		 */
818		exid = (u_int32_t)strtoul(names[i] + len, NULL, 10);
819		__qam_exid(dbp, fid, exid);
820
821		switch (op) {
822		case QAM_NAME_DISCARD:
823			snprintf(exname, exlen,
824			     "%s%s", fullname, names[i] + len);
825			if ((t_ret = __memp_nameop(dbp->env,
826			    fid, NULL, exname, NULL,
827			    F_ISSET(dbp, DB_AM_INMEM))) != 0 && ret == 0)
828				ret = t_ret;
829			break;
830
831		case QAM_NAME_RENAME:
832			snprintf(nbuf, sizeof(nbuf), QUEUE_EXTENT,
833			     ndir, PATH_SEPARATOR[0], new, exid);
834			QAM_EXNAME(qp, exid, buf, sizeof(buf));
835			if ((ret = __fop_rename(env,
836			    txn, buf, nbuf, fid, DB_APP_DATA, 1,
837			    F_ISSET(dbp, DB_AM_NOT_DURABLE) ?
838			    DB_LOG_NOT_DURABLE : 0)) != 0)
839				goto err;
840			break;
841
842		case QAM_NAME_REMOVE:
843			QAM_EXNAME(qp, exid, buf, sizeof(buf));
844			if ((ret = __fop_remove(env, txn, fid, buf,
845			    DB_APP_DATA, F_ISSET(dbp, DB_AM_NOT_DURABLE) ?
846			    DB_LOG_NOT_DURABLE : 0)) != 0)
847				goto err;
848			break;
849		}
850	}
851
852err:	if (fullname != NULL)
853		__os_free(env, fullname);
854	if (exname != NULL)
855		__os_free(env, exname);
856	if (namep != NULL)
857		__os_free(env, namep);
858	if (names != NULL)
859		__os_dirfree(env, names, cnt);
860	return (ret);
861}
862