/*
 * Save the offsets we've reached for each disk to a file, so we can start
 * at that position next time the daemon is started.
 */
void
writeoffsets(struct disk *disks, const char *save_file) {
	FILE *fp;
	struct disk *dp;

	if ((fp = fopen(save_file, "w")) == NULL) {
		syslog(LOG_NOTICE, "open %s failed: %m", save_file);
		return;
	}

	if (debug)
		fprintf(stderr,
			"#\tposition/size\tdays\trate\terrors\tintvl\tnext\n");
	for (dp = disks; dp->device != NULL; dp++)
		if (strcmp(dp->device, "*") != 0) {
			fprintf(fp, "%s " QD "\n", dp->device,
			    (quad_t)dseek(dp, 0, SEEK_CUR));
			if (debug) {
				fprintf(stderr, "%s " QD "\n", dp->device,
					(quad_t)dseek(dp, 0, SEEK_CUR));
				fprintf(stderr,
					"#\t" QD "\t%d\t%d\t%d\t" QD "\t" QD
					"\n",
					(quad_t)dp->size, dp->days, dp->rate,
					dp->errors, dp->interval, dp->next);
			}
		}
	fclose(fp);
}
/*
 * Read the next chunk from the specified disk, retrying if necessary.
 */
void
readchunk(struct disk *dp, char *buf) {
	ssize_t n;
	int s;

	dp->next = dp->interval;
	n = read(dp->fd, buf, READ_SIZE);
	if (n == 0) {
		eof:
		syslog(LOG_INFO, "reached end of %s with %d errors",
		    dp->device, dp->errors);
		dseek(dp, 0, SEEK_SET);
		dp->errors = 0;
		return;
	} else if (n > 0)
		return;

	/*
	 * Read error, retry in smaller chunks.
	 */
	logreaderror(dp, READ_SIZE);

	for (s = 0; s < READ_SIZE; s += 512) {
		n = read(dp->fd, buf, 512);
		if (n == 0)
			goto eof;
		else if (n < 0) {
			/* log the error and seek past it. */
			logreaderror(dp, 512);
			dseek(dp, 512, SEEK_CUR);
		}
	}
}
Example #3
0
/* asqlDbfDseek()
 * append record
 *************************************************************************/
_declspec(dllexport) long asqlDbfDseek(TS_CLIENT_CURSOR *tscc, void *df, \
					long  recNo, \
					char  cFromWhere, \
					EXCHNG_BUF_INFO *exbuf )
{
    TS_COM_PROPS tscp;
    char  lpszResponse[4096];

    memset(&tscp, 0, sizeof(TS_COM_PROPS));

    while( tscc != NULL ) {
	if( tscc->p == (void *)df )
	    goto  agdr_dfTrueR;
	tscc = tscc->pNext;
    }
    df = NULL;
agdr_dfTrueR:

    tscp.packetType = 'R';
    tscp.msgType = 'E';
    tscp.len = 0;

    if( df == NULL ) {
	tscp.lp = -1;
	memcpy( lpszResponse, &tscp, sizeof(TS_COM_PROPS));
	SrvWriteExchngBuf(exbuf, lpszResponse, sizeof(TS_COM_PROPS));

	return  -1;
    }

    switch( cFromWhere ) {
	case 'E':
		dseek((dFILE *)df, recNo, dSEEK_END);
		break;
	case 'C':
		dseek((dFILE *)df, recNo, dSEEK_CUR);
		break;
	//case 'S':
	default:
		dseek((dFILE *)df, recNo, dSEEK_SET);
		break;
    }
    tscp.msgType = 'D';
    tscp.lp = 0;

    memcpy(lpszResponse, &tscp, sizeof(TS_COM_PROPS));
    SrvWriteExchngBuf(exbuf, lpszResponse, sizeof(TS_COM_PROPS));

    return  0;

} //end of asqlDbfDseek()
Example #4
0
/* asqlGetDbfRec()
 *
 *************************************************************************/
