Category : Network Files
Archive   : SOSS_SRC.ZIP
Filename : FILES.C

 
Output of file : FILES.C contained in archive : SOSS_SRC.ZIP

/*
* files.c --
* File manipulation procedures. This module is highly machine
* dependent.
*
* Author:
* See-Mong Tan
* Modified by:
* Rich Braun @ Kronos
*
* Revision history:
*
* $Log: files.c_v $
* Revision 1.6 1991/05/13 17:43:50 richb
* Correct the return value of file_freeblocks so it won't produce
* an error if there are 0 free blocks.
*
* Revision 1.5 1991/04/17 18:04:30 richb
* Correct the modification time stored when a file block is
* written. (small bug in 3.1.)
*
* Revision 1.4 1991/04/11 20:36:51 richb
* Add name truncation support.
*
* Revision 1.3 1991/03/15 22:39:19 richb
* Several mods: fixed unlink and rename.
*
*/

#ifdef RCSID
static char _rcsid_ = "$Id: files.c_v 1.6 1991/05/13 17:43:50 richb Exp $";
#endif

#include "common.h"
#include "msc-dos.h" /* for dos drive and file routines */
#include /* for dos directory ops */
#include /* these ... */
#include /* for ... */
#include /* low level DOS access */

/* Default permissions for files are 555 for read-only and 777 */
/* for writeable. The low-order 9 bits can be modified by the */
/* administrator. */
u_short uperm_rdonly = UPERM_FILE | UPERM_READ | UPERM_EXEC;
u_short uperm_write = UPERM_FILE | UPERM_READ | UPERM_WRITE | UPERM_EXEC;
u_short uperm_dir = UPERM_DIR | UPERM_READ | UPERM_WRITE | UPERM_SEARCH;

#ifdef NOVELL
#include "novell.h" /* Novell definitions */
static struct _uidmap {
u_long unix_ID;
u_long Novell_ID;
} *uIDmappings = NULL;
static int uIDcount;
static u_long uIDoffset;
static u_long gID;
static u_short novelldirprot;

static bool_t ldnovell (u_long, char *, struct nfsfattr *);
#endif /* NOVELL */

#ifdef DOSAUTH
/* Support for handling file authentication */
struct _authent {
u_short uid; /* UID of file owner */
u_short gid; /* GID of file owner */
u_short mode; /* Protection mode bits */
u_short reserved;
};
#define AUTHSIZE (sizeof (struct _authent))
#define AUTHFILE "AUTHS.DMP"
static int authfh = 0;
#endif /* DOSAUTH */

static int file_nsubdirs(char *);
static bool_t file_readattr(char *, struct nfsfattr *);
static void cvtd2uattr (u_long, struct find_t *, struct nfsfattr *);

#define DRIVE_SCALINGFACTOR 2 /* scaling factor for dos drive blocks */

/*
* bool_t file_getattr(char *path, struct nfsfattr *attr) --
* Gets attributes for designated file or directory and puts
* the information in *attr. Returns TRUE if successful, FALSE otherwise.
*/
bool_t file_getattr(path, attr)
char *path;
struct nfsfattr *attr;
{
if (path == NULL)
return FALSE;

/* See if info is cached in the inode tree */

if (inattrget (pntoin (path), attr) != NULL)
return TRUE;

/* Else go out to disk */
return file_readattr(path, attr);
}

