1
0
Files
irix-657m-src/eoe/cmd/sun/rpc.lockd/sysid.c
2022-09-29 17:59:04 +03:00

999 lines
21 KiB
C

/*
* sysid.c
*
* Simple-minded (PID, IP address) <-> system ID mapping functions
*
*
* Copyright 1991,1992 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
* the contents of this file may not be disclosed to third parties, copied or
* duplicated in any form, in whole or in part, without the prior written
* permission of Silicon Graphics, Inc.
*
* RESTRICTED RIGHTS LEGEND:
* Use, duplication or disclosure by the Government is subject to restrictions
* as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
* and Computer Software clause at DFARS 252.227-7013, and/or in similar or
* successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
* rights reserved under the Copyright Laws of the United States.
*/
#ident "$Revision: 1.11 $"
#include <stdio.h>
#include <sys/types.h>
#include <sys/syssgi.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/flock.h>
#include <sys/errno.h>
#include <sys/fcntl.h>
/* #include <sys/systm.h> */
#include <netinet/in.h>
#include <syslog.h>
#include <bstring.h>
#include <assert.h>
#include <netdb.h>
#include <unistd.h>
#include "prot_lock.h"
/* region types */
#define S_BEFORE 010
#define S_START 020
#define S_MIDDLE 030
#define S_END 040
#define S_AFTER 050
#define E_BEFORE 001
#define E_START 002
#define E_MIDDLE 003
#define E_END 004
#define E_AFTER 005
static struct filock *insflck(struct filock **, struct flock *, struct filock *);
static void delflck(struct filock **lck_list, struct filock *fl);
static int regflck(struct flock *ld, struct filock *flp);
static int flckadj(struct filock **, struct filock *, struct flock *);
static struct filock *blocked(struct filock *, struct flock *, struct filock **);
static int isonlist(struct filock *lck_list, struct filock *fl);
static filock_t *get_filock_list(int fd);
extern int debug;
extern int pid;
extern int errno;
#define MAXIDS 1000
#define FDS 10 /* initial size of fd array */
static struct id {
u_long ipaddr; /* client's address,
* IPADDR_ANY (0) == not used */
pid_t pid; /* and PID */
int *fd; /* array of fd's with active locks */
int nfd; /* current # of fds in array */
int fdsize; /* max size of array */
int filocks;
} id[MAXIDS];
static int max; /* high water mark for last id used */
/*
* Insert the fd into sysid's entry, if it's not there.
* Returns 1 for success, 0 if the mallocs fail.
*/
static int
addfd(struct id *id, int fd)
{
if (id->fd) {
int j;
for (j=0; j < id->nfd; j++) {
if (id->fd[j] == fd) {
return 1;
}
}
if (id->fdsize == id->nfd) {
id->fdsize *= 2;
if ((id->fd = realloc(id->fd,
sizeof(fd) * id->fdsize)) == NULL) {
return 0;
}
bzero(&id->fd[id->nfd], sizeof(fd) * id->nfd);
}
} else {
id->fdsize = FDS;
if ((id->fd = calloc(id->fdsize, sizeof(fd))) == NULL) {
return 0;
}
id->nfd = 0;
}
id->fd[id->nfd++] = fd;
return 1;
}
/*
* Remove the fd from the sysid's entry, if it's there.
* Returns the number of remaining fd's for the id, or -1 if the fd is not
* found. 0 means the sysid can be discarded.
*/
static int
rmfd(struct id *id, int fd)
{
int j;
for (j=0; j < id->nfd; j++) {
if (id->fd[j] == fd) {
if (--id->nfd > 0) {
bcopy(&id->fd[j+1], &id->fd[j],
sizeof(fd) * (id->nfd - j));
}
return id->nfd;
}
}
return -1;
}
sysid_t
findsysid( pid_t pid, u_long ipaddr )
{
int i;
for ( i = 0; i < max; i++ ) {
if ( (id[i].ipaddr == ipaddr) && (id[i].pid == pid) ) {
return( i + 1 );
}
}
return( 0 );
}
sysid_t
mksysid(pid_t pid, u_long ipaddr, int *isnewp)
{
int i, free = -1;
for (i = 0; i < max; i++) {
if (id[i].ipaddr == ipaddr && id[i].pid == pid) {
if (isnewp)
*isnewp = 0;
return i+1;
}
if (id[i].ipaddr == INADDR_ANY && free == -1) {
free = i;
}
}
if (free == -1) { /* no free slots */
if (max >= MAXIDS) {
if (debug)
printf("!! mksysid: out of IDs (%d)\n", max);
if (isnewp)
*isnewp = 0;
return 0; /* failed */
}
free = max++;
}
id[free].pid = pid;
id[free].ipaddr = ipaddr;
id[free].filocks = 0;
if (isnewp)
*isnewp = 1;
return free+1; /* sysid 0 == this host */
}
pid_t
sysid2pid(sysid_t sysid)
{
if (sysid > 0 && sysid <= MAXIDS) {
if (id[sysid-1].ipaddr != INADDR_ANY)
return id[sysid-1].pid;
}
if (debug)
printf("sysid2pid: unknown %d\n", sysid);
return 0;
}
static void
freeid(struct id *id)
{
if (id->fd)
free(id->fd);
bzero(id, sizeof(*id));
}
void
relsysid(sysid_t sysid)
{
int lock_count = 0;
filock_t *flp;
struct id *idp;
int i;
if (sysid > 0 && sysid <= MAXIDS) {
idp = &id[sysid-1];
assert(idp->ipaddr != INADDR_ANY);
/*
* If there are no locks held by this sysid, free the sysid.
* Determine this by looking at the file descriptor list.
* It is important that this check be done after lock accounting
* and after the file descriptor has been freed.
*/
if ( (idp->nfd == 0) || (idp->filocks == 0) ) {
if (debug) {
printf("relsysid: releasing sysid %d, nfd %d, filocks %d\n",
(int)sysid, idp->nfd, idp->filocks);
}
/*
* if idp->nfd == 0 then idp->filocks == 0
*/
assert(idp->filocks == 0);
/*
* don't worry if nfd is non-zero, just free the sysid
*/
freeid(idp);
}
} else if (debug)
printf("relsysid: unknown %d\n", sysid);
}
static void
clear_fd(int fd)
{
int i;
for (i = 0; i < max; i++) {
if (id[i].ipaddr != INADDR_ANY && rmfd(&id[i], fd) == 0) {
if (debug)
printf(" clr: id %d (%x %d)\n",
i+1, id[i].ipaddr, id[i].pid);
}
}
}
int
save_fd(sysid_t sysid, int fd)
{
if (sysid > 0 && sysid <= MAXIDS) {
assert(id[sysid-1].ipaddr != INADDR_ANY);
return addfd(&id[sysid-1], fd);
}
if (debug)
printf("save_fd: unknown %d\n", sysid);
return 0;
}
void
foreach_fd(u_long ipaddr, void (*proc)(int, sysid_t))
{
int i;
for (i = 0; i < max; i++) {
if (id[i].ipaddr == ipaddr) {
int j;
for (j=0; j < id[i].nfd; j++) {
if (id[i].fd[j]) {
proc(id[i].fd[j], i+1);
}
}
freeid(&id[i]);
}
}
}
/* ---------------------------------------------------------------------- */
/*
* File handle to file descriptor mapping.
*/
static struct fdtable {
netobj fh;
int fd; /* 0 means unused */
int nblocked;
int gc; /* try to garbage collect */
struct stat stat; /* get inumber for debugging */
filock_t *filocks; /* list of locks held on this file */
filock_t *blocked; /* list of locks blocked on this file */
} *fd_table;
static int lastfd;
static int used_fd;
/* ---------------------------------------------------------------------- */
static int
isunlocked(int fd)
{
return (fd_table[fd].filocks == NULL);
}
void
init_fdtable(void)
{
int num = sysconf(_SC_OPEN_MAX);
fd_table = calloc(num, sizeof(struct fdtable));
if (fd_table == NULL) {
syslog(LOG_ERR, "fatal error: no mem. for fdtable (%d slots)",
num);
exit(1);
}
}
static void
printffdt(FILE *fp)
{
register struct fdtable *f;
register int i;
if (fd_table == NULL)
return;
fprintf(fp, "In print_fdtable()... #fd %d, last %d\n", used_fd, lastfd);
for (f = fd_table, i = 0; i <= lastfd; f++, i++) {
if (f->fd) {
fprintf(fp, "%d: fd %d #bl %d gc %d inum %d/%x\n",
i,
f->fd,
f->nblocked,
f->gc,
f->stat.st_ino,
f->stat.st_dev);
}
}
}
static void
print_fdtable(void)
{
printffdt(stdout);
}
void
dumpfdt(FILE *f)
{
printffdt(f);
}
void
fd_block(int fd, int cnt)
{
assert(fd > 0 && fd <= lastfd);
assert(fd_table[fd].fd);
fd_table[fd].nblocked += cnt;
assert(fd_table[fd].nblocked >= 0);
}
void
fd_unblock(struct reclock *rec)
{
int fd = rec->blk.fd;
struct flock lckdat;
fd_block(fd, -1);
/*
* remove the file lock from the blocked list
*/
if ( rec->blk.flp ) {
bcopy(&rec->blk.flp->set, &lckdat, sizeof(lckdat));
delflck(&fd_table[fd].blocked, rec->blk.flp);
errno = record_lock(fd, rec, &lckdat);
if ( errno ) {
syslog(LOG_ERR, "fd_unblock: unable to record lock: %m");
}
}
}
static void
free_fdt(struct fdtable *f)
{
(void) close(f->fd);
bzero(f->fh.n_bytes, sizeof (f->fh.n_bytes));
xfree(&f->fh.n_bytes);
f->fh.n_len = 0;
if (f->fd == lastfd)
lastfd--;
f->fd = f->gc = 0;
used_fd--;
assert(used_fd >= 0);
}
void
fdgctimer(void)
{
register struct fdtable *f;
register int i;
int cnt = 0;
if (debug)
printf("\nenter fdgctimer\n");
for (f = fd_table, i = 0; i <= lastfd; f++, i++) {
if (f->fd && f->gc) {
if (isunlocked(f->fd))
free_fdt(f);
else
cnt++;
}
}
if (cnt != 0)
start_timer(TIMER_FDGC);
}
void
remove_fd(int fd)
{
struct fdtable *f = &fd_table[fd];
assert(fd > 0 && fd <= lastfd);
assert(f->fd);
if (debug) {
printf("rm_fd: %d inum %d/%x\n",
fd, f->stat.st_ino, f->stat.st_dev);
}
free_fdt(f);
if (debug)
print_fdtable();
}
int
get_fd(a, isnewp)
struct reclock *a;
int *isnewp;
{
register struct fdtable *f;
int fd, i;
for (f = fd_table, i = 0; i <= lastfd; f++, i++) {
if (f->fd && obj_cmp(&(f->fh), &(a->lck.fh))) {
if (debug) {
printf("get_fd: old #%d %d inum %d/%x\n",
i, f->fd,
f->stat.st_ino, f->stat.st_dev);
}
if (isnewp)
*isnewp = 0;
f->gc = 0;
return (f->fd);
}
}
if (debug) {
printf("get_fd: fh=");
for (i = 0; i < a->lck.fh.n_len; i++) {
printf("%02x", (a->lck.fh.n_bytes[i] & 0xff));
}
printf("\n");
}
/*
* Convert fh to fd
*/
if ((fd = syssgi(SGI_NFSCNVT, a->lck.fh.n_bytes, O_RDWR)) == -1) {
syslog(LOG_ERR, "get_fd: unable to cvt fh: %m");
if (errno == ESTALE)
return (-1);
return (-2);
}
f = &fd_table[fd];
obj_copy(&f->fh, &a->lck.fh);
f->fd = fd;
if (lastfd < fd)
lastfd = fd;
f->gc = 0;
(void) fstat(fd, &f->stat); /* debugging */
if (isnewp)
*isnewp = 1;
used_fd++;
if (debug) {
printf(" new %d inum %d/%x\n",
f->fd,
f->stat.st_ino, f->stat.st_dev);
if (debug > 1)
print_fdtable();
}
return (fd);
}
static filock_t *
get_filock_list(int fd)
{
return(fd_table[fd].filocks);
}
/*
* Free the state associated with a file descriptor if we do not hold
* any locks on it.
*/
void
free_fd(int fd)
{
struct fdtable *f = &fd_table[fd];
assert(fd > 0 && fd <= lastfd);
assert(f->fd);
if (f->nblocked == 0) {
if (isunlocked(fd)) {
clear_fd(fd);
free_fdt(f);
} else {
/*
* XXX
* Since we don't keep of locks held on the fd, and
* there's no fcntl to ask the kernel for that info,
* try to free it from a timer.
*/
f->gc = 1;
start_timer(TIMER_FDGC);
}
}
}
static void
printsysid(FILE *f)
{
int i;
struct hostent *hp;
u_long lastip = -1;
for (i = 0; i < max; i++) {
if (id[i].ipaddr != INADDR_ANY) {
int j;
if (!hp || id[i].ipaddr != lastip)
hp = gethostbyaddr(&id[i].ipaddr, 4, AF_INET);
fprintf(f, "sysid %d = %d, %s (%s)\n",
i+1, id[i].pid,
inet_ntoa(*(struct in_addr *)&id[i].ipaddr),
(hp != NULL) ? hp->h_name : "?");
lastip = id[i].ipaddr;
if (id[i].nfd != 0) {
fprintf(f, " fd:");
for (j=0; j < id[i].nfd; j++)
fprintf(f, " %d", id[i].fd[j]);
fprintf(f, "\n");
}
}
}
}
void
print_sysidtable(void)
{
static FILE *f = NULL;
if (f == NULL)
f = fopen("/dev/console", "w");
if (f)
printsysid(f);
}
void
dumpsysid(FILE *f)
{
printsysid(f);
}
/* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */
/* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */
/* All Rights Reserved */
/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF */
/* UNIX System Laboratories, Inc. */
/* The copyright notice above does not evidence any */
/* actual or intended publication of such source code. */
/*
* All record lock lists (referenced by a pointer in the vnode) are
* ordered by starting position relative to the beginning of the file.
*
* In this file the name "l_end" is a macro and is used in place of
* "l_len" because the end, not the length, of the record lock is
* stored internally.
*/
#undef SAMEOWNER
#define SAMEOWNER(a, b) (((a)->l_pid == (b)->l_pid) && \
((a)->l_sysid == (b)->l_sysid))
/*
* Insert lock (lckdat) after given lock (fl); If fl is NULL place the
* new lock at the beginning of the list and update the head ptr to
* list which is stored at the address given by lck_list.
*/
static struct filock *
insflck(struct filock **lck_list, struct flock *lckdat, struct filock *fl)
{
register struct filock *new;
if ((new = xmalloc(sizeof *new)) == NULL) {
return NULL;
}
bzero( new, sizeof *new );
new->set = *lckdat;
new->stat.wakeflg = 0;
if (fl == NULL) {
new->next = *lck_list;
if (new->next != NULL)
new->next->prev = new;
*lck_list = new;
} else {
new->next = fl->next;
if (fl->next != NULL)
fl->next->prev = new;
fl->next = new;
}
new->prev = fl;
return new;
}
/*
* Delete lock (fl) from the record lock list. If fl is the first lock
* in the list, remove it and update the head ptr to the list which is
* stored at the address given by lck_list.
*/
static void
delflck(struct filock **lck_list, struct filock *fl)
{
assert(isonlist(*lck_list, fl));
if (fl->prev != NULL)
fl->prev->next = fl->next;
else
*lck_list = fl->next;
if (fl->next != NULL)
fl->next->prev = fl->prev;
free(fl);
}
/*
* regflck sets the type of span of this (un)lock relative to the specified
* already existing locked section.
* There are five regions:
*
* S_BEFORE S_START S_MIDDLE S_END S_AFTER
* 010 020 030 040 050
* E_BEFORE E_START E_MIDDLE E_END E_AFTER
* 01 02 03 04 05
* |-------------------------------|
*
* relative to the already locked section. The type is two octal digits,
* the 8's digit is the start type and the 1's digit is the end type.
*/
static int
regflck(struct flock *ld, struct filock *flp)
{
register int regntype;
if (ld->l_start > flp->set.l_start) {
if (ld->l_start-1 == flp->set.l_end)
return S_END|E_AFTER;
if (ld->l_start > flp->set.l_end)
return S_AFTER|E_AFTER;
regntype = S_MIDDLE;
} else if (ld->l_start == flp->set.l_start)
regntype = S_START;
else
regntype = S_BEFORE;
if (ld->l_end < flp->set.l_end) {
if (ld->l_end == flp->set.l_start-1)
regntype |= E_START;
else if (ld->l_end < flp->set.l_start)
regntype |= E_BEFORE;
else
regntype |= E_MIDDLE;
} else if (ld->l_end == flp->set.l_end)
regntype |= E_END;
else
regntype |= E_AFTER;
return regntype;
}
/*
* Adjust file lock from region specified by 'ld', in the record
* lock list indicated by the head ptr stored at the address given
* by lck_list. Start updates at the lock given by 'insrtp'. It is
* assumed the list is ordered on starting position, relative to
* the beginning of the file, and no updating is required on any
* locks in the list previous to the one pointed to by insrtp.
* Insrtp is a result from the routine blocked(). Flckadj() scans
* the list looking for locks owned by the process requesting the
* new (un)lock :
*
* - If the new record (un)lock overlays an existing lock of
* a different type, the region overlaid is released.
*
* - If the new record (un)lock overlays or adjoins an exist-
* ing lock of the same type, the existing lock is deleted
* and its region is coalesced into the new (un)lock.
*
* When the list is sufficiently scanned and the new lock is not
* an unlock, the new lock is inserted into the appropriate
* position in the list.
*/
static int
flckadj(struct filock **lck_list,
register struct filock *insrtp,
struct flock *ld)
{
register struct filock *flp, *nflp;
int regtyp;
struct id *idp = &id[ld->l_sysid - 1];
assert(idp->filocks >= 0);
nflp = (insrtp == NULL) ? *lck_list : insrtp;
while (flp = nflp) {
nflp = flp->next;
if( SAMEOWNER(&(flp->set), ld) ) {
/* Release already locked region if necessary */
switch (regtyp = regflck(ld, flp)) {
case S_BEFORE|E_BEFORE:
nflp = NULL;
break;
case S_BEFORE|E_START:
if (ld->l_type == flp->set.l_type) {
ld->l_end = flp->set.l_end;
delflck(lck_list, flp);
idp->filocks--;
}
nflp = NULL;
break;
case S_START|E_END:
/*
* Don't bother if this is in the middle of
* an already similarly set section.
*/
if (ld->l_type == flp->set.l_type)
return 0;
case S_START|E_AFTER:
insrtp = flp->prev;
delflck(lck_list, flp);
idp->filocks--;
break;
case S_BEFORE|E_END:
if (ld->l_type == flp->set.l_type)
nflp = NULL;
case S_BEFORE|E_AFTER:
delflck(lck_list, flp);
idp->filocks--;
break;
case S_START|E_MIDDLE:
insrtp = flp->prev;
case S_MIDDLE|E_MIDDLE:
/*
* Don't bother if this is in the middle of
* an already similarly set section.
*/
if (ld->l_type == flp->set.l_type)
return 0;
case S_BEFORE|E_MIDDLE:
if (ld->l_type == flp->set.l_type)
ld->l_end = flp->set.l_end;
else {
/* setup piece after end of (un)lock */
register struct filock *tdi, *tdp;
struct flock td;
td = flp->set;
td.l_start = ld->l_end + 1;
tdp = tdi = flp;
do {
if (tdp->set.l_start < td.l_start)
tdi = tdp;
else
break;
} while (tdp = tdp->next);
if (insflck(lck_list, &td, tdi) == NULL) {
return ENOLCK;
} else {
idp->filocks++;
}
}
if (regtyp == (S_MIDDLE|E_MIDDLE)) {
/* setup piece before (un)lock */
flp->set.l_end = ld->l_start - 1;
insrtp = flp;
} else {
delflck(lck_list, flp);
idp->filocks--;
}
nflp = NULL;
break;
case S_MIDDLE|E_END:
/*
* Don't bother if this is in the middle of
* an already similarly set section.
*/
if (ld->l_type == flp->set.l_type)
return 0;
flp->set.l_end = ld->l_start - 1;
insrtp = flp;
break;
case S_MIDDLE|E_AFTER:
if (ld->l_type == flp->set.l_type) {
ld->l_start = flp->set.l_start;
insrtp = flp->prev;
delflck(lck_list, flp);
idp->filocks--;
} else {
flp->set.l_end = ld->l_start - 1;
insrtp = flp;
}
break;
case S_END|E_AFTER:
if (ld->l_type == flp->set.l_type) {
ld->l_start = flp->set.l_start;
insrtp = flp->prev;
delflck(lck_list, flp);
idp->filocks--;
}
break;
case S_AFTER|E_AFTER:
insrtp = flp;
break;
}
}
}
if (ld->l_type != F_UNLCK) {
if (flp = insrtp) {
do {
if (flp->set.l_start < ld->l_start)
insrtp = flp;
else
break;
} while (flp = flp->next);
}
if (insflck(lck_list, ld, insrtp) == NULL) {
return ENOLCK;
} else {
idp->filocks++;
}
}
assert(idp->filocks >= 0);
return 0;
}
/*
* blocked() checks whether a new lock (lckdat) would be
* blocked by a previously set lock owned by another process.
* Insrt is set to point to the lock where lock list updating
* should begin to place the new lock.
*/
static struct filock *
blocked(struct filock *flp,
struct flock *lckdat,
struct filock **insrt)
{
register struct filock *f;
*insrt = NULL;
for (f = flp; f != NULL; f = f->next) {
if (f->set.l_start < lckdat->l_start)
*insrt = f;
else
break;
if( SAMEOWNER(&(f->set), lckdat) ) {
if ((lckdat->l_start-1) <= f->set.l_end)
break;
} else if (lckdat->l_start <= f->set.l_end
&& (f->set.l_type == F_WRLCK
|| (f->set.l_type == F_RDLCK && lckdat->l_type == F_WRLCK)))
return f;
}
for (; f != NULL; f = f->next) {
if (lckdat->l_end < f->set.l_start)
break;
if (lckdat->l_start <= f->set.l_end
&& ( !SAMEOWNER(&(f->set), lckdat) )
&& (f->set.l_type == F_WRLCK
|| (f->set.l_type == F_RDLCK
&& lckdat->l_type == F_WRLCK)))
return f;
}
return NULL;
}
static int
isonlist(struct filock *lck_list, struct filock *fl)
{
struct filock *fi;
for (fi = lck_list; fi; fi = fi->next)
if (fi == fl)
return 1;
if (debug) {
printf("lock type %d, start %d, len %d not on list\n", fl->set.l_type,
(int)fl->set.l_start, (int)fl->set.l_len );
}
return 0;
}
/*
* add/remove a lock record to the specified file for the lock described by
* flp
* coalesce lock entries where possible and keep the list sorted
* This mimics what the kernel does.
* The code for reclock was extracted verbatim from the kernel.
*/
int
record_lock(int fd, struct reclock *reclk, struct flock *lckdat)
{
struct filock *found, *insrt = NULL, *new;
errno = 0;
assert(lckdat->l_whence == 0);
/* Convert l_len to be the end of the rec lock l_end */
if (lckdat->l_len < 0) {
errno = EINVAL;
return -1;
}
if (lckdat->l_len == 0)
lckdat->l_end = MAXEND;
else
lckdat->l_end += (lckdat->l_start - 1);
/* check for arithmetic overflow */
if (lckdat->l_start > lckdat->l_end) {
errno = EINVAL;
return -1;
}
switch (lckdat->l_type) {
case F_RDLCK:
case F_WRLCK:
if ((found = blocked(fd_table[fd].filocks, lckdat, &insrt))
== NULL) {
reclk->blk.flp = NULL;
errno = flckadj(&fd_table[fd].filocks, insrt, lckdat);
} else if (!(reclk->blk.flp = insflck(&fd_table[fd].blocked,
lckdat, NULL))) {
errno = ENOLCK;
}
break;
case F_UNLCK:
/* removing a file record lock */
errno = flckadj(&fd_table[fd].filocks, fd_table[fd].filocks,
lckdat);
break;
default:
/* invalid lock type */
errno = EINVAL;
break;
}
/* Restore l_len */
if (lckdat->l_end == MAXEND)
lckdat->l_len = 0;
else
lckdat->l_len -= (lckdat->l_start-1);
return( (errno == 0) ? 0 : -1 );
}