smballoc.c 16.9 KB
Newer Older
1 2 3 4 5 6
/* Synchronet message base (SMB) alloc/free routines */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
7
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
 *																			*
 * This library is free software; you can redistribute it and/or			*
 * modify it under the terms of the GNU Lesser General Public License		*
 * as published by the Free Software Foundation; either version 2			*
 * of the License, or (at your option) any later version.					*
 * See the GNU Lesser General Public License for more details: lgpl.txt or	*
 * http://www.fsf.org/copyleft/lesser.html									*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

#include "smblib.h"
#include "genwrap.h"

/****************************************************************************/
/* Finds unused space in data file based on block allocation table and		*/
/* marks space as used in allocation table.                                 */
/* File must be opened read/write DENY ALL									*/
/* Returns offset to beginning of data (in bytes, not blocks)				*/
/* Assumes smb_open_da() has been called									*/
/* smb_close_da() should be called after									*/
/* Returns negative on error												*/
/****************************************************************************/
34
off_t smb_allocdat(smb_t* smb, off_t length, uint16_t refs)
35
{
deuce's avatar
64-bit  
deuce committed
36
    uint16_t  i;
37 38
	ulong	j,l,blocks;
	off_t offset=0;
39 40

	if(smb->sda_fp==NULL) {
41
		safe_snprintf(smb->last_error, sizeof(smb->last_error), "%s msgbase not open", __FUNCTION__);
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
		return(SMB_ERR_NOT_OPEN);
	}
	blocks=smb_datblocks(length);
	j=0;	/* j is consecutive unused block counter */
	fflush(smb->sda_fp);
	rewind(smb->sda_fp);
	while(!feof(smb->sda_fp) && (long)offset>=0) {
		if(smb_fread(smb,&i,sizeof(i),smb->sda_fp)!=sizeof(i))
			break;
		offset+=SDT_BLOCK_LEN;
		if(!i) j++;
		else   j=0;
		if(j==blocks) {
			offset-=(blocks*SDT_BLOCK_LEN);
			break; 
		} 
	}
	if((long)offset<0) {
60
		safe_snprintf(smb->last_error,sizeof(smb->last_error),"%s invalid data offset: %" PRIdOFF, __FUNCTION__, offset);
61 62 63
		return(SMB_ERR_DAT_OFFSET);
	}
	clearerr(smb->sda_fp);
64
	if(fseeko(smb->sda_fp,(offset/SDT_BLOCK_LEN)*sizeof(refs),SEEK_SET)) {
65
		safe_snprintf(smb->last_error,sizeof(smb->last_error),"%s seeking to: %" PRIdOFF, __FUNCTION__
66
			,(offset/SDT_BLOCK_LEN)*sizeof(refs));
67 68 69 70 71
		return(SMB_ERR_SEEK);
	}
	for(l=0;l<blocks;l++)
		if(!fwrite(&refs,sizeof(refs),1,smb->sda_fp)) {
			safe_snprintf(smb->last_error,sizeof(smb->last_error)
72
				,"%s writing allocation bytes at offset %" PRIdOFF, __FUNCTION__
73 74 75 76 77 78 79 80 81 82 83
				,((offset/SDT_BLOCK_LEN)+l)*sizeof(refs));
			return(SMB_ERR_WRITE);
		}
	fflush(smb->sda_fp);
	return(offset);
}