/*
* bool_t file_readattr(char *path, struct nfsfattr *attr) --
* Reads file attributes for a file from disk.
* Returns TRUE if successful, FALSE otherwise.
* Adds file to directory tree if the file does not already exist in
* the tree.
*/
static bool_t file_readattr(path, attr)
char *path;
struct nfsfattr *attr;
{
struct find_t findbuf;
u_long nodeid;
char npath [10];

/* Special-case the root directory */
if (strlen (path) == 2 && path[1] == ':') {

(void) bzero(attr, sizeof(struct nfsfattr));
attr->na_blocksize = NFS_MAXDATA;

/* Get attributes of volume label */
sprintf (npath, "%s\\*.*", path);
if (_dos_findfirst(npath, _A_VOLID, &findbuf) != 0) {

/* Load attributes from findbuf */
cvtd2uattr (nodeid, &findbuf, attr);
}
/* Set directory attributes */
attr->na_type = NFDIR;
attr->na_mode = uperm_dir;
attr->na_nlink = 2;
attr->na_blocks = 1; /* just say 1 block */
attr->na_size = 1024;

/* cache this info */
inattrset (nodeid, attr);
return TRUE;
}

/* Look for the file given */
else if (_dos_findfirst(path, _A_NORMAL | _A_SUBDIR | _A_RDONLY,
&findbuf) != 0) {
/* not successful */
if ((nodeid = pntoin(path)) != -1)
inremnode (nodeid);
return FALSE;
}
else {
if ((nodeid = pntoin(path)) == -1)
nodeid = addpathtodirtree(path);

/* Load attributes from findbuf */
(void) bzero(attr, sizeof(struct nfsfattr));
cvtd2uattr (nodeid, &findbuf, attr);

if (findbuf.attrib & _A_SUBDIR)
attr->na_mode = uperm_dir;
else if (findbuf.attrib & _A_RDONLY)
attr->na_mode = uperm_rdonly;
else
attr->na_mode = uperm_write;

#ifdef NOVELL
/* Novell stuff */
(void) ldnovell (nodeid, path+2, attr);
#endif
#ifdef DOSAUTH
/* Read authentication entry */
if (authfh != 0 && lseek (authfh, nodeid * AUTHSIZE, SEEK_SET) ==
nodeid * AUTHSIZE) {
struct _authent authentry;
if (read (authfh, &authentry, AUTHSIZE) == AUTHSIZE) {
attr->na_uid = authentry.uid;
attr->na_gid = authentry.gid;
attr->na_mode = authentry.mode;
}
}
#endif /* DOSAUTH */
/* cache this info */
inattrset (nodeid, attr);
return TRUE;
}
}

static void cvtd2uattr (nodeid, findbuf, attr)
u_long nodeid;
struct find_t *findbuf;
struct nfsfattr *attr;
{
int handle; /* DOS file handle */

/* file protection bits and type */
if (findbuf->attrib & _A_SUBDIR) { /* subdirectory */
attr->na_type = NFDIR;
attr->na_nlink = 2; /*** + file_nsubdirs(path) ***/
/* # of subdirectories plus this one */
}
else if (findbuf->attrib & _A_RDONLY) { /* rdonly */
attr->na_type = NFREG;
attr->na_nlink = 1;
}
else {
attr->na_type = NFREG; /* normal file */
attr->na_nlink = 1;
}

/* file size in bytes */
if (findbuf->attrib & _A_SUBDIR) { /* directory */
attr->na_blocks = 1; /* just say 1 block */
attr->na_size = 1024;
}
else {
attr->na_size = findbuf->size;
attr->na_blocks = findbuf->size / BASIC_BLOCKSIZE +
(findbuf->size % BASIC_BLOCKSIZE == 0 ? 0 : 1);
}
/* preferred transfer size in blocks */
attr->na_blocksize = NFS_MAXDATA;

/* device # == drive # */
attr->na_fsid = ingetfsid (nodeid);
attr->na_rdev = attr->na_fsid;

/* inode # */
attr->na_nodeid = nodeid;

/* time of last access */
attr->na_atime.tv_usec = 0;
attr->na_atime.tv_sec = unixtime(findbuf->wr_time, findbuf->wr_date);

/* time of last write */
attr->na_mtime = attr->na_atime; /* note all times are the same */

/* time of last change */
attr->na_ctime = attr->na_atime;
}

#ifdef NOVELL
/*
* u_char novell_GDH (int fsid) --
* Get Novell directory handle for a filesystem.
*/
u_char novell_GDH (fsid)
int fsid;
{
union REGS regsin, regsout;

/* Get the file system's directory handle */
regsin.x.ax = 0xE900;
regsin.x.dx = fsid - 1;
intdos (®sin, ®sout);

/* Return -1 error code if the permanent directory bit is not set or */
/* the local-directory bit is set. */
if ((regsout.h.ah & 0x83) != 1)
return 255;

return regsout.h.al;
}