_declspec(dllexport) long asqlGetDbfRec(TS_CLIENT_CURSOR *tscc, void *df, \
					long  recNo, \
					EXCHNG_BUF_INFO *exbuf )
{
    RECPROPS recprops;
    TS_COM_PROPS tscp;
    char  lpszResponse[4096];

    memset(&tscp, 0, sizeof(TS_COM_PROPS));

    while( tscc != NULL ) {
	if( tscc->p == (void *)df )
	    goto  agdr_dfTrue;
	tscc = tscc->pNext;
    }
    df = NULL;
agdr_dfTrue:

    if( df == NULL || recNo <= 0  ) {
	tscp.packetType = 'R';
	tscp.msgType = 'E';
	tscp.len = 0;
	tscp.lp = -1;
	memcpy(lpszResponse, &tscp, sizeof(TS_COM_PROPS));

	SrvWriteExchngBuf(exbuf, lpszResponse, 4096);
	return  -1;
    }

    //to consider the view  2000.6.24
    //((dFILE *)df)->rec_p = recNo;
    dseek((dFILE *)df, recNo-1, dSEEK_SET);
 
    if( get1rec((dFILE *)df) == NULL ) {
	tscp.packetType = 'R';
	tscp.msgType = 'E';
	tscp.len = 0;
	tscp.lp = -2;
	memcpy(lpszResponse, &tscp, sizeof(TS_COM_PROPS));
	SrvWriteExchngBuf(exbuf, lpszResponse, 4096);

	return  -2;
    }

    tscp.packetType = 'R';
    tscp.msgType = 'D';
    tscp.len = sizeof(RECPROPS)+((dFILE *)df)->rec_len;
    tscp.lp = 0;

    memcpy(lpszResponse, &tscp, sizeof(TS_COM_PROPS));
    recprops.iPhyRecNum = ((dFILE *)df)->rec_p;
    recprops.bDeleteFlag = ((dFILE *)df)->rec_buf[0] - ' ';
    memcpy(&lpszResponse[sizeof(TS_COM_PROPS)], &recprops, sizeof(RECPROPS));

    memcpy(&lpszResponse[sizeof(TS_COM_PROPS)+sizeof(RECPROPS)], ((dFILE *)df)->rec_buf, ((dFILE *)df)->rec_len);
    SrvWriteExchngBuf(exbuf, lpszResponse, sizeof(TS_COM_PROPS)+tscp.len);

    return  0;

} //end of asqlGetDbfRec()
/*
 * Read the offsets written by writeoffsets().
 */
void
readoffsets(struct disk *disks, const char *save_file) {
	FILE *fp;
	struct disk *dp;
	char *space, buf[1024];

	if ((fp = fopen(save_file, "r")) == NULL) {
		if (debug > 1 && errno == ENOENT)
			fprintf(stderr, "open %s failed: %s [continuing]\n",
				save_file, strerror(errno));
		if (errno != ENOENT)
			syslog(LOG_NOTICE, "open %s failed: %m", save_file);
		return;
	}

	while (fgets(buf, sizeof buf, fp) != NULL) {
		if ((space = strchr(buf, ' ')) == NULL)
			continue;
		*space = '\0';

		for (dp = disks;
		    dp->device != NULL && strcmp(dp->device, buf) != 0; dp++)
			; /* nothing */

		if (dp->device != NULL) {
			if (debug)
				fprintf(stderr, "%s: seek %s", buf, space + 1);
			dseek(dp, (off_t)strtoq(space + 1, NULL, 0), SEEK_SET);
		}
	}
	fclose(fp);
	if (debug)
		fprintf(stderr, "readoffsets: done\n");
}
Example #6
0
/* asqlAppDbfRec()
 * append record
 *************************************************************************/