/****************************************************************************/
/* Allocates space for data, but doesn't search for unused blocks           */
/* Returns negative on error												*/
/****************************************************************************/
84
off_t smb_fallocdat(smb_t* smb, off_t length, uint16_t refs)
85
{
86 87
	ulong	l,blocks;
	off_t offset;
88 89

	if(smb->sda_fp==NULL) {
90
		safe_snprintf(smb->last_error, sizeof(smb->last_error), "%s msgbase not open", __FUNCTION__);
91 92 93 94 95 96
		return(SMB_ERR_NOT_OPEN);
	}
	fflush(smb->sda_fp);
	clearerr(smb->sda_fp);
	blocks=smb_datblocks(length);
	if(fseek(smb->sda_fp,0L,SEEK_END)) {
97
		safe_snprintf(smb->last_error,sizeof(smb->last_error),"%s rewinding", __FUNCTION__);
98 99 100 101 102
		return(SMB_ERR_SEEK);
	}
	offset=(ftell(smb->sda_fp)/sizeof(refs))*SDT_BLOCK_LEN;
	if((long)offset<0) {
		safe_snprintf(smb->last_error,sizeof(smb->last_error)
103
			,"%s invalid data offset: %" PRIdOFF, __FUNCTION__, offset);
104 105 106 107 108 109 110 111
		return(SMB_ERR_DAT_OFFSET);
	}
	for(l=0;l<blocks;l++)
		if(!fwrite(&refs,sizeof(refs),1,smb->sda_fp))
			break;
	fflush(smb->sda_fp);
	if(l<blocks) {
		safe_snprintf(smb->last_error,sizeof(smb->last_error)
112
			,"%s writing allocation bytes", __FUNCTION__);
113 114 115 116 117 118 119 120
		return(SMB_ERR_WRITE);
	}
	return(offset);
}

/****************************************************************************/
/* De-allocates space for data												*/
/* Returns non-zero on error												*/
121
/* Always unlocks the SMB header (when not hyper-alloc)						*/
122
/****************************************************************************/
123
int smb_freemsgdat(smb_t* smb, off_t offset, ulong length, uint16_t refs)
124 125
{
	BOOL	da_opened=FALSE;
126
	int		retval=SMB_SUCCESS;
deuce's avatar
64-bit  
deuce committed
127
	uint16_t	i;
128
	long	l,blocks;
129
	off_t	sda_offset;
130
	off_t	flen;
131

132 133 134
	if(offset < 0)
		return SMB_ERR_DAT_OFFSET;

135 136 137
	if(smb->status.attr&SMB_HYPERALLOC)	/* do nothing */
		return(SMB_SUCCESS);

138 139 140 141
	blocks = smb_datblocks(length);

	if(blocks < 1)
		return SMB_SUCCESS;	// Nothing to do
142 143 144 145 146 147

	if(smb->sda_fp==NULL) {
		if((i=smb_open_da(smb))!=SMB_SUCCESS)
			return(i);
		da_opened=TRUE;
	}
148 149 150
	flen = filelength(fileno(smb->sda_fp));
	if(flen < sizeof(uint16_t))
		return 0;	// Nothing to do
151 152 153
	
	if(!smb->locked && smb_locksmbhdr(smb) != SMB_SUCCESS)
		return SMB_ERR_LOCK;
154 155

	clearerr(smb->sda_fp);
156 157
	// Free from the last block first
	for(l=blocks-1; l >= 0; l--) {
158
		sda_offset=((offset/SDT_BLOCK_LEN)+l)*sizeof(i);
159
		if(fseeko(smb->sda_fp,sda_offset,SEEK_SET)) {
160
			safe_snprintf(smb->last_error,sizeof(smb->last_error)
161
				,"%s %d '%s' seeking to %" PRIdOFF " of allocation file", __FUNCTION__
162
				,get_errno(),STRERROR(get_errno())
163
				,sda_offset);
164 165 166 167 168
			retval=SMB_ERR_SEEK;
			break;
		}
		if(smb_fread(smb,&i,sizeof(i),smb->sda_fp)!=sizeof(i)) {
			safe_snprintf(smb->last_error,sizeof(smb->last_error)
169
				,"%s reading allocation record at offset %" PRIdOFF, __FUNCTION__
170 171 172 173 174 175 176 177
				,sda_offset);
			retval=SMB_ERR_READ;
			break;
		}
		if(refs==SMB_ALL_REFS || refs>i)
			i=0;			/* don't want to go negative */
		else
			i-=refs;
178 179 180

		// Completely free? and at end of SDA? Just truncate record from end of file
		if(i == 0 && ftell(smb->sda_fp) == flen) {
181
			if(chsize(fileno(smb->sda_fp), (long)sda_offset) == 0) {
182 183 184 185
				flen = sda_offset;
				continue;
			}
		}
186

187 188
		if(fseek(smb->sda_fp,-(int)sizeof(i),SEEK_CUR)) {
			safe_snprintf(smb->last_error,sizeof(smb->last_error)
189
				,"%s %d '%s' seeking backwards 2 bytes in allocation file", __FUNCTION__
190
				,get_errno(),STRERROR(get_errno()));
191 192 193 194 195
			retval=SMB_ERR_SEEK;
			break;
		}
		if(!fwrite(&i,sizeof(i),1,smb->sda_fp)) {
			safe_snprintf(smb->last_error,sizeof(smb->last_error)
196
				,"%s writing allocation bytes at offset %" PRIdOFF, __FUNCTION__
197
				,sda_offset);
198
			retval=SMB_ERR_WRITE;
199 200 201 202
			break;
		}
	}
	fflush(smb->sda_fp);
rswindell's avatar
rswindell committed
203
	if(filelength(fileno(smb->sdt_fp)) / SDT_BLOCK_LEN > (long)(filelength(fileno(smb->sda_fp)) / sizeof(uint16_t)))
204 205
		if(chsize(fileno(smb->sdt_fp), (long)(filelength(fileno(smb->sda_fp)) / sizeof(uint16_t)) * SDT_BLOCK_LEN) != 0)
			retval = SMB_ERR_TRUNCATE;
206 207
	if(da_opened)
		smb_close_da(smb);
208
	smb_unlocksmbhdr(smb);
209 210 211 212 213
	return(retval);
}