/*
* void ldnovell (u_long nodeid, char *path, struct nfsfattr *attr) --
* Loads attributes of a Novell network file
*/
static bool_t ldnovell (nodeid, path, attr)
u_long nodeid;
char *path;
struct nfsfattr *attr;
{
FILE *fp, *fopen();
int i;
int stat;
u_char handle;
char fcn [10];
union REGS regsin, regsout;
struct SREGS segregs;

if ((handle = novell_GDH (ingetfsid (nodeid))) == 255)
return FALSE;

/* Initialize the Novell ID mapping table if not set */
if (uIDmappings == NULL) {
uIDmappings = (struct _uidmap *) malloc (sizeof (struct _uidmap) *
MAXNOVELLID);
if (uIDmappings == NULL) {
fprintf (stderr, "out of memory\n");
abort();
}
uIDoffset = uIDcount = gID = 0;

/* Default protection is 775 */
novelldirprot = UPERM_OWNER | UPERM_GROUP | UPERM_READ | UPERM_SEARCH;

if ((fp = fopen(IDFILE, "r")) == NULL) {
fprintf (stderr, ">>> File %s missing\n", IDFILE);
}
else {
while(fscanf(fp, "%s", fcn) != EOF) {
/* Check command line for 'offset', 'group', 'user', */
/* 'protection'. */
if (fcn[0] == 'o')
fscanf(fp, "%ld", &uIDoffset);
else if (fcn[0] == 'g')
fscanf(fp, "%ld", &gID);
else if (fcn[0] == 'p')
fscanf(fp, "%o", &novelldirprot);
else if (fcn[0] == 'u') {
char user[20];
fscanf(fp, "%s %ld %ld", user,
&uIDmappings[uIDcount].unix_ID,
&uIDmappings[uIDcount].Novell_ID);
if (uIDcount < MAXNOVELLID-1)
uIDcount++;
}
}
fclose (fp);
}
}

segregs.ds = get_ds();
segregs.es = segregs.ds;

if (attr->na_type != NFDIR) {
/* Set up the Scan File Information request block, as defined on */
/* page 283 of the Novell API Reference, rev 1.00. */

static struct _sfireq sfireq = /* These are placed in static */
{0, 0x0F, -1, -1, 0, 0, 0}; /* memory because code here */
static struct _sfirep sfirep; /* assumes seg register DS. */

sfireq.handle = handle;
sfireq.pathlen = strlen (path);
sfireq.len = 6 + sfireq.pathlen;
sfirep.len = sizeof (sfirep) -2;
strcpy (sfireq.path, path);

novell_API(0xE3, &sfireq, &sfirep, regsin, ®sout, &segregs);
if (regsout.h.al != 0) {
DBGPRT2 (nfsdebug, "%s sfi err %d", path, regsout.h.al);
return FALSE;
}
attr->na_uid = sfirep.info.ownerID;
attr->na_gid = gID;
}
else {
/* Special case for directories: invoke Scan Directory For */
/* Trustees system call, defined on p. 280 of Novell API Ref. */

/* Load the protection bits */
attr->na_mode = UPERM_DIR | novelldirprot;
#if 0
/* SDFT not supported because Novell allows a directory to be */
/* a member of several groups while Unix only attaches one */
/* group ID to the file. We punt and just return the same */
/* group every time, to avoid confusion. */

static struct _sdftreq sdftreq = {0, 0x0C, 0, 0, 0};
static struct _sdftrep sdftrep;

sdftreq.handle = handle;
sdftreq.pathlen = strlen (path);
sdftreq.len = 4 + sdftreq.pathlen;
sdftrep.len = sizeof (sdftrep) -2;
strcpy (sdftreq.path, path);

novell_API (0xE2, &sdftreq, &sdftrep, regsin, ®sout, &segregs);
if (regsout.h.al == 0) {
attr->na_uid = sdftrep.ownerID;
}
else
#endif /* 0 */
{
/* If SDFT call failed, usually due to access problems, */
/* use the Scan Directory Info call. */
static struct _sdireq sdireq = {0, 0x02, 0, 0, 0};
static struct _sdirep sdirep;

sdireq.handle = handle;
sdireq.pathlen = strlen (path);
sdireq.len = 5 + sdireq.pathlen;
sdirep.len = sizeof (sdirep) -2;
strcpy (sdireq.path, path);

novell_API (0xE2, &sdireq, &sdirep, regsin, ®sout, &segregs);
DBGPRT3 (nfsdebug, "SDIREQ %d: %d %ld", sdireq.handle, regsout.h.al,
sdirep.ownerID);
if (regsout.h.al != 0)
return FALSE;
attr->na_uid = sdirep.ownerID;
attr->na_gid = gID;
}
}

DBGPRT2 (nfslookup, "%s ID = %ld", path, attr->na_uid);

/* Look for ID in mapping table */
for (i = 0; i < uIDcount; i++)
if (uIDmappings[i].Novell_ID == attr->na_uid) {
attr->na_uid = uIDmappings[i].unix_ID;
return TRUE;
}

/* Not found in mapping table: add the offset and return. */
attr->na_uid += uIDoffset;
return TRUE;
}
#endif /* NOVELL */