_declspec(dllexport) long asqlAppDbfRec(TS_CLIENT_CURSOR *tscc, void *df, \
					LPSTR sRecBuf, \
					EXCHNG_BUF_INFO *exbuf )
{
    TS_COM_PROPS tscp;
    char  lpszResponse[4096];

    memset(&tscp, 0, sizeof(TS_COM_PROPS));

    while( tscc != NULL ) {
	if( tscc->p == (void *)df )
            goto  agdr_dfTrueR;
	tscc = tscc->pNext;
    }
    df = NULL;
agdr_dfTrueR:

    tscp.packetType = 'R';
    tscp.msgType = 'E';
    tscp.len = 0;

    if( df == NULL ) {
	tscp.lp = -1;
	memcpy( lpszResponse, &tscp, sizeof(TS_COM_PROPS));
	SrvWriteExchngBuf(exbuf, lpszResponse, sizeof(TS_COM_PROPS));

	return  -1;
    }

    memcpy(((dFILE *)df)->rec_buf, sRecBuf, ((dFILE *)df)->rec_len);

    //lock it to avoid other thread move to the end for appending
    wmtDbfLock((dFILE *)df);
    dseek((dFILE *)df, 0, dSEEK_END);

    //1999.12.21
    absSyncDfBh( (dFILE *)df );

    if( put1rec( (dFILE *)df ) == NULL ) {
	//error
	tscp.lp = -4;
    } else {
	tscp.msgType = 'D';
	tscp.lp = 0;
    }
    wmtDbfUnLock((dFILE *)df);

    memcpy(lpszResponse, &tscp, sizeof(TS_COM_PROPS));
    SrvWriteExchngBuf(exbuf, lpszResponse, sizeof(TS_COM_PROPS));

    return  0;

} //end of asqlAppDbfRec()
Example #7
0
/* asqlPutDbfRec()
 *
 *************************************************************************/
_declspec(dllexport) long asqlPutDbfRec(TS_CLIENT_CURSOR *tscc, void *df, \
					long  recNo, \
					LPSTR recBuf, \
					EXCHNG_BUF_INFO *exbuf )
{
    TS_COM_PROPS tscp;
    char  lpszResponse[4096];

    memset(&tscp, 0, sizeof(TS_COM_PROPS));

    while( tscc != NULL ) {
	if( tscc->p == (void *)df )
	    goto  agdr_dfTrueR;
	tscc = tscc->pNext;
    }
    df = NULL;
agdr_dfTrueR:

    tscp.packetType = 'R';
    tscp.msgType = 'E';
    tscp.len = 0;

    if( df == NULL || recNo <= 0 ) {
	tscp.lp = -1;
	memcpy(lpszResponse, &tscp, sizeof(TS_COM_PROPS));
	SrvWriteExchngBuf(exbuf, lpszResponse, 4096);

	return  -1;
    }

    memcpy(((dFILE *)df)->rec_buf, recBuf, ((dFILE *)df)->rec_len);

    //((dFILE *)df)->rec_p = recNo;
    dseek((dFILE *)df, recNo-1, dSEEK_SET);

    //1999.12.21
    absSyncDfBh( (dFILE *)df );

    if( put1rec( (dFILE *)df ) == NULL ) {
	//error
	tscp.lp = ((dFILE *)df)->error;
    } else {
	tscp.msgType = 'D';
	tscp.lp = 0;
    }
    memcpy(lpszResponse, &tscp, sizeof(TS_COM_PROPS));
    SrvWriteExchngBuf(exbuf, lpszResponse, sizeof(TS_COM_PROPS));

    return  0;

} //end of asqlPutDbfRec()
/*
 * Set the process title so it's easy to see using ps(1) how much has been
 * done.
 */