/****************************************************************************/
/* Adds to data allocation records for blocks starting at 'offset'          */
214
/* SMB header should be locked before calling this function					*/
215 216
/* Returns non-zero on error												*/
/****************************************************************************/
217
int smb_incdat(smb_t* smb, off_t offset, ulong length, uint16_t refs)
218
{
deuce's avatar
64-bit  
deuce committed
219
	uint16_t	i;
220 221 222
	ulong	l,blocks;

	if(smb->sda_fp==NULL) {
223
		safe_snprintf(smb->last_error, sizeof(smb->last_error), "%s msgbase not open", __FUNCTION__);
224 225 226 227 228
		return(SMB_ERR_NOT_OPEN);
	}
	clearerr(smb->sda_fp);
	blocks=smb_datblocks(length);
	for(l=0;l<blocks;l++) {
229
		if(fseeko(smb->sda_fp,((offset/SDT_BLOCK_LEN)+l)*sizeof(i),SEEK_SET)) {
230
			safe_snprintf(smb->last_error,sizeof(smb->last_error),"%s seeking to %" PRIdOFF, __FUNCTION__
231
				,((offset/SDT_BLOCK_LEN)+l)*sizeof(i));
232 233 234 235
			return(SMB_ERR_SEEK);
		}
		if(smb_fread(smb,&i,sizeof(i),smb->sda_fp)!=sizeof(i)) {
			safe_snprintf(smb->last_error,sizeof(smb->last_error)
236
				,"%s reading allocation record at offset %" PRIdOFF, __FUNCTION__
237 238 239 240 241
				,((offset/SDT_BLOCK_LEN)+l)*sizeof(i));
			return(SMB_ERR_READ);
		}
		i+=refs;
		if(fseek(smb->sda_fp,-(int)sizeof(i),SEEK_CUR)) {
242
			safe_snprintf(smb->last_error,sizeof(smb->last_error),"%s rewinding %ld", __FUNCTION__, -(long)sizeof(i));
243 244 245 246
			return(SMB_ERR_SEEK);
		}
		if(!fwrite(&i,sizeof(i),1,smb->sda_fp)) {
			safe_snprintf(smb->last_error,sizeof(smb->last_error)
247
				,"%s writing allocation record at offset %" PRIdOFF, __FUNCTION__
248 249 250 251
				,((offset/SDT_BLOCK_LEN)+l)*sizeof(i));
			return(SMB_ERR_WRITE); 
		}
	}
252
	return fflush(smb->sda_fp); /* SMB_SUCCESS == 0 */
253 254 255 256 257 258
}