/*
* int file_nsubdirs(char *path) --
* Returns # of subdirectories in the given directory.
*/
static int file_nsubdirs(path)
char *path;
{
int subdirs = 0;
struct find_t ft;
char name[MAXPATHNAMELEN];
#define VALID(ft) (strcmp((ft).name, ".") != 0 && strcmp((ft).name, "..") != 0)

/* Hack: this routine eats a lot of time and doesn't work anyway. */

return 0;

#if 0
(void) strcpy(name, path);
(void) strcat(name, "\\*.*"); /* append wildcard */

if (_dos_findfirst(name, _A_SUBDIR, &ft) != 0)
if (VALID(ft))
subdirs++;
else
return 0;

while(_dos_findnext(&ft) == 0)
if (VALID(ft))
subdirs++;
DBGPRT2 (nfslookup, "dos_findfirst '%s', found %d subdirs",
name, subdirs);
return subdirs;
#endif /* 0 */

#undef VALID
}

/*
* int file_freeblocks(int drive, long *free, long *total) --
* Return # of free blocks in specified filesystem (ie. drive).
*/
int file_freeblocks(drive, free, total)
int drive;
long *free, *total;
{
struct diskfree_t df; /* disk free */

if (_dos_getdiskfree(drive, &df) != 0) { /* dos call */
(void) fprintf(stderr, "freeblocks: err, cannot read\n");
return -1;
}
/* DBGPRT2 (nfsdebug, "%c: blocks free %ld", 'A'+drive-1,
(long) df.avail_clusters * (long) df.sectors_per_cluster); */

*free = ((long)df.avail_clusters * (long)df.sectors_per_cluster)
/ DRIVE_SCALINGFACTOR;
*total = ((long)df.total_clusters * (long)df.sectors_per_cluster)
/ DRIVE_SCALINGFACTOR;
return 0;
}

/* a file pointer cache for read requests */
#define FRDCSIZ 10
static struct {
u_long nodeid;
FILE *fp;
} frdc_cache[FRDCSIZ]; /* up to ten cached file handles */

static int frdc_last; /* last location saved */

/*
* void frdc_save(u_long nodeid, FILE *fp) --
* Cache read file pointers.
*/
static void frdc_save(nodeid, fp)
u_long nodeid;
FILE *fp;
{
if (frdc_cache[frdc_last].fp != NULL)
(void) fclose(frdc_cache[frdc_last].fp); /* throw away */
frdc_cache[frdc_last].nodeid = nodeid;
frdc_cache[frdc_last++].fp = fp;
if (frdc_last == FRDCSIZ)
frdc_last = 0;
}

/*
* void frdc_del(u_long nodeid) --
* Delete saved file pointer from read cache. No effect if file
* was not cached. Closes file pointer also.
*/
static void frdc_del(nodeid)
u_long nodeid;
{
int i;

for(i = 0; i < FRDCSIZ; i++) {
if (frdc_cache[i].fp != NULL && frdc_cache[i].nodeid == nodeid) {
(void) fclose(frdc_cache[i].fp);
frdc_cache[i].fp = NULL;
return;
}
}
}

/*
* FILE *frdc_find(u_long nodeid) --
* Finds cached file pointer corresponding to nodeid, or NULL
* if no such file exists.
*/
FILE *frdc_find(nodeid)
u_long nodeid;
{
int i;

for(i = 0; i < FRDCSIZ; i++) {
if (frdc_cache[i].fp != NULL && frdc_cache[i].nodeid == nodeid)
return frdc_cache[i].fp;
}

return NULL;
}

