1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-25 23:21:54 +03:00
samba-mirror/source3/locking/locking.c
Andrew Tridgell 0c33046a0a - added "netbios name" option in smb.conf to make controlling the name
that samba uses possible

- added "socket address" option to allow virtual SMB servers (on
systems with IP aliasing line Linux)

- disabled FAST_SHARE_MODES by default in Linux as older Linux boxes
can't do shared writeable mappings. We really need autoconf ...

- added new option types in loadparm so a string type can be specified
ot be uppercase only, this is used for the workgroup and netbios name
options

- auto-create the lock directory if it doesn't exist in shared mem
startup

- get rid of announce_backup()

- change a few comments in nmbd code

- rewrote the chaining code completely. Hopefully it will handle any
depth chains now.

- added LPRng support
(This used to be commit e9eac6cd49)
1996-08-19 11:17:29 +00:00

707 lines
17 KiB
C

/*
Unix SMB/Netbios implementation.
Version 1.9.
Locking functions
Copyright (C) Andrew Tridgell 1992-1996
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Revision History:
12 aug 96: Erik.Devriendt@te6.siemens.be
added support for shared memory implementation of share mode locking
*/
#include "includes.h"
extern int DEBUGLEVEL;
extern connection_struct Connections[];
extern files_struct Files[];
pstring share_del_pending="";
/****************************************************************************
routine to do file locking
****************************************************************************/
BOOL fcntl_lock(int fd,int op,uint32 offset,uint32 count,int type)
{
#if HAVE_FCNTL_LOCK
struct flock lock;
int ret;
#if 1
uint32 mask = 0xC0000000;
/* make sure the count is reasonable, we might kill the lockd otherwise */
count &= ~mask;
/* the offset is often strange - remove 2 of its bits if either of
the top two bits are set. Shift the top ones by two bits. This
still allows OLE2 apps to operate, but should stop lockd from
dieing */
if ((offset & mask) != 0)
offset = (offset & ~mask) | ((offset & mask) >> 2);
#else
uint32 mask = ((unsigned)1<<31);
/* interpret negative counts as large numbers */
if (count < 0)
count &= ~mask;
/* no negative offsets */
offset &= ~mask;
/* count + offset must be in range */
while ((offset < 0 || (offset + count < 0)) && mask)
{
offset &= ~mask;
mask = mask >> 1;
}
#endif
DEBUG(5,("fcntl_lock %d %d %d %d %d\n",fd,op,(int)offset,(int)count,type));
lock.l_type = type;
lock.l_whence = SEEK_SET;
lock.l_start = (int)offset;
lock.l_len = (int)count;
lock.l_pid = 0;
errno = 0;
ret = fcntl(fd,op,&lock);
if (errno != 0)
DEBUG(3,("fcntl lock gave errno %d (%s)\n",errno,strerror(errno)));
/* a lock query */
if (op == F_GETLK)
{
if ((ret != -1) &&
(lock.l_type != F_UNLCK) &&
(lock.l_pid != 0) &&
(lock.l_pid != getpid()))
{
DEBUG(3,("fd %d is locked by pid %d\n",fd,lock.l_pid));
return(True);
}
/* it must be not locked or locked by me */
return(False);
}
/* a lock set or unset */
if (ret == -1)
{
DEBUG(3,("lock failed at offset %d count %d op %d type %d (%s)\n",
offset,count,op,type,strerror(errno)));
/* perhaps it doesn't support this sort of locking?? */
if (errno == EINVAL)
{
DEBUG(3,("locking not supported? returning True\n"));
return(True);
}
return(False);
}
/* everything went OK */
DEBUG(5,("Lock call successful\n"));
return(True);
#else
return(False);
#endif
}
/*******************************************************************
lock a file - returning a open file descriptor or -1 on failure
The timeout is in seconds. 0 means no timeout
********************************************************************/
int file_lock(char *name,int timeout)
{
int fd = open(name,O_RDWR|O_CREAT,0666);
time_t t=0;
if (fd < 0) return(-1);
#if HAVE_FCNTL_LOCK
if (timeout) t = time(NULL);
while (!timeout || (time(NULL)-t < timeout)) {
if (fcntl_lock(fd,F_SETLK,0,1,F_WRLCK)) return(fd);
msleep(LOCK_RETRY_TIMEOUT);
}
return(-1);
#else
return(fd);
#endif
}
/*******************************************************************
unlock a file locked by file_lock
********************************************************************/
void file_unlock(int fd)
{
if (fd<0) return;
#if HAVE_FCNTL_LOCK
fcntl_lock(fd,F_SETLK,0,1,F_UNLCK);
#endif
close(fd);
}
/****************************************************************************
utility function called to see if a file region is locked
****************************************************************************/
BOOL is_locked(int fnum,int cnum,uint32 count,uint32 offset)
{
int snum = SNUM(cnum);
if (count == 0)
return(False);
if (!lp_locking(snum) || !lp_strict_locking(snum))
return(False);
return(fcntl_lock(Files[fnum].fd,F_GETLK,offset,count,
(Files[fnum].can_write?F_WRLCK:F_RDLCK)));
}
/****************************************************************************
utility function called by locking requests
****************************************************************************/
BOOL do_lock(int fnum,int cnum,uint32 count,uint32 offset,int *eclass,uint32 *ecode)
{
BOOL ok = False;
if (!lp_locking(SNUM(cnum)))
return(True);
if (count == 0) {
*eclass = ERRDOS;
*ecode = ERRnoaccess;
return False;
}
if (Files[fnum].can_lock && OPEN_FNUM(fnum) && (Files[fnum].cnum == cnum))
ok = fcntl_lock(Files[fnum].fd,F_SETLK,offset,count,
(Files[fnum].can_write?F_WRLCK:F_RDLCK));
if (!ok) {
*eclass = ERRDOS;
*ecode = ERRlock;
return False;
}
return True; /* Got lock */
}
/****************************************************************************
utility function called by unlocking requests
****************************************************************************/
BOOL do_unlock(int fnum,int cnum,uint32 count,uint32 offset,int *eclass,uint32 *ecode)
{
BOOL ok = False;
if (!lp_locking(SNUM(cnum)))
return(True);
if (Files[fnum].can_lock && OPEN_FNUM(fnum) && (Files[fnum].cnum == cnum))
ok = fcntl_lock(Files[fnum].fd,F_SETLK,offset,count,F_UNLCK);
if (!ok) {
*eclass = ERRDOS;
*ecode = ERRlock;
return False;
}
return True; /* Did unlock */
}
#if FAST_SHARE_MODES
/*******************************************************************
initialize the shared memory for share_mode management
******************************************************************/
BOOL start_share_mode_mgmt(void)
{
pstring shmem_file_name;
strcpy(shmem_file_name,lp_lockdir());
if (!directory_exist(shmem_file_name,NULL))
mkdir(shmem_file_name,0755);
trim_string(shmem_file_name,"","/");
if (!*shmem_file_name) return(False);
strcat(shmem_file_name, "/SHARE_MEM_FILE");
return shm_open(shmem_file_name, SHMEM_SIZE);
}
/*******************************************************************
deinitialize the shared memory for share_mode management
******************************************************************/
BOOL stop_share_mode_mgmt(void)
{
return shm_close();
}
#else
/* SHARE MODE LOCKS USING SLOW DESCRIPTION FILES */
/*******************************************************************
name a share file
******************************************************************/
static BOOL share_name(int cnum,struct stat *st,char *name)
{
strcpy(name,lp_lockdir());
standard_sub(cnum,name);
trim_string(name,"","/");
if (!*name) return(False);
name += strlen(name);
sprintf(name,"/share.%d.%d",(int)st->st_dev,(int)st->st_ino);
return(True);
}
/*******************************************************************
use the fnum to get the share file name
******************************************************************/
static BOOL share_name_fnum(int fnum,char *name)
{
struct stat st;
if (fstat(Files[fnum].fd,&st) != 0) return(False);
return(share_name(Files[fnum].cnum,&st,name));
}
#endif
/*******************************************************************
get the share mode of a file using the fnum
******************************************************************/
int get_share_mode_by_fnum(int cnum,int fnum,int *pid)
{
struct stat sbuf;
if (fstat(Files[fnum].fd,&sbuf) == -1) return(0);
return(get_share_mode(cnum,&sbuf,pid));
}
/*******************************************************************
get the share mode of a file using the files name
******************************************************************/
int get_share_mode_byname(int cnum,char *fname,int *pid)
{
struct stat sbuf;
if (stat(fname,&sbuf) == -1) return(0);
return(get_share_mode(cnum,&sbuf,pid));
}
/*******************************************************************
get the share mode of a file
********************************************************************/
int get_share_mode(int cnum,struct stat *sbuf,int *pid)
{
#if FAST_SHARE_MODES
share_mode_record *scanner_p;
share_mode_record *prev_p;
int ret;
BOOL found = False;
*pid = 0;
if(!shm_lock()) return (0);
scanner_p = (share_mode_record *)shm_offset2addr(shm_get_userdef_off());
prev_p = scanner_p;
while(scanner_p)
{
if( (scanner_p->st_dev == sbuf->st_dev) && (scanner_p->st_ino == sbuf->st_ino) )
{
found = True;
break;
}
else
{
prev_p = scanner_p ;
scanner_p = (share_mode_record *)shm_offset2addr(scanner_p->next_offset);
}
}
if(!found)
{
shm_unlock();
return (0);
}
if(scanner_p->locking_version != LOCKING_VERSION)
{
DEBUG(2,("Deleting old share mode record due to old locking version %d",scanner_p->locking_version));
if(prev_p == scanner_p)
shm_set_userdef_off(scanner_p->next_offset);
else
prev_p->next_offset = scanner_p->next_offset;
shm_free(shm_addr2offset(scanner_p));
*pid = 0;
shm_unlock();
return (0);
}
*pid = scanner_p->pid;
ret = scanner_p->share_mode;
if (*pid && !process_exists(*pid))
{
ret = 0;
*pid = 0;
}
if (! *pid)
{
if(prev_p == scanner_p)
shm_set_userdef_off(scanner_p->next_offset);
else
prev_p->next_offset = scanner_p->next_offset;
shm_free(shm_addr2offset(scanner_p));
}
if (*pid)
DEBUG(5,("Read share mode record mode 0x%X pid=%d\n",ret,*pid));
if(!shm_unlock()) return (0);
return(ret);
#else
pstring fname;
int fd2;
char buf[16];
int ret;
time_t t;
*pid = 0;
if (!share_name(cnum,sbuf,fname)) return(0);
fd2 = open(fname,O_RDONLY,0);
if (fd2 < 0) return(0);
if (read(fd2,buf,16) != 16) {
close(fd2);
unlink(fname);
return(0);
}
close(fd2);
t = IVAL(buf,0);
ret = IVAL(buf,4);
*pid = IVAL(buf,8);
if (IVAL(buf,12) != LOCKING_VERSION) {
if (!unlink(fname)) DEBUG(2,("Deleted old locking file %s",fname));
*pid = 0;
return(0);
}
if (*pid && !process_exists(*pid)) {
ret=0;
*pid = 0;
}
if (! *pid) unlink(fname); /* XXXXX race, race */
if (*pid)
DEBUG(5,("Read share file %s mode 0x%X pid=%d\n",fname,ret,*pid));
return(ret);
#endif
}
/*******************************************************************
del the share mode of a file, if we set it last
********************************************************************/
void del_share_mode(int fnum)
{
#if FAST_SHARE_MODES
struct stat st;
time_t t=0;
int pid=0;
BOOL del = False;
share_mode_record *scanner_p;
share_mode_record *prev_p;
BOOL found = False;
if (fstat(Files[fnum].fd,&st) != 0) return;
if (!shm_lock()) return;
scanner_p = (share_mode_record *)shm_offset2addr(shm_get_userdef_off());
prev_p = scanner_p;
while(scanner_p)
{
if( (scanner_p->st_dev == st.st_dev) && (scanner_p->st_ino == st.st_ino) )
{
found = True;
break;
}
else
{
prev_p = scanner_p ;
scanner_p = (share_mode_record *)shm_offset2addr(scanner_p->next_offset);
}
}
if(!found)
{
shm_unlock();
return;
}
t = scanner_p->time;
pid = scanner_p->pid;
if( (scanner_p->locking_version != LOCKING_VERSION) || !pid || !process_exists(pid))
del = True;
if (!del && t == Files[fnum].open_time && pid==(int)getpid())
del = True;
if (del)
{
DEBUG(2,("Deleting share mode record\n"));
if(prev_p == scanner_p)
shm_set_userdef_off(scanner_p->next_offset);
else
prev_p->next_offset = scanner_p->next_offset;
shm_free(shm_addr2offset(scanner_p));
}
shm_unlock();
return;
#else
pstring fname;
int fd2;
char buf[16];
time_t t=0;
int pid=0;
BOOL del = False;
if (!share_name_fnum(fnum,fname)) return;
fd2 = open(fname,O_RDONLY,0);
if (fd2 < 0) return;
if (read(fd2,buf,16) != 16)
del = True;
close(fd2);
if (!del) {
t = IVAL(buf,0);
pid = IVAL(buf,8);
}
if (!del)
if (IVAL(buf,12) != LOCKING_VERSION || !pid || !process_exists(pid))
del = True;
if (!del && t == Files[fnum].open_time && pid==(int)getpid())
del = True;
if (del) {
if (!unlink(fname))
DEBUG(2,("Deleted share file %s\n",fname));
else {
DEBUG(3,("Pending delete share file %s\n",fname));
if (*share_del_pending) DEBUG(0,("Share del clash!\n"));
strcpy(share_del_pending,fname);
}
}
#endif
}
/*******************************************************************
set the share mode of a file
********************************************************************/
BOOL set_share_mode(int fnum,int mode)
{
#if FAST_SHARE_MODES
int pid = (int)getpid();
struct stat st;
shm_offset_t new_off;
share_mode_record *new_p;
if (fstat(Files[fnum].fd,&st) != 0) return(False);
if (!shm_lock()) return (False);
new_off = shm_alloc(sizeof(share_mode_record) + strlen(Files[fnum].name) );
if (new_off == NULL_OFFSET) return (False);
new_p = (share_mode_record *)shm_offset2addr(new_off);
new_p->locking_version = LOCKING_VERSION;
new_p->share_mode = mode;
new_p->time = Files[fnum].open_time;
new_p->pid = pid;
new_p->st_dev = st.st_dev;
new_p->st_ino = st.st_ino;
strcpy(new_p->file_name,Files[fnum].name);
new_p->next_offset = shm_get_userdef_off();
shm_set_userdef_off(new_off);
DEBUG(3,("Created share record for %s with mode 0x%X pid=%d\n",Files[fnum].name,mode,pid));
if (!shm_unlock()) return (False);
return(True);
#else
pstring fname;
int fd2;
char buf[16];
int pid = (int)getpid();
if (!share_name_fnum(fnum,fname)) return(False);
{
int old_umask = umask(0);
fd2 = open(fname,O_WRONLY|O_CREAT|O_TRUNC,0644);
umask(old_umask);
}
if (fd2 < 0) {
DEBUG(2,("Failed to create share file %s\n",fname));
return(False);
}
SIVAL(buf,0,Files[fnum].open_time);
SIVAL(buf,4,mode);
SIVAL(buf,8,pid);
SIVAL(buf,12,LOCKING_VERSION);
if (write(fd2,buf,16) != 16) {
close(fd2);
unlink(fname);
return(False);
}
write(fd2,Files[fnum].name,strlen(Files[fnum].name)+1);
close(fd2);
DEBUG(3,("Created share file %s with mode 0x%X pid=%d\n",fname,mode,pid));
return(True);
#endif
}
/*******************************************************************
cleanup any stale share files
********************************************************************/
void clean_share_modes(void)
{
#ifdef USE_SHMEM
share_mode_record *scanner_p;
share_mode_record *prev_p;
int pid;
if (!shm_lock()) return;
scanner_p = (share_mode_record *)shm_offset2addr(shm_get_userdef_off());
prev_p = scanner_p;
while(scanner_p)
{
pid = scanner_p->pid;
if( (scanner_p->locking_version != LOCKING_VERSION) || !process_exists(pid))
{
DEBUG(2,("Deleting stale share mode record"));
if(prev_p == scanner_p)
{
shm_set_userdef_off(scanner_p->next_offset);
shm_free(shm_addr2offset(scanner_p));
scanner_p = (share_mode_record *)shm_offset2addr(shm_get_userdef_off());
prev_p = scanner_p;
}
else
{
prev_p->next_offset = scanner_p->next_offset;
shm_free(shm_addr2offset(scanner_p));
scanner_p = (share_mode_record *)shm_offset2addr(prev_p->next_offset);
}
}
else
{
prev_p = scanner_p ;
scanner_p = (share_mode_record *)shm_offset2addr(scanner_p->next_offset);
}
}
shm_unlock();
return;
#else
char *lockdir = lp_lockdir();
void *dir;
char *s;
if (!*lockdir) return;
dir = opendir(lockdir);
if (!dir) return;
while ((s=readdirname(dir))) {
char buf[16];
int pid;
int fd;
pstring lname;
int dev,inode;
if (sscanf(s,"share.%d.%d",&dev,&inode)!=2) continue;
strcpy(lname,lp_lockdir());
trim_string(lname,NULL,"/");
strcat(lname,"/");
strcat(lname,s);
fd = open(lname,O_RDONLY,0);
if (fd < 0) continue;
if (read(fd,buf,16) != 16) {
close(fd);
if (!unlink(lname))
printf("Deleted corrupt share file %s\n",s);
continue;
}
close(fd);
pid = IVAL(buf,8);
if (IVAL(buf,12) != LOCKING_VERSION || !process_exists(pid)) {
if (!unlink(lname))
printf("Deleted stale share file %s\n",s);
}
}
closedir(dir);
#endif
}