void
updateproctitle(struct disk *disks) {
	struct disk *dp;
	char *bp, *p;
	static char *buf;
	static size_t bufsize;
	size_t size;
	int inc, ret;
	double percent;

	bp = buf;
	size = bufsize;
	for (dp = disks; dp->device != NULL; dp++) {
		p = dp->device;
		if (strcmp(p, "*") == 0)
			continue;
		if (strncmp(p, _PATH_DEV, sizeof _PATH_DEV - 1) == 0)
			p += sizeof _PATH_DEV - 1;

		percent = 100 * (double)dseek(dp, 0, SEEK_CUR) / dp->size;
		if ((ret = snprintf(bp, size, "%s %.2f%%, ", p, percent)) < 0)
			ret = 0;
		if ((size_t)ret >= size) {
			inc = ((ret + 1023) >> 10) << 10;
			size += inc;
			bufsize += inc;
			if ((buf = reallocf(buf, bufsize)) == NULL) {
				/* Not fatal. */
				syslog(LOG_NOTICE, "reallocf failure: %m");
				bufsize = 0;
				return;
			}
			bp = buf + bufsize - size;
			ret = snprintf(bp, size, "%s %.2f%%, ", p, percent);
			if (ret < 0)
				ret = 0;
		}
		bp += ret;
		size -= ret;
	}
Example #9
0
//#pragma argsused
short PD_style _ASK_AppRec( OpndType *lpOpnd, short ParaNum, short *OpndTop, \
		short *CurState )
{
    dFILE *tdf;
    int    i;
extern WSToMT FromToStru fFrTo;

    if( *CurState == 0 || *CurState == LASTWORKTIMEOFACTION )
    {
	*OpndTop -= ParaNum;
	return  0;
    }

    if( xIsOpndField(&lpOpnd[0]) == 0 )
    {
	i = xGetOpndLong(&lpOpnd[0]) - 1;
	if( i < 0 || i >= fFrTo.cSouDbfNum ) {
		ErrorSet.xERROR = iRefTabelErr;
		sprintf(ErrorSet.string, "apprec(%d)", i);
		return  1;
	}

	tdf = fFrTo.cSouFName[i];
    } else {

	tdf = ((dFIELDWHENACTION *)lpOpnd[0].oval)->pTargetDfile;
	if( tdf == NULL )       return  1;
    }

#ifdef WSToMT
    //lock it to avoid other thread move to the end for appending
    wmtDbfLock(tdf);
#endif
    dseek(tdf, 0, dSEEK_END);

    if( fFrTo.phuf != NULL )
    {
	tdf->rec_buf[0] = '*';
	if( put1rec( tdf ) == NULL ) {

	    wmtDbfUnLock(tdf);

	    ErrorSet.xERROR = iFailFunCall;
	    sprintf(ErrorSet.string, "calrec(%d)", tdf->error);
	    return  1;
	}

	wmtDbfUnLock(tdf);

	tdf->rec_buf[0] = ' ';

	fseek(fFrTo.phuf, -4, SEEK_CUR);

	fwrite(&tdf, sizeof(dFILE *), 1, fFrTo.phuf);
	fwrite(&(tdf->rec_p), sizeof(long), 1, fFrTo.phuf);
	fwrite(tdf->rec_buf, tdf->rec_len, 1, fFrTo.phuf);

	fwrite("\0\0\0\0", sizeof(dFILE *), 1, fFrTo.phuf);
    } else {
	if( put1rec( tdf ) == NULL ) {

	    wmtDbfUnLock(tdf);

	    ErrorSet.xERROR = iFailWriteRec;
	    sprintf(ErrorSet.string, "calrec(%d)", tdf->error);
	    return  1;
	}
    }

#ifdef WSToMT
    wmtDbfUnLock(tdf);
#endif

    *OpndTop -= ParaNum;    /* maintain the opnd stack */

    return( 0 );

} /* end of function _ASK_AppRec() */
Example #10
0
/* asqlBlobMemFetch()
 *
 *************************************************************************/
_declspec(dllexport) long asqlBlobMemFetch(TS_CLIENT_CURSOR *tscc, \
					LPCSTR lpszUser, \
					LPCSTR lpszUserDir,\
					void *df, \
					long recNo, \
					LPSTR fieldName, \
					EXCHNG_BUF_INFO *exbuf )
{
    TS_COM_PROPS tscp;
    unsigned short iw;
    char  	   buf[4096];
    char           *sp;
    long           memSize;

    memset(&tscp, 0, sizeof(TS_COM_PROPS));

    while( tscc != NULL ) {
	if( tscc->p == (void *)df )
	    goto  agdr_dfTrueR;
	tscc = tscc->pNext;
    }

    df = NULL;
agdr_dfTrueR:

    tscp.packetType = 'R';
    tscp.msgType = 'E';
    tscp.len = 0;

    if( df == NULL || recNo <= 0 ) {
	tscp.lp = -1;
	SrvWriteExchngBuf(exbuf, &tscp, sizeof(TS_COM_PROPS));

	return  -1;
    }

    iw = GetFldid((dFILE *)df, fieldName);
    if( iw == 0xFFFF ) {
	tscp.lp = -4;
	SrvWriteExchngBuf(exbuf, &tscp, sizeof(TS_COM_PROPS));
	return  -4;
    }

    //((dFILE *)df)->rec_p = recNo;
    dseek((dFILE *)df, recNo-1, dSEEK_SET);

    if( get1rec((dFILE *)df) == NULL ) {
	tscp.lp = -4;
	SrvWriteExchngBuf(exbuf, &tscp, sizeof(TS_COM_PROPS));
	return  -4;
    }

    sp = blobToMem((dFILE *)df, iw, &memSize);
    if( sp != NULL ) {
	TS_COM_PROPS tscp1;
	//
	//tscp.leftPacket = '\1';	//no use: there are any other packet to transmit
	//tscp.endPacket = '\1';  //not end, DONNOT GIVE UP
	tscp1.packetType = 'R';
	tscp1.msgType = 'B';
	//tscp.lp = 0;
	SrvWriteExchngBufEx(exbuf, &tscp1, sp, memSize, 0);

	//come from readBtreeData(), free it with its function
	//free(sp);
	freeBlobMem((dFILE *)df, sp);

	//send a 'D' packet
	tscp.msgType = 'D';
	tscp.lp = 0;
	SrvWriteExchngBuf(exbuf, &tscp, sizeof(TS_COM_PROPS));

	return  0;
    }

    tscp.lp = -5;
    memcpy(buf, &tscp, sizeof(TS_COM_PROPS));
    SrvWriteExchngBuf(exbuf, buf, sizeof(TS_COM_PROPS));
    return  -5;

} //end of asqlBlobMemFetch()
Example #11
0
/* asqlBlobPut()
 *
 *************************************************************************/
_declspec(dllexport) long asqlBlobPut(TS_CLIENT_CURSOR *tscc,
					LPCSTR lpszUser, \
					LPCSTR lpszUserDir,\
					void *df, \
					long recNo, \
					LPSTR fieldName, \
					EXCHNG_BUF_INFO *exbuf )
{
    TS_COM_PROPS tscp;
    unsigned short iw;
    char  buf[4096];
    char  szCn[128];
//    int   i;

    memset(&tscp, 0, sizeof(TS_COM_PROPS));

    while( tscc != NULL ) {
	if( tscc->p == (void *)df )
	    goto  agdr_dfTrueR;
	tscc = tscc->pNext;
    }
    df = NULL;
agdr_dfTrueR:

    tscp.packetType = 'R';
    tscp.msgType = 'E';
    tscp.len = 0;

    if( df == NULL || recNo <= 0 ) {
	tscp.lp = -1;
	memcpy(buf, &tscp, sizeof(TS_COM_PROPS));
	SrvWriteExchngBuf(exbuf, buf, sizeof(TS_COM_PROPS));

	return  -1;
    }

    iw = GetFldid((dFILE *)df, fieldName);
    if( iw == 0xFFFF ) {
	tscp.lp = -4;
	memcpy(buf, &tscp, sizeof(TS_COM_PROPS));
	SrvWriteExchngBuf(exbuf, buf, sizeof(TS_COM_PROPS));
	return  -4;
    }

    if( lServerAsRunning ) {
	/*i = 32;
	GetComputerName(szCn, &i);
	i = MAXPATH;
	if( GetUserHomeDir(szCn, lpszUser, buf, &i) != 0 )
	{
	    tscp.packetType = 'R';
	    tscp.msgType = 'E';
	    tscp.len = 4096-sizeof(TS_COM_PROPS);
	    tscp.lp = 0;
	    memcpy(buf, &tscp, sizeof(TS_COM_PROPS));
	    strcpy(&buf[sizeof(TS_COM_PROPS)], "ERR:4 INF:No Thread service");
	    SrvWriteExchngBuf(exbuf, buf, 4096);
	    return  4;
	}*/
	strZcpy(buf, lpszUserDir, MAXPATH);
	beSurePath(buf);
	sprintf(szCn, "%s.DTM", fieldName);
	makefilename(buf, buf, szCn);
    } else {
	sprintf(buf, "%s.DTM", fieldName);
    }

    wmtDbfLock((dFILE *)df);

    //((dFILE *)df)->rec_p = recNo;
    dseek((dFILE *)df, recNo-1, dSEEK_SET);

    if( get1rec((dFILE *)df) == NULL ) {
	wmtDbfUnLock((dFILE *)df);
	tscp.lp = -4;
	memcpy(buf, &tscp, sizeof(TS_COM_PROPS));
	SrvWriteExchngBuf(exbuf, buf, 4096);
	return  -4;
    }

    if( (tscp.lp=dbtFromFile((dFILE *)df, iw, buf)) >= 0 ) {
	tscp.msgType = 'D';
    }
    wmtDbfUnLock((dFILE *)df);

    memcpy(buf, &tscp, sizeof(TS_COM_PROPS));
    SrvWriteExchngBuf(exbuf, buf, tscp.len+sizeof(TS_COM_PROPS));

    return  0;

} //end of asqlBlobPut()
Example #12
0
/* asqlBlobMemPut()
 *
 *************************************************************************/
_declspec(dllexport) long asqlBlobMemPut(TS_CLIENT_CURSOR *tscc, \
					LPCSTR lpszUser, \
					LPCSTR lpszUserDir,\
					void *df, \
					long recNo, \
					LPSTR fieldName, \
					EXCHNG_BUF_INFO *exbuf )
{
    TS_COM_PROPS tscp;
    unsigned short iw;
    char           *sp;
    long           memSize;
    BOOL 	   pbIfEnd;

    memset(&tscp, 0, sizeof(TS_COM_PROPS));

    while( tscc != NULL ) {
	if( tscc->p == (void *)df )
	    goto  agdr_dfTrueR;
	tscc = tscc->pNext;
    }
	df = NULL;
agdr_dfTrueR:

    if( SrvReadExchngBufEx(exbuf, &sp, &memSize, &pbIfEnd) != 0 ) {
	tscp.lp = -5;
	SrvWriteExchngBuf(exbuf, &tscp, sizeof(TS_COM_PROPS));
	return  -5;
    }

    tscp.packetType = 'R';
    tscp.msgType = 'E';
    tscp.len = 0;

    if( df == NULL || recNo <= 0 ) {
	HANDLE hDataBuf;

	hDataBuf = LocalHandle(sp);
	LocalUnlock( hDataBuf );
	LocalFree( hDataBuf );

	tscp.lp = -1;
	SrvWriteExchngBuf(exbuf, &tscp, sizeof(TS_COM_PROPS));

	return  -1;
    }

    iw = GetFldid((dFILE *)df, fieldName);
    if( iw == 0xFFFF ) {
	HANDLE hDataBuf;

	hDataBuf = LocalHandle(sp);
	LocalUnlock( hDataBuf );
	LocalFree( hDataBuf );

	tscp.lp = -4;
	SrvWriteExchngBuf(exbuf, &tscp, sizeof(TS_COM_PROPS));
	return  -4;
    }

    //((dFILE *)df)->rec_p = recNo;
    dseek((dFILE *)df, recNo-1, dSEEK_SET);

    if( get1rec((dFILE *)df) == NULL ) {
	HANDLE hDataBuf;

	hDataBuf = LocalHandle(sp);
	LocalUnlock( hDataBuf );
	LocalFree( hDataBuf );

	tscp.lp = -4;
	SrvWriteExchngBuf(exbuf, &tscp, sizeof(TS_COM_PROPS));
	return  -4;
    }

    //2000.7.21
    /*
    if( memSize < 0 ) {
	
    } else {
	blobFromMem((dFILE *)df, iw, sp, memSize);
    }*/
  
    if( memSize >= 0 ) {
	blobFromMem((dFILE *)df, iw, sp, memSize);
    }


    { ///////////////////////
	HANDLE hDataBuf;

	hDataBuf = LocalHandle(sp);
	LocalUnlock( hDataBuf );
	LocalFree( hDataBuf );
    }

    tscp.msgType = 'D';
    tscp.lp = 0;
    SrvWriteExchngBuf(exbuf, &tscp, sizeof(TS_COM_PROPS));

    return  0;

} //end of asqlBlobMemPut()
Example #13
0
/*
 * Report a read error, logging how many bytes were trying to be read, which
 * sector they were being read from, and try to also find out what that sector
 * is used for.
 */
void
logreaderror(struct disk *dp, int nbytes) {
	quad_t secno;
	off_t saved_offset;
	char newdev[512];

#ifdef	DIOCGDINFO
	int fd, slice, part;
	struct dos_partition *dos;
	struct disklabel label;
	char buf[512];
#else	/* DIOCGDINFO */
	static size_t geomSize = 0;
	static char * geomConf = NULL;
	char * cp;
	static size_t partLen = 31;
	static char *part = NULL;
	quad_t relsec = -1;
	char *typefld = NULL;
#endif	/* DIOCGDINFO */

	saved_offset = dseek(dp, 0, SEEK_CUR);
	secno = (quad_t)saved_offset / dp->secsize;
	dp->errors++;

	syslog(LOG_NOTICE, "error reading %d bytes from sector " QD " on %s",
	    nbytes, secno, dp->device);

#ifdef	DIOCGDINFO
	/*
	 * First, find out which slice it's in.  To do this, we seek to the
	 * start of the disk, read the first sector, and go through the DOS
	 * slice table.
	 */
	if (dseek(dp, 0, SEEK_SET) == -1) {
		syslog(LOG_NOTICE, "could not seek to start of disk: %m");
		exit(EXIT_FAILURE);
	}

	if (read(dp->fd, buf, sizeof buf) != sizeof buf) {
		dseek(dp, saved_offset, SEEK_SET);
		return;
	}

	/* seek back to where we were */
	if (dseek(dp, saved_offset, SEEK_SET) == -1) {
		syslog(LOG_NOTICE,
		       "could not seek to previous position"
		       "after reading first sector: %m");
		exit(EXIT_FAILURE);
	}

	// TODO:  should validate DOS partition table (vs GPT or dedicated)
	dos = (struct dos_partition *)&buf[DOSPARTOFF];
	for (slice = 0; slice < NDOSPART; slice++)
		if (dos[slice].dp_start <= secno &&
		  secno < dos[slice].dp_start + dos[slice].dp_size)
			break;

	if (slice == NDOSPART) {
		syslog(LOG_NOTICE,
		  "sector " QD " on %s doesn't appear "
		  "to be within any DOS slice", secno, dp->device);
		return;
	}

	/* Make secno relative to this slice */
	secno -= dos[slice].dp_start;

	snprintf(newdev, sizeof newdev, "%ss%d", dp->device, slice + 1);
	syslog(LOG_DEBUG, "bad sector seems to be within %s", newdev);

	/* Check the type of that partition. */
	if (dos[slice].dp_typ != DOSPTYP_386BSD) {
		/* If not a BSD slice, we can't do much more. */
		syslog(LOG_NOTICE, "last bad sector is sector " QD
		    " on device %s, type %02x", secno, newdev,
		    dos[slice].dp_typ);
		return;
	}

	if ((fd = open(newdev, O_RDONLY)) < 0) {
		syslog(LOG_NOTICE, "open %s failure: %m", newdev);
		return;
	}

	/* Try to read the disklabel from that device. */
	if (ioctl(fd, DIOCGDINFO, &label) < 0) {
		syslog(LOG_NOTICE, "DIOCGDINFO on %s failed: %m",
		    newdev);
		return;
	}

	/* Check which partition this sector is in. */
	for (part = 0; part < MAXPARTITIONS; part++)
		if (part != 2 && /* skip 'c' partition */
		    label.d_partitions[part].p_offset <= secno &&
		    secno < label.d_partitions[part].p_offset +
		            label.d_partitions[part].p_size)
			break;

	if (part == MAXPARTITIONS) {
		syslog(LOG_NOTICE,
		  "sector " QD " on %s doesn't appear "
		  "to be within any BSD partition", secno, newdev);
		return;
	}

	secno -= label.d_partitions[part].p_offset;
	snprintf(newdev, sizeof newdev, "%ss%d%c",
	    dp->device, slice + 1, 'a' + part);
	syslog(LOG_DEBUG, "bad sector seems to be within %s", newdev);
	if (label.d_partitions[part].p_fstype != FS_BSDFFS) {
		/* Again, if not a BSD partition, can't do much. */
		syslog(LOG_NOTICE, "last bad sector is sector " QD
		    " on device %s, type %s", secno, newdev,
		    fstypename(label.d_partitions[part].p_fstype));
		return;
	}
#else	/* DIOCGDINFO */
	if (geomSize == 0) {
		sysctlbyname("kern.geom.conftxt", NULL, &geomSize, NULL, 0);
		if (geomSize <= 0) {
			printf("sysctlbyname() returned size = %d\n", geomSize);
			geomSize = 0;
			exit(EXIT_FAILURE);
		}
		geomConf = malloc(geomSize);
		if (geomConf == NULL) {
			printf("malloc(%d) returned NULL\n", geomSize);
			geomSize = 0;
			exit(EXIT_FAILURE);
		}
		if (sysctlbyname("kern.geom.conftxt", geomConf, &geomSize,
				 NULL, 0) != 0) {
			perror("sysctlbyname()");
			geomSize = 0;
			free(geomConf);
			geomConf = NULL;
			exit(EXIT_FAILURE);
		}
	}
	if (part == NULL)
		part = malloc(partLen + 1);

	for ( cp = geomConf ; *cp ; ++cp ) {
		// find line "0 DISK " matching current disk, using
		// strncmp because where cp points is not null-terminated
		// (All DISK lines, and only DISK lines, are at level 0.)
		// Magic numbers:  7 == strlen("0 DISK "); 5 == strlen("/dev/")
		if (strncmp(cp, "0 DISK ", 7) == 0
		 && strncmp(&cp[7], &dp->device[5], strlen(dp->device) - 5) == 0
		 && cp[7 + strlen(dp->device) - 5] == ' ') {
			for (;;) {
				// scan to end of line
				while (cp[1] && *cp != '\n')
					++cp;
				++cp;	// start of next line
				// Find PART line containing the failed sector;
				// must be on same disk => stop upon finding
				// another DISK line.  If more than one matching
				// PART -- due to nested geoms -- use the last
				// (innermost).
				if (*cp == '\0' || *cp == '0')
					break;	// end of current DISK's entries
				// scan to end of level number
				while (cp[1] && *cp != ' ')
					++cp;
				if (strncmp(cp, " PART ", 6) == 0) {
					char *pp = &cp[6];
					int pl = strchr(pp, ' ') - pp;
					quad_t mediasize, offset;
					long secsize;

					cp = pp + pl;	// cp -> mediasize
					mediasize = strtoq(cp, &cp, 10);
					secsize = strtol(cp, &cp, 10);
					mediasize /= secsize;
					cp = strchr(cp, 'o') + 1;
					offset = strtoq(cp, &cp, 10) / secsize;
					if (secno >= offset
					 && (secno - offset) < mediasize) {
						if (pl > partLen) {
							part = realloc(part,
									pl+1);
							partLen = pl;
						}
						strncpy(part, pp, pl);
						part[pl] = '\0';
						relsec = secno - offset;
						typefld = cp + 1;
					}
				}
			}
		}
		// scan to end of line
		while (cp[1] && *cp != '\n')
			++cp;
	}
////	printf("part %s, relsec %qd, typefld %p: %.27s\n",
////		part, relsec, typefld, typefld);
	secno = relsec;
	strncpy(newdev, part, sizeof(newdev) - 1);
	newdev[sizeof(newdev) - 1] = '\0';		// paranoia
#endif	/* DIOCGDINFO */

	syslog(LOG_NOTICE, "last bad sector is sector " QD
	  " on 4.2BSD filesystem %s", secno, newdev);

	/*
	 * XXX: todo: find out which file on the BSD filesystem uses this
	 * sector...
	 */
}