/*
* int file_read(u_long nodeid, u_long offset,
* u_long count, char *buffer) --
* Reads count bytes at offset into buffer. Returns # of bytes read,
* and -1 if an error occurs, or 0 for EOF or null file.
*/
int file_read(nodeid, offset, count, buffer)
u_long nodeid;
u_long offset, count;
char *buffer;
{
FILE *fp;
bool_t saved = FALSE;
int bytes = 0;
char path [MAXPATHNAMELEN];

if ((fp = frdc_find(nodeid)) != NULL) {
saved = TRUE;
}
else if ((fp = fopen(intopn (nodeid, path), "rb")) == NULL)
return -1;

/* Seek to correct position */
if (fseek(fp, (long) offset, 0) != 0) {
if (!feof(fp)) {
(void) fclose(fp);
return -1;
}
else
return 0;
}

/* Read from the file */
bytes = fread(buffer, sizeof(char), (size_t) count, fp);
if (!saved)
frdc_save(nodeid, fp);

return bytes;
}

/*
* int file_rddir(u_long nodeid, u_long offs, struct udirect *udp) --
* Put file information at offs in directory at path in nfs cookie
* at *udp. Returns # of bytes in record, 0 if there are no more entries
* or -1 for a read error.
*/
int file_rddir(nodeid, offs, udp)
u_long nodeid;
u_long offs;
struct udirect *udp;
{
char filename[MAXPATHNAMELEN];
char npath[MAXPATHNAMELEN], path[MAXPATHNAMELEN];
struct find_t findbuf;
struct nfsfattr attr;
u_long fnode;

#define SUD 32 /* sizeof(struct udirect) */ /* full udp cookie size */

if (offs == 0) {
/* Force a read of the full directory if offset is zero. */

if (intopn (nodeid, path) == NULL)
return -1;

/* look for the first file in the directory */
(void) sprintf(npath, "%s\\*.*", path);
if (_dos_findfirst(npath, _A_NORMAL | _A_SUBDIR |
_A_RDONLY, &findbuf) == 0) {

/* Read file attributes from each entry into inode cache */
for (;;) {
/* convert to lowercase and get the full path */
(void) strtolower(findbuf.name);
(void) sprintf(filename, "%s\\%s", path, findbuf.name);

if ((fnode = pntoin(filename)) == -1)
fnode = addpathtodirtree(filename);
if (inattrget (fnode, &attr) == (struct nfsfattr *) NULL)
(void) bzero (&attr, sizeof (struct nfsfattr));
cvtd2uattr (fnode, &findbuf, &attr);
if (findbuf.attrib & _A_SUBDIR)
attr.na_mode = uperm_dir;
else if (findbuf.attrib & _A_RDONLY)
attr.na_mode = uperm_rdonly;
else
attr.na_mode = uperm_write;
#ifdef NOVELL
(void) ldnovell (fnode, filename+2, &attr);
#endif
/* cache this info */
inattrset (fnode, &attr);

/* fetch next entry */
if (_dos_findnext(&findbuf) != 0)
break;
}
}
}

/* fetch the proper inode */
if ((udp->d_fileno = ingetentry (nodeid, offs / SUD, udp->d_name)) == -1)
return -1;

/* store the remaining udp info */
udp->d_namlen = strlen(udp->d_name);
udp->d_offset = offs + SUD;
udp->d_reclen = UDIRSIZ(udp);

/* return 0 if this is the last entry */
if (ingetentry (nodeid, (offs / SUD) + 1, filename) == -1)
return 0;
else
return udp->d_reclen;

#undef SUD
}

/*
* char *strtolower(char *s) --
* Converts all characters in s to lower case. Returns s.
*/
char *strtolower(s)
char *s;
{
char *tmp;

tmp = s;
while(*s != '\0') {
if (isalpha(*s) && isupper(*s))
*s = tolower(*s);
s++;
}

return tmp;
}

