Home | History | Annotate | Download | only in common
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  *
     25  * Portions Copyright 2008 Denis Cheng
     26  */
     27 
     28 #include "config.h"
     29 #include "filebench.h"
     30 #include "flowop.h"
     31 #include "threadflow.h" /* For aiolist definition */
     32 
     33 #ifndef HAVE_OFF64_T
     34 /*
     35  * We are probably on linux.
     36  * According to http://www.suse.de/~aj/linux_lfs.html, defining the
     37  * above, automatically changes type of off_t to off64_t. so let
     38  * us use only off_t as off64_t is not defined
     39  */
     40 #define	off64_t off_t
     41 #endif /* HAVE_OFF64_T */
     42 
     43 #include <fcntl.h>
     44 #include <stdio.h>
     45 #include <stdlib.h>
     46 #include <unistd.h>
     47 #include <libgen.h>
     48 #include <sys/mman.h>
     49 #include <sys/stat.h>
     50 #include <sys/types.h>
     51 #include <sys/param.h>
     52 #include <sys/resource.h>
     53 
     54 #include "filebench.h"
     55 #include "fsplug.h"
     56 
     57 #ifdef HAVE_AIO
     58 #include <aio.h>
     59 #endif /* HAVE_AIO */
     60 
     61 #ifdef HAVE_LIBAIO_H
     62 #include <libaio.h>
     63 #endif /* HAVE_LIBAIO_H */
     64 
     65 #ifndef HAVE_AIOCB64_T
     66 #define	aiocb64 aiocb
     67 #endif /* HAVE_AIOCB64_T */
     68 
     69 /*
     70  * These routines implement local file access. They are placed into a
     71  * vector of functions that are called by all I/O operations in fileset.c
     72  * and flowop_library.c. This represents the default file system plug-in,
     73  * and may be replaced by vectors for other file system plug-ins.
     74  */
     75 
     76 static int fb_lfs_freemem(fb_fdesc_t *fd, off64_t size);
     77 static int fb_lfs_open(fb_fdesc_t *, char *, int, int);
     78 static int fb_lfs_pread(fb_fdesc_t *, caddr_t, fbint_t, off64_t);
     79 static int fb_lfs_read(fb_fdesc_t *, caddr_t, fbint_t);
     80 static int fb_lfs_pwrite(fb_fdesc_t *, caddr_t, fbint_t, off64_t);
     81 static int fb_lfs_write(fb_fdesc_t *, caddr_t, fbint_t);
     82 static int fb_lfs_lseek(fb_fdesc_t *, off64_t, int);
     83 static int fb_lfs_truncate(fb_fdesc_t *, off64_t);
     84 static int fb_lfs_rename(const char *, const char *);
     85 static int fb_lfs_close(fb_fdesc_t *);
     86 static int fb_lfs_link(const char *, const char *);
     87 static int fb_lfs_symlink(const char *, const char *);
     88 static int fb_lfs_unlink(char *);
     89 static ssize_t fb_lfs_readlink(const char *, char *, size_t);
     90 static int fb_lfs_mkdir(char *, int);
     91 static int fb_lfs_rmdir(char *);
     92 static DIR *fb_lfs_opendir(char *);
     93 static struct dirent *fb_lfs_readdir(DIR *);
     94 static int fb_lfs_closedir(DIR *);
     95 static int fb_lfs_fsync(fb_fdesc_t *);
     96 static int fb_lfs_stat(char *, struct stat64 *);
     97 static int fb_lfs_fstat(fb_fdesc_t *, struct stat64 *);
     98 static int fb_lfs_access(const char *, int);
     99 static void fb_lfs_recur_rm(char *);
    100 
    101 static fsplug_func_t fb_lfs_funcs =
    102 {
    103 	"locfs",
    104 	fb_lfs_freemem,		/* flush page cache */
    105 	fb_lfs_open,		/* open */
    106 	fb_lfs_pread,		/* pread */
    107 	fb_lfs_read,		/* read */
    108 	fb_lfs_pwrite,		/* pwrite */
    109 	fb_lfs_write,		/* write */
    110 	fb_lfs_lseek,		/* lseek */
    111 	fb_lfs_truncate,	/* ftruncate */
    112 	fb_lfs_rename,		/* rename */
    113 	fb_lfs_close,		/* close */
    114 	fb_lfs_link,		/* link */
    115 	fb_lfs_symlink,		/* symlink */
    116 	fb_lfs_unlink,		/* unlink */
    117 	fb_lfs_readlink,	/* readlink */
    118 	fb_lfs_mkdir,		/* mkdir */
    119 	fb_lfs_rmdir,		/* rmdir */
    120 	fb_lfs_opendir,		/* opendir */
    121 	fb_lfs_readdir,		/* readdir */
    122 	fb_lfs_closedir,	/* closedir */
    123 	fb_lfs_fsync,		/* fsync */
    124 	fb_lfs_stat,		/* stat */
    125 	fb_lfs_fstat,		/* fstat */
    126 	fb_lfs_access,		/* access */
    127 	fb_lfs_recur_rm		/* recursive rm */
    128 };
    129 
    130 #ifdef HAVE_AIO
    131 /*
    132  * Local file system asynchronous IO flowops are in this module, as
    133  * they have a number of local file system specific features.
    134  */
    135 static int fb_lfsflow_aiowrite(threadflow_t *threadflow, flowop_t *flowop);
    136 static int fb_lfsflow_aiowait(threadflow_t *threadflow, flowop_t *flowop);
    137 
    138 static flowop_proto_t fb_lfsflow_funcs[] = {
    139 	FLOW_TYPE_AIO, FLOW_ATTR_WRITE, "aiowrite", flowop_init_generic,
    140 	fb_lfsflow_aiowrite, flowop_destruct_generic,
    141 	FLOW_TYPE_AIO, 0, "aiowait", flowop_init_generic,
    142 	fb_lfsflow_aiowait, flowop_destruct_generic
    143 };
    144 
    145 #endif /* HAVE_AIO */
    146 
    147 /*
    148  * Initialize this processes I/O functions vector to point to
    149  * the vector of local file system I/O functions
    150  */
    151 void
    152 fb_lfs_funcvecinit(void)
    153 {
    154 	fs_functions_vec = &fb_lfs_funcs;
    155 }
    156 
    157 /*
    158  * Initialize those flowops whose implementation is file system
    159  * specific.
    160  */
    161 void
    162 fb_lfs_flowinit(void)
    163 {
    164 	int nops;
    165 
    166 	/*
    167 	 * re-initialize the I/O functions vector while we are at
    168 	 * it as it may have been redefined since the process was
    169 	 * created, at least if this is the master processes
    170 	 */
    171 	fb_lfs_funcvecinit();
    172 
    173 #ifdef HAVE_AIO
    174 	nops = sizeof (fb_lfsflow_funcs) / sizeof (flowop_proto_t);
    175 	flowop_flow_init(fb_lfsflow_funcs, nops);
    176 #endif /* HAVE_AIO */
    177 }
    178 
    179 /*
    180  * Frees up memory mapped file region of supplied size. The
    181  * file descriptor "fd" indicates which memory mapped file.
    182  * If successful, returns 0. Otherwise returns -1 if "size"
    183  * is zero, or -1 times the number of times msync() failed.
    184  */
    185 static int
    186 fb_lfs_freemem(fb_fdesc_t *fd, off64_t size)
    187 {
    188 	off64_t left;
    189 	int ret = 0;
    190 
    191 	for (left = size; left > 0; left -= MMAP_SIZE) {
    192 		off64_t thismapsize;
    193 		caddr_t addr;
    194 
    195 		thismapsize = MIN(MMAP_SIZE, left);
    196 		addr = mmap64(0, thismapsize, PROT_READ|PROT_WRITE,
    197 		    MAP_SHARED, fd->fd_num, size - left);
    198 		ret += msync(addr, thismapsize, MS_INVALIDATE);
    199 		(void) munmap(addr, thismapsize);
    200 	}
    201 	return (ret);
    202 }
    203 
    204 /*
    205  * Does a posix pread. Returns what the pread() returns.
    206  */
    207 static int
    208 fb_lfs_pread(fb_fdesc_t *fd, caddr_t iobuf, fbint_t iosize, off64_t fileoffset)
    209 {
    210 	return (pread64(fd->fd_num, iobuf, iosize, fileoffset));
    211 }
    212 
    213 /*
    214  * Does a posix read. Returns what the read() returns.
    215  */
    216 static int
    217 fb_lfs_read(fb_fdesc_t *fd, caddr_t iobuf, fbint_t iosize)
    218 {
    219 	return (read(fd->fd_num, iobuf, iosize));
    220 }
    221 
    222 #ifdef HAVE_AIO
    223 
    224 /*
    225  * Asynchronous write section. An Asynchronous IO element
    226  * (aiolist_t) is used to associate the asynchronous write request with
    227  * its subsequent completion. This element includes a aiocb64 struct
    228  * that is used by posix aio_xxx calls to track the asynchronous writes.
    229  * The flowops aiowrite and aiowait result in calls to these posix
    230  * aio_xxx system routines to do the actual asynchronous write IO
    231  * operations.
    232  */
    233 
    234 
    235 /*
    236  * Allocates an asynchronous I/O list (aio, of type
    237  * aiolist_t) element. Adds it to the flowop thread's
    238  * threadflow aio list. Returns a pointer to the element.
    239  */
    240 static aiolist_t *
    241 aio_allocate(flowop_t *flowop)
    242 {
    243 	aiolist_t *aiolist;
    244 
    245 	if ((aiolist = malloc(sizeof (aiolist_t))) == NULL) {
    246 		filebench_log(LOG_ERROR, "malloc aiolist failed");
    247 		filebench_shutdown(1);
    248 	}
    249 
    250 	/* Add to list */
    251 	if (flowop->fo_thread->tf_aiolist == NULL) {
    252 		flowop->fo_thread->tf_aiolist = aiolist;
    253 		aiolist->al_next = NULL;
    254 	} else {
    255 		aiolist->al_next = flowop->fo_thread->tf_aiolist;
    256 		flowop->fo_thread->tf_aiolist = aiolist;
    257 	}
    258 	return (aiolist);
    259 }
    260 
    261 /*
    262  * Searches for the aiolist element that has a matching
    263  * completion block, aiocb. If none found returns FILEBENCH_ERROR. If
    264  * found, removes the aiolist element from flowop thread's
    265  * list and returns FILEBENCH_OK.
    266  */
    267 static int
    268 aio_deallocate(flowop_t *flowop, struct aiocb64 *aiocb)
    269 {
    270 	aiolist_t *aiolist = flowop->fo_thread->tf_aiolist;
    271 	aiolist_t *previous = NULL;
    272 	aiolist_t *match = NULL;
    273 
    274 	if (aiocb == NULL) {
    275 		filebench_log(LOG_ERROR, "null aiocb deallocate");
    276 		return (FILEBENCH_OK);
    277 	}
    278 
    279 	while (aiolist) {
    280 		if (aiocb == &(aiolist->al_aiocb)) {
    281 			match = aiolist;
    282 			break;
    283 		}
    284 		previous = aiolist;
    285 		aiolist = aiolist->al_next;
    286 	}
    287 
    288 	if (match == NULL)
    289 		return (FILEBENCH_ERROR);
    290 
    291 	/* Remove from the list */
    292 	if (previous)
    293 		previous->al_next = match->al_next;
    294 	else
    295 		flowop->fo_thread->tf_aiolist = match->al_next;
    296 
    297 	return (FILEBENCH_OK);
    298 }
    299 
    300 /*
    301  * Emulate posix aiowrite(). Determines which file to use,
    302  * either one file of a fileset, or the file associated
    303  * with a fileobj, allocates and fills an aiolist_t element
    304  * for the write, and issues the asynchronous write. This
    305  * operation is only valid for random IO, and returns an
    306  * error if the flowop is set for sequential IO. Returns
    307  * FILEBENCH_OK on success, FILEBENCH_NORSC if iosetup can't
    308  * obtain a file to open, and FILEBENCH_ERROR on any
    309  * encountered error.
    310  */
    311 static int
    312 fb_lfsflow_aiowrite(threadflow_t *threadflow, flowop_t *flowop)
    313 {
    314 	caddr_t iobuf;
    315 	fbint_t wss;
    316 	fbint_t iosize;
    317 	fb_fdesc_t *fdesc;
    318 	int ret;
    319 
    320 	iosize = avd_get_int(flowop->fo_iosize);
    321 
    322 	if ((ret = flowoplib_iosetup(threadflow, flowop, &wss, &iobuf,
    323 	    &fdesc, iosize)) != FILEBENCH_OK)
    324 		return (ret);
    325 
    326 	if (avd_get_bool(flowop->fo_random)) {
    327 		uint64_t fileoffset;
    328 		struct aiocb64 *aiocb;
    329 		aiolist_t *aiolist;
    330 
    331 		if (filebench_randomno64(&fileoffset,
    332 		    wss, iosize, NULL) == -1) {
    333 			filebench_log(LOG_ERROR,
    334 			    "file size smaller than IO size for thread %s",
    335 			    flowop->fo_name);
    336 			return (FILEBENCH_ERROR);
    337 		}
    338 
    339 		aiolist = aio_allocate(flowop);
    340 		aiolist->al_type = AL_WRITE;
    341 		aiocb = &aiolist->al_aiocb;
    342 
    343 		aiocb->aio_fildes = fdesc->fd_num;
    344 		aiocb->aio_buf = iobuf;
    345 		aiocb->aio_nbytes = (size_t)iosize;
    346 		aiocb->aio_offset = (off64_t)fileoffset;
    347 		aiocb->aio_reqprio = 0;
    348 
    349 		filebench_log(LOG_DEBUG_IMPL,
    350 		    "aio fd=%d, bytes=%llu, offset=%llu",
    351 		    fdesc->fd_num, (u_longlong_t)iosize,
    352 		    (u_longlong_t)fileoffset);
    353 
    354 		flowop_beginop(threadflow, flowop);
    355 		if (aio_write64(aiocb) < 0) {
    356 			filebench_log(LOG_ERROR, "aiowrite failed: %s",
    357 			    strerror(errno));
    358 			filebench_shutdown(1);
    359 		}
    360 		flowop_endop(threadflow, flowop, iosize);
    361 	} else {
    362 		return (FILEBENCH_ERROR);
    363 	}
    364 
    365 	return (FILEBENCH_OK);
    366 }
    367 
    368 
    369 
    370 #define	MAXREAP 4096
    371 
    372 /*
    373  * Emulate posix aiowait(). Waits for the completion of half the
    374  * outstanding asynchronous IOs, or a single IO, which ever is
    375  * larger. The routine will return after a sufficient number of
    376  * completed calls issued by any thread in the procflow have
    377  * completed, or a 1 second timout elapses. All completed
    378  * IO operations are deleted from the thread's aiolist.
    379  */
    380 static int
    381 fb_lfsflow_aiowait(threadflow_t *threadflow, flowop_t *flowop)
    382 {
    383 	struct aiocb64 **worklist;
    384 	aiolist_t *aio = flowop->fo_thread->tf_aiolist;
    385 	int uncompleted = 0;
    386 
    387 	worklist = calloc(MAXREAP, sizeof (struct aiocb64 *));
    388 
    389 	/* Count the list of pending aios */
    390 	while (aio) {
    391 		uncompleted++;
    392 		aio = aio->al_next;
    393 	}
    394 
    395 	do {
    396 		uint_t ncompleted = 0;
    397 		uint_t todo;
    398 		struct timespec timeout;
    399 		int inprogress;
    400 		int i;
    401 
    402 		/* Wait for half of the outstanding requests */
    403 		timeout.tv_sec = 1;
    404 		timeout.tv_nsec = 0;
    405 
    406 		if (uncompleted > MAXREAP)
    407 			todo = MAXREAP;
    408 		else
    409 			todo = uncompleted / 2;
    410 
    411 		if (todo == 0)
    412 			todo = 1;
    413 
    414 		flowop_beginop(threadflow, flowop);
    415 
    416 #if (defined(HAVE_AIOWAITN) && defined(USE_PROCESS_MODEL))
    417 		if (((aio_waitn64((struct aiocb64 **)worklist,
    418 		    MAXREAP, &todo, &timeout)) == -1) &&
    419 		    errno && (errno != ETIME)) {
    420 			filebench_log(LOG_ERROR,
    421 			    "aiowait failed: %s, outstanding = %d, "
    422 			    "ncompleted = %d ",
    423 			    strerror(errno), uncompleted, todo);
    424 		}
    425 
    426 		ncompleted = todo;
    427 		/* Take the  completed I/Os from the list */
    428 		inprogress = 0;
    429 		for (i = 0; i < ncompleted; i++) {
    430 			if ((aio_return64(worklist[i]) == -1) &&
    431 			    (errno == EINPROGRESS)) {
    432 				inprogress++;
    433 				continue;
    434 			}
    435 			if (aio_deallocate(flowop, worklist[i])
    436 			    == FILEBENCH_ERROR) {
    437 				filebench_log(LOG_ERROR, "Could not remove "
    438 				    "aio from list ");
    439 				flowop_endop(threadflow, flowop, 0);
    440 				return (FILEBENCH_ERROR);
    441 			}
    442 		}
    443 
    444 		uncompleted -= ncompleted;
    445 		uncompleted += inprogress;
    446 
    447 #else
    448 
    449 		for (ncompleted = 0, inprogress = 0,
    450 		    aio = flowop->fo_thread->tf_aiolist;
    451 		    ncompleted < todo, aio != NULL; aio = aio->al_next) {
    452 			int result = aio_error64(&aio->al_aiocb);
    453 
    454 			if (result == EINPROGRESS) {
    455 				inprogress++;
    456 				continue;
    457 			}
    458 
    459 			if ((aio_return64(&aio->al_aiocb) == -1) || result) {
    460 				filebench_log(LOG_ERROR, "aio failed: %s",
    461 				    strerror(result));
    462 				continue;
    463 			}
    464 
    465 			ncompleted++;
    466 
    467 			if (aio_deallocate(flowop, &aio->al_aiocb) < 0) {
    468 				filebench_log(LOG_ERROR, "Could not remove "
    469 				    "aio from list ");
    470 				flowop_endop(threadflow, flowop, 0);
    471 				return (FILEBENCH_ERROR);
    472 			}
    473 		}
    474 
    475 		uncompleted -= ncompleted;
    476 
    477 #endif
    478 		filebench_log(LOG_DEBUG_SCRIPT,
    479 		    "aio2 completed %d ios, uncompleted = %d, inprogress = %d",
    480 		    ncompleted, uncompleted, inprogress);
    481 
    482 	} while (uncompleted > MAXREAP);
    483 
    484 	flowop_endop(threadflow, flowop, 0);
    485 
    486 	free(worklist);
    487 
    488 	return (FILEBENCH_OK);
    489 }
    490 
    491 #endif /* HAVE_AIO */
    492 
    493 /*
    494  * Does an open64 of a file. Inserts the file descriptor number returned
    495  * by open() into the supplied filebench fd. Returns FILEBENCH_OK on
    496  * successs, and FILEBENCH_ERROR on failure.
    497  */
    498 
    499 static int
    500 fb_lfs_open(fb_fdesc_t *fd, char *path, int flags, int perms)
    501 {
    502 	if ((fd->fd_num = open64(path, flags, perms)) < 0)
    503 		return (FILEBENCH_ERROR);
    504 	else
    505 		return (FILEBENCH_OK);
    506 }
    507 
    508 /*
    509  * Does an unlink (delete) of a file.
    510  */
    511 static int
    512 fb_lfs_unlink(char *path)
    513 {
    514 	return (unlink(path));
    515 }
    516 
    517 /*
    518  * Does a readlink of a symbolic link.
    519  */
    520 static ssize_t
    521 fb_lfs_readlink(const char *path, char *buf, size_t buf_size)
    522 {
    523 	return (readlink(path, buf, buf_size));
    524 }
    525 
    526 /*
    527  * Does fsync of a file. Returns with fsync return info.
    528  */
    529 static int
    530 fb_lfs_fsync(fb_fdesc_t *fd)
    531 {
    532 	return (fsync(fd->fd_num));
    533 }
    534 
    535 /*
    536  * Do a posix lseek of a file. Return what lseek() returns.
    537  */
    538 static int
    539 fb_lfs_lseek(fb_fdesc_t *fd, off64_t offset, int whence)
    540 {
    541 	return (lseek64(fd->fd_num, offset, whence));
    542 }
    543 
    544 /*
    545  * Do a posix rename of a file. Return what rename() returns.
    546  */
    547 static int
    548 fb_lfs_rename(const char *old, const char *new)
    549 {
    550 	return (rename(old, new));
    551 }
    552 
    553 
    554 /*
    555  * Do a posix close of a file. Return what close() returns.
    556  */
    557 static int
    558 fb_lfs_close(fb_fdesc_t *fd)
    559 {
    560 	return (close(fd->fd_num));
    561 }
    562 
    563 /*
    564  * Use mkdir to create a directory.
    565  */
    566 static int
    567 fb_lfs_mkdir(char *path, int perm)
    568 {
    569 	return (mkdir(path, perm));
    570 }
    571 
    572 /*
    573  * Use rmdir to delete a directory. Returns what rmdir() returns.
    574  */
    575 static int
    576 fb_lfs_rmdir(char *path)
    577 {
    578 	return (rmdir(path));
    579 }
    580 
    581 /*
    582  * does a recursive rm to remove an entire directory tree (i.e. a fileset).
    583  * Supplied with the path to the root of the tree.
    584  */
    585 static void
    586 fb_lfs_recur_rm(char *path)
    587 {
    588 	char cmd[MAXPATHLEN];
    589 
    590 	(void) snprintf(cmd, sizeof (cmd), "rm -rf %s", path);
    591 	(void) system(cmd);
    592 }
    593 
    594 /*
    595  * Does a posix opendir(), Returns a directory handle on success,
    596  * NULL on failure.
    597  */
    598 static DIR *
    599 fb_lfs_opendir(char *path)
    600 {
    601 	return (opendir(path));
    602 }
    603 
    604 /*
    605  * Does a readdir() call. Returns a pointer to a table of directory
    606  * information on success, NULL on failure.
    607  */
    608 static struct dirent *
    609 fb_lfs_readdir(DIR *dirp)
    610 {
    611 	return (readdir(dirp));
    612 }
    613 
    614 /*
    615  * Does a closedir() call.
    616  */
    617 static int
    618 fb_lfs_closedir(DIR *dirp)
    619 {
    620 	return (closedir(dirp));
    621 }
    622 
    623 /*
    624  * Does an fstat of a file.
    625  */
    626 static int
    627 fb_lfs_fstat(fb_fdesc_t *fd, struct stat64 *statbufp)
    628 {
    629 	return (fstat64(fd->fd_num, statbufp));
    630 }
    631 
    632 /*
    633  * Does a stat of a file.
    634  */
    635 static int
    636 fb_lfs_stat(char *path, struct stat64 *statbufp)
    637 {
    638 	return (stat64(path, statbufp));
    639 }
    640 
    641 /*
    642  * Do a pwrite64 to a file.
    643  */
    644 static int
    645 fb_lfs_pwrite(fb_fdesc_t *fd, caddr_t iobuf, fbint_t iosize, off64_t offset)
    646 {
    647 	return (pwrite64(fd->fd_num, iobuf, iosize, offset));
    648 }
    649 
    650 /*
    651  * Do a write to a file.
    652  */
    653 static int
    654 fb_lfs_write(fb_fdesc_t *fd, caddr_t iobuf, fbint_t iosize)
    655 {
    656 	return (write(fd->fd_num, iobuf, iosize));
    657 }
    658 
    659 /*
    660  * Does a truncate operation and returns the result
    661  */
    662 static int
    663 fb_lfs_truncate(fb_fdesc_t *fd, off64_t fse_size)
    664 {
    665 #ifdef HAVE_FTRUNCATE64
    666 	return (ftruncate64(fd->fd_num, fse_size));
    667 #else
    668 	return (ftruncate(fd->fd_num, (off_t)fse_size));
    669 #endif
    670 }
    671 
    672 /*
    673  * Does a link operation and returns the result
    674  */
    675 static int
    676 fb_lfs_link(const char *existing, const char *new)
    677 {
    678 	return (link(existing, new));
    679 }
    680 
    681 /*
    682  * Does a symlink operation and returns the result
    683  */
    684 static int
    685 fb_lfs_symlink(const char *existing, const char *new)
    686 {
    687 	return (symlink(existing, new));
    688 }
    689 
    690 /*
    691  * Does an access() check on a file.
    692  */
    693 static int
    694 fb_lfs_access(const char *path, int amode)
    695 {
    696 	return (access(path, amode));
    697 }
    698