/****************************************************************************/
/* Increments data allocation records (message references) by number of		*/
/* header references specified (usually 1)									*/
/* The opposite function of smb_freemsg()									*/
259
/* Always unlocks the SMB header (when not hyper-alloc)						*/
260
/****************************************************************************/
261
int smb_incmsg_dfields(smb_t* smb, smbmsg_t* msg, uint16_t refs)
262
{
263
	int		i=SMB_SUCCESS;
264
	BOOL	da_opened=FALSE;
265
	uint16_t	x;
266 267 268 269 270 271 272 273 274 275

	if(smb->status.attr&SMB_HYPERALLOC)  /* Nothing to do */
		return(SMB_SUCCESS);

	if(smb->sda_fp==NULL) {
		if((i=smb_open_da(smb))!=SMB_SUCCESS)
			return(i);
		da_opened=TRUE;
	}

276 277 278
	if(!smb->locked && smb_locksmbhdr(smb)!=SMB_SUCCESS)
		return SMB_ERR_LOCK;

279 280 281 282 283
	for(x=0;x<msg->hdr.total_dfields;x++) {
		if((i=smb_incdat(smb,msg->hdr.offset+msg->dfield[x].offset
			,msg->dfield[x].length,refs))!=SMB_SUCCESS)
			break; 
	}
284
	smb_unlocksmbhdr(smb);
285 286 287

	if(da_opened)
		smb_close_da(smb);
288

289 290 291 292 293 294 295
	return(i);
}

/****************************************************************************/
/* De-allocates blocks for header record									*/
/* Returns non-zero on error												*/
/****************************************************************************/
296
int smb_freemsghdr(smb_t* smb, off_t offset, ulong length)
297 298
{
	uchar	c=0;
299 300
	long	l,blocks;
	off_t	sha_offset;
301 302

	if(smb->sha_fp==NULL) {
303
		safe_snprintf(smb->last_error, sizeof(smb->last_error), "%s msgbase not open", __FUNCTION__);
304 305 306
		return(SMB_ERR_NOT_OPEN);
	}
	clearerr(smb->sha_fp);
307 308 309 310 311 312
	blocks = smb_hdrblocks(length);
	if(blocks < 1)
		return SMB_ERR_HDR_LEN;

	sha_offset = offset/SHD_BLOCK_LEN;
	if(filelength(fileno(smb->sha_fp)) <= (sha_offset + blocks)) {
313
		if(chsize(fileno(smb->sha_fp), (long)sha_offset) == 0) {
314 315
			if(chsize(fileno(smb->shd_fp), (long)(smb->status.header_offset + offset)) != 0)
				return SMB_ERR_TRUNCATE;
316 317 318 319
			return SMB_SUCCESS;
		}
	}

320
	if(fseeko(smb->sha_fp, sha_offset, SEEK_SET)) {
321
		safe_snprintf(smb->last_error,sizeof(smb->last_error),"%s seeking to %ld", __FUNCTION__, (long)sha_offset);
322
		return(SMB_ERR_SEEK);
323
	}
324 325 326
	for(l=0;l<blocks;l++)
		if(!fwrite(&c,1,1,smb->sha_fp)) {
			safe_snprintf(smb->last_error,sizeof(smb->last_error)
327
				,"%s writing allocation record", __FUNCTION__);
328 329
			return(SMB_ERR_WRITE);
		}
330
	return fflush(smb->sha_fp);	/* SMB_SUCCESS == 0 */
331 332 333 334
}