/*
* enum nfsstat file_write(u_long nodeid, u_long offset,
* u_long count, char *buffer) --
* Write to file with name at offset, count bytes of data from buffer.
* File should already exist. Returns 0 for success, or some error
* code if not.
*/
enum nfsstat file_write(nodeid, offset, count, buffer)
u_long nodeid;
u_long offset;
u_long count;
char *buffer;
{
int handle; /* write file handle */
long newoff;
char name [MAXPATHNAMELEN];
int fw_num = WR_SIZ;
struct nfsfattr attr;
time_t now;

if (intopn (nodeid, name) == NULL)
return NFSERR_STALE;

frdc_del(nodeid); /* delete from read cache */

/* Get cached file attributes */
if (inattrget (nodeid, &attr) == (struct nfsfattr *) NULL) {
fprintf (stderr, "file_write: attrget failed\n");
}
else {
/* If the file is read-only, temporarily set it to read/write */
if (!(attr.na_mode & UCHK_WR))
(void) _dos_setfileattr(name, _A_NORMAL);
}

/* open for writing only */
handle = open(name, O_WRONLY | O_BINARY);
if (handle == -1)
return puterrno(errno); /* return error code */

DBGPRT4 (nfswrite, "%s, %ld bytes at %ld (len = %ld)",
name, count, offset, filelength (handle));

newoff = lseek(handle, offset, 0);
if (count < WR_SIZ) fw_num = count;
if (write(handle, buffer, fw_num) == -1) {
(void) close(handle);
return puterrno(errno); /* some error */
}

/* Update cached file attributes */
attr.na_size = filelength (handle);
(void) time (&now);
attr.na_atime.tv_usec = 0;
attr.na_atime.tv_sec = now;
attr.na_mtime = attr.na_atime;
attr.na_ctime = attr.na_atime;
(void) inattrset (nodeid, &attr);

(void) close(handle);

/* If the file is read-only, set its _A_RDONLY attribute */
if (!(attr.na_mode & UCHK_WR))
(void) _dos_setfileattr(name, _A_RDONLY);
return NFS_OK;
}

/*
* enum nfsstat file_create(char *name, struct nfssattr *sattr,
* struct nfsfattr *fattr)
* Creates a file with full path name and attributes sattr, and returns
* the file attributes in *fattr. Adds file to directory tree.
* Returns NFS_OK for success, or some error code for failure.
*/
enum nfsstat file_create(name, sattr, fattr)
char *name;
struct nfssattr *sattr;
struct nfsfattr *fattr;
{
int handle; /* file handle */
enum nfsstat stat;
u_long node;
int sattribs = S_IREAD; /* set attributes */

if (name == NULL)
return NFSERR_NOENT;

if ((stat = validate_path (name)) != NFS_OK)
return (stat);

if (sattr->sa_mode & UCHK_WR) /* file is writeable */
sattribs |= S_IWRITE; /* set DOS file to be read & write */

/* Remove the inode if assigned */
if ((node = pntoin (name)) != -1) {
frdc_del (node);
inremnode (node);
}

#if 0

/* obsolete code -- now get uid/gid from RPC header */
if (sattr->sa_uid == -1 || sattr->sa_gid == -1) {
char parent [MAXPATHNAMELEN];
struct nfsfattr pattr; /* attributes of parent */
char *strrchr();

/* Set up UID and GID defaults from parent inode */
strcpy (parent, name);
*strrchr (parent, '\\') = '\0';
if (!file_getattr (parent, &pattr)) {
DBGPRT1 (nfsdebug, "no attrs %s", parent);
}
else {
if (sattr->sa_uid == -1)
sattr->sa_uid = pattr.na_uid;
if (sattr->sa_gid == -1)
sattr->sa_uid = pattr.na_gid;
}
}
#endif

DBGPRT4 (nfsdebug, "file_create %s <%o> [%ld,%ld]", name,
(int) sattr->sa_mode, sattr->sa_uid, sattr->sa_gid);

/* check if file already exists */
if ((handle = open(name, O_CREAT | O_TRUNC, sattribs)) == -1)
return puterrno (errno);
close(handle);

/* Add to inode tree */
if ((node = pntoin(name)) == -1)
node = addpathtodirtree(name);

/* Set the file's ownership */
(void) file_setowner (node, sattr->sa_uid, sattr->sa_gid);

/* Read the file's attributes and return */
if (! file_readattr(name, fattr))
return NFSERR_IO; /* just created but not found now! */

return NFS_OK;
}

/*
* int file_setperm(char *path, long perm) --
* Sets file permissions depending on perm. Perm is of two types,
* uperm_write (regular file) or uperm_rdonly (read only).
* Returns 0 for success or some error code if an error occurs.
*/
enum nfsstat file_setperm(nodeid, perm)
u_long nodeid;
long perm;
{
int stat, attribs;
char path [MAXPATHNAMELEN];
struct nfsfattr attr;

if (intopn(nodeid, path) == NULL) /* get file name */
return NFSERR_STALE;

#ifndef DOSAUTH
if (perm & UPERM_WRITE) {
attribs = _A_NORMAL;
perm = uperm_write;
}
else {
attribs = _A_RDONLY;
perm = uperm_rdonly;
}
#endif

stat = _dos_setfileattr(path, attribs);
if (stat == 0) {
/* Update cached file attributes */
if (inattrget (nodeid, &attr) != (struct nfsfattr *) NULL) {
attr.na_mode = perm;
(void) inattrset (nodeid, &attr);
return NFS_OK;
}
} else
return puterrno(errno);
}