/****************************************************************************/
/****************************************************************************/
335
int smb_freemsg_dfields(smb_t* smb, smbmsg_t* msg, uint16_t refs)
336 337
{
	int		i;
338
	uint16_t	x;
339 340 341 342 343 344 345 346 347 348 349 350

	for(x=0;x<msg->hdr.total_dfields;x++) {
		if((i=smb_freemsgdat(smb,msg->hdr.offset+msg->dfield[x].offset
			,msg->dfield[x].length,refs))!=SMB_SUCCESS)
			return(i); 
	}
	return(SMB_SUCCESS);
}

/****************************************************************************/
/* Frees all allocated header and data blocks (1 reference) for 'msg'       */
/****************************************************************************/
351
int smb_freemsg(smb_t* smb, smbmsg_t* msg)
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376
{
	int 	i;

	if(smb->status.attr&SMB_HYPERALLOC)  /* Nothing to do */
		return(SMB_SUCCESS);

	if(!smb_valid_hdr_offset(smb,msg->idx.offset))
		return(SMB_ERR_HDR_OFFSET);

	if((i=smb_freemsg_dfields(smb,msg,1))!=SMB_SUCCESS)
		return(i);

	return(smb_freemsghdr(smb,msg->idx.offset-smb->status.header_offset
		,msg->hdr.length));
}

/****************************************************************************/
/* Finds unused space in header file based on block allocation table and	*/
/* marks space as used in allocation table.                                 */
/* File must be opened read/write DENY ALL									*/
/* Returns offset to beginning of header (in bytes, not blocks) 			*/
/* Assumes smb_open_ha() has been called									*/
/* smb_close_ha() should be called after									*/
/* Returns negative value on error 											*/
/****************************************************************************/
377
off_t smb_allochdr(smb_t* smb, ulong length)
378 379 380 381 382
{
	uchar	c;
	ulong	i,l,blocks,offset=0;

	if(smb->sha_fp==NULL) {
383
		safe_snprintf(smb->last_error, sizeof(smb->last_error), "%s msgbase not open", __FUNCTION__);
384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401
		return(SMB_ERR_NOT_OPEN);
	}
	blocks=smb_hdrblocks(length);
	i=0;	/* i is consecutive unused block counter */
	fflush(smb->sha_fp);
	rewind(smb->sha_fp);
	while(!feof(smb->sha_fp)) {
		if(smb_fread(smb,&c,sizeof(c),smb->sha_fp)!=sizeof(c)) 
			break;
		offset+=SHD_BLOCK_LEN;
		if(!c) i++;
		else   i=0;
		if(i==blocks) {
			offset-=(blocks*SHD_BLOCK_LEN);
			break; 
		} 
	}
	clearerr(smb->sha_fp);
402
	if(fseek(smb->sha_fp,offset/SHD_BLOCK_LEN,SEEK_SET)) {
403
		safe_snprintf(smb->last_error,sizeof(smb->last_error),"%s seeking to %ld", __FUNCTION__, offset/SHD_BLOCK_LEN);
404
		return(SMB_ERR_SEEK);
405
	}
406 407 408
	for(l=0;l<blocks;l++)
		if(fputc(1,smb->sha_fp)!=1) {
			safe_snprintf(smb->last_error,sizeof(smb->last_error)
409
				,"%s writing allocation record", __FUNCTION__);
410 411 412 413 414 415 416
			return(SMB_ERR_WRITE);
		}
	fflush(smb->sha_fp);
	return(offset);
}