/*
* int file_setsize(u_long nodeid, long size) --
* Sets file size.
* Returns 0 for success or some error code if an error occurs.
*/
enum nfsstat file_setsize(nodeid, size)
u_long nodeid;
long size;
{
int stat, handle;
char path [MAXPATHNAMELEN];
struct nfsfattr attr;

if (intopn(nodeid, path) == NULL) /* get file name */
return NFSERR_STALE;

if (size == 0L) {
if ((handle = open(path, O_CREAT | O_TRUNC, S_IWRITE)) == -1)
return puterrno (errno);
close(handle);

/* Update cached file attributes */
if (inattrget (nodeid, &attr) != (struct nfsfattr *) NULL) {
attr.na_size = 0;
(void) inattrset (nodeid, &attr);
}
return NFS_OK;
}
else
return NFSERR_IO;
}

/*
* int file_settime(u_long nodeid, long secs) --
* Sets file time specified by secs (# of seconds since 0000, Jan 1, 1970.
* Returns 0 for success or some error code if an error occurs.
*/
enum nfsstat file_settime(nodeid, secs)
u_long nodeid;
long secs;
{
int stat, handle;
unsigned fdate, ftime;
char path [MAXPATHNAMELEN];
struct nfsfattr attr;

if (intopn(nodeid, path) == NULL) /* get file name */
return NFSERR_STALE;

/* must open file to change time */
if ((handle = open(path, O_RDONLY, S_IREAD)) == -1)
return puterrno(errno);

/* Convert Unix time format into DOS format */
dostime (secs, &fdate, &ftime);

stat = _dos_setftime(handle, fdate, ftime);

(void) close(handle);
if (stat != 0)
return puterrno(errno);
else {
/* Update cached file attributes */
if (inattrget (nodeid, &attr) != (struct nfsfattr *) NULL) {
attr.na_atime.tv_usec = 0;
attr.na_atime.tv_sec = secs;
attr.na_mtime = attr.na_atime;
attr.na_ctime = attr.na_atime;
(void) inattrset (nodeid, &attr);
}

return NFS_OK;
}
}

/*
* int file_setowner(u_long nodeid, long uid, long gid) --
* Sets file ownership values.
* This can only set the values in cache, because DOS doesn't
* support an on-disk representation.
*/
enum nfsstat file_setowner(nodeid, uid, gid)
u_long nodeid;
long uid, gid;
{
struct nfsfattr attr;
char path [MAXPATHNAMELEN];

if (intopn (nodeid, path) == NULL)
return NFSERR_NOENT;
DBGPRT3 (nfsdebug, "Setting owner to [%ld,%ld] %s", uid, gid, path);

if (file_getattr (path, &attr)) {
#ifdef NOVELL
if (!(attr.na_mode & UPERM_DIR) && uid != -1) {
/* Set up the Scan File Information request block, as defined on */
/* page 283 of the Novell API Reference, rev 1.00. */

static struct _sfireq sfireq = /* These are placed in static */
{0, 0x0F, -1, -1, 0, 0, 0}; /* memory because code here */
static struct _sfirep sfirep; /* assumes seg register DS. */
static struct _setfireq setfireq =
{0, 0x10, 0};
static struct _setfirep setfirep;
u_char handle;
union REGS regsin, regsout;
struct SREGS segregs;
int i;

if ((handle = novell_GDH (ingetfsid (nodeid))) != 255) {
segregs.ds = get_ds();
segregs.es = segregs.ds;

sfireq.handle = handle;
sfireq.pathlen = strlen (path+2);
sfireq.len = 6 + sfireq.pathlen;
sfirep.len = sizeof (sfirep) -2;
strcpy (sfireq.path, path+2);

novell_API(0xE3, &sfireq, &sfirep, regsin, ®sout, &segregs);
if (regsout.h.al != 0)
return NFSERR_IO;

/* Set up the Set File Info request block */
setfireq.handle = handle;
setfireq.pathlen = strlen (path+2);
setfireq.len = 4 + sizeof (struct _fileinfo) + sfireq.pathlen;
setfirep.len = sizeof (setfirep) -2;
strcpy (setfireq.path, path+2);
setfireq.info = sfirep.info;
setfireq.info.size = 0;

/* Look up the Novell user ID */
setfireq.info.ownerID = uid - uIDoffset;
for (i = 0; i < uIDcount; i++)
if (uIDmappings[i].unix_ID == uid + uIDoffset) {
setfireq.info.ownerID = uIDmappings[i].Novell_ID;
break;
}

/* Issue the Set File Information request */
novell_API(0xE3, &setfireq, &setfirep, regsin, ®sout,
&segregs);
if (regsout.h.al != 0)
return NFSERR_ACCES;
}
}
#endif /* NOVELL */

/* Update cached file attributes */
if (uid != -1)
attr.na_uid = uid;
if (gid != -1)
attr.na_gid = gid;
(void) inattrset (nodeid, &attr);
return NFS_OK;
}
else
return NFSERR_NOENT;
}