/****************************************************************************/
rswindell's avatar
rswindell committed
417
/* Allocates space for header, but doesn't search for unused blocks          */
418 419
/* Returns negative value on error 											*/
/****************************************************************************/
420
off_t smb_fallochdr(smb_t* smb, ulong length)
421 422 423 424 425
{
	uchar	c=1;
	ulong	l,blocks,offset;

	if(smb->sha_fp==NULL) {
426
		safe_snprintf(smb->last_error, sizeof(smb->last_error), "%s msgbase not open", __FUNCTION__);
427 428 429 430 431
		return(SMB_ERR_NOT_OPEN);
	}
	blocks=smb_hdrblocks(length);
	fflush(smb->sha_fp);
	clearerr(smb->sha_fp);
432
	if(fseek(smb->sha_fp,0L,SEEK_END)) {
433
		safe_snprintf(smb->last_error,sizeof(smb->last_error),"%s rewinding", __FUNCTION__);
434
		return(SMB_ERR_SEEK);
435
	}
436 437 438 439
	offset=ftell(smb->sha_fp)*SHD_BLOCK_LEN;
	for(l=0;l<blocks;l++)
		if(!fwrite(&c,1,1,smb->sha_fp)) {
			safe_snprintf(smb->last_error,sizeof(smb->last_error)
440
				,"%s writing allocation record", __FUNCTION__);
441 442 443 444 445 446 447 448 449 450 451
			return(SMB_ERR_WRITE);
		}
	fflush(smb->sha_fp);
	return(offset);
}

/************************************************************************/
/* Allocate header blocks using Hyper Allocation						*/
/* this function should be most likely not be called from anywhere but	*/
/* smb_addmsghdr()														*/
/************************************************************************/
452
off_t smb_hallochdr(smb_t* smb)
453 454 455 456
{
	ulong offset;

	if(smb->shd_fp==NULL) {
457
		safe_snprintf(smb->last_error, sizeof(smb->last_error), "%s msgbase not open", __FUNCTION__);
458 459 460
		return(SMB_ERR_NOT_OPEN);
	}
	fflush(smb->shd_fp);
461
	if(fseek(smb->shd_fp,0L,SEEK_END)) {
462
		safe_snprintf(smb->last_error,sizeof(smb->last_error),"%s rewinding", __FUNCTION__);
463
		return(SMB_ERR_SEEK);
464
	}
465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
	offset=ftell(smb->shd_fp);
	if(offset<smb->status.header_offset) 	/* Header file truncated?!? */
		return(smb->status.header_offset);

	offset-=smb->status.header_offset;		/* SMB headers not included */

	/* Even block boundry */
	offset+=PAD_LENGTH_FOR_ALIGNMENT(offset,SHD_BLOCK_LEN);

	return(offset);
}

/************************************************************************/
/* Allocate data blocks using Hyper Allocation							*/
/* smb_locksmbhdr() should be called before this function and not		*/
/* unlocked until all data fields for this message have been written	*/
/* to the SDT file														*/
/************************************************************************/
483
off_t smb_hallocdat(smb_t* smb)
484
{
485
	off_t offset;
486 487 488

	if(smb->sdt_fp==NULL) {
		safe_snprintf(smb->last_error,sizeof(smb->last_error)
489
			,"%s msgbase not open", __FUNCTION__);
490 491 492 493 494 495
		return(SMB_ERR_NOT_OPEN);
	}
	fflush(smb->sdt_fp);
	offset=filelength(fileno(smb->sdt_fp));
	if(offset<0) {
		safe_snprintf(smb->last_error,sizeof(smb->last_error)
496
			,"%s invalid file length: %" PRIdOFF, __FUNCTION__, offset);
497 498
		return(SMB_ERR_FILE_LEN);
	}
499
	if(fseek(smb->sdt_fp,0L,SEEK_END)) {
500
		safe_snprintf(smb->last_error,sizeof(smb->last_error),"%s rewinding", __FUNCTION__);
501
		return(SMB_ERR_SEEK);
502
	}
503 504 505
	offset=ftell(smb->sdt_fp);
	if(offset<0) {
		safe_snprintf(smb->last_error,sizeof(smb->last_error)
506
			,"%s invalid file offset: %" PRIdOFF, __FUNCTION__, offset);
507 508 509 510 511 512
		return(SMB_ERR_DAT_OFFSET);
	}

	/* Make sure even block boundry */
	offset+=PAD_LENGTH_FOR_ALIGNMENT(offset,SDT_BLOCK_LEN);

513
	return (long)offset;
514
}