/*
* int file_unlink(char *name) --
* Removes named file.
*/
enum nfsstat file_unlink(name)
char *name;
{
u_long node;
int stat;

/* Close the file if we still have a handle to it */
if ((node = pntoin (name)) != -1)
frdc_del (node);

/* Reset file attributes */
(void) _dos_setfileattr(name, _A_NORMAL);

/* Call unlink library function to remove the file. */
stat = unlink(name);
DBGPRT3 (nfserr, "unlink %s: stat = %d, len = %d", name, stat, strlen(name));

/* Remove the inode associated with the file, if present. */
if (stat == 0 && node != -1)
inremnode (node);
return (stat == 0) ? NFS_OK : puterrno (errno);
}


/*
* int file_rename(char *oldname, char *newname) --
* Renames a file
*/
enum nfsstat file_rename(oldname, newname)
char *oldname, *newname;
{
u_long node;
int err;
struct stat buf;
enum nfsstat code;

/* Close the file if we still have a handle to it */
if ((node = pntoin (oldname)) != -1)
frdc_del (node);

/* Reset file attributes (e.g., read-only) */
(void) _dos_setfileattr(oldname, _A_NORMAL);

/* Validate the new filename */
if ((code = validate_path (newname)) != NFS_OK)
return (code);

/* Delete destination file if present */
if (stat (newname, &buf) == 0) {
if (buf.st_mode & S_IFDIR)
return NFSERR_ISDIR;
if ((code = file_unlink (newname)) != NFS_OK)
return code;
}

/* Call rename library function to rename the file. */
err = rename(oldname,newname);

/* Update the inode associated with the file, if present. */
if (err == 0 && node != -1) {
(void) inremnode (node);
(void) addpathtodirtree(newname);
}
return (err == 0) ? NFS_OK : puterrno (errno);
}

/*
* enum nfsstat validate_path (char *name) --
* Validate a path name's syntax. Returns 0 if OK.
* Modifies the path appropriately if NFS_TRUNCATENAMES is set.
*/
enum nfsstat validate_path (name)
char *name;
{
char *ptr, *ptr2, *strrchr(), *strchr();
int i;

if ((ptr = strrchr (name, '\\')) == (char *)NULL &&
(ptr = strrchr (name, ':')) == (char *)NULL)
ptr = name;
else
ptr++;

/* Check validity of characters in final component */
for (ptr2 = ptr; *ptr2; ) {
if (*ptr2 <= ' ' || (*ptr2 & 0x80) || !inchvalid[*ptr2 - '!'])
return NFSERR_INVAL;
else
ptr2++;
}

/* Verify there are no more than 8 chars before '.' */
if ((i = strcspn (ptr, ".")) > 8) {
if (!NFS_TRUNCATENAMES)
return NFSERR_NAMETOOLONG;
strcpy (ptr + 8, ptr + i);
}
if ((ptr = strchr (name, '.')) == (char *)NULL)
return NFS_OK;
else
ptr++;
if (strlen (ptr) > 3) {
if (!NFS_TRUNCATENAMES)
return NFSERR_NAMETOOLONG;
ptr[3] = '\0';
}
return NFS_OK;
}