403 lines
12 KiB
C
403 lines
12 KiB
C
/****************************************************************
|
|
* *
|
|
* Copyright 2001, 2013 Fidelity Information Services, Inc *
|
|
* *
|
|
* This source code contains the intellectual property *
|
|
* of its copyright holder(s), and is made available *
|
|
* under a license. If you do not know the terms of *
|
|
* the license, please stop and do not read further. *
|
|
* *
|
|
****************************************************************/
|
|
|
|
#include "mdef.h"
|
|
|
|
#include "gtm_inet.h" /* for struct in_addr */
|
|
#include "gtm_stat.h"
|
|
#include "gtm_string.h"
|
|
#include "gtm_stdio.h"
|
|
#include "gtm_unistd.h"
|
|
#include "gtm_socket.h" /* for using sockaddr and sockaddr_storage */
|
|
#include "gtm_netdb.h"
|
|
#include "gtm_ipv6.h"
|
|
|
|
#include "parse_file.h"
|
|
#include "io.h"
|
|
#include "iosp.h"
|
|
#include "eintr_wrappers.h"
|
|
#include "trans_log_name.h"
|
|
#include "setzdir.h"
|
|
#include "gtmmsg.h" /* for gtm_putmsg */
|
|
|
|
#define LOCALHOSTNAME "localhost"
|
|
#define LOCALHOSTNAME6 "::1"
|
|
|
|
error_def(ERR_FILENOTFND);
|
|
error_def(ERR_GETADDRINFO);
|
|
error_def(ERR_GETNAMEINFO);
|
|
error_def(ERR_PARBUFSM);
|
|
error_def(ERR_PARNORMAL);
|
|
error_def(ERR_SYSCALL);
|
|
error_def(ERR_TEXT);
|
|
|
|
enum parse_state
|
|
{
|
|
NOSTATE,
|
|
NAME,
|
|
DOT1,
|
|
DOT2,
|
|
SLASH
|
|
};
|
|
|
|
GBLREF mval dollar_zdir;
|
|
|
|
int4 parse_file(mstr *file, parse_blk *pblk)
|
|
{
|
|
struct stat statbuf;
|
|
struct addrinfo *ai_ptr = NULL, *localhost_ai_ptr = NULL, *temp_ai_ptr = NULL;
|
|
struct addrinfo hints;
|
|
mstr trans, tmp;
|
|
int status, diff, local_node_len, query_node_len, node_name_len;
|
|
parse_blk def;
|
|
char local_node_name[MAX_HOST_NAME_LEN + 1], query_node_name[MAX_HOST_NAME_LEN + 1];
|
|
char *base, *ptr, *top, *del, *node, *name, *ext, ch;
|
|
char **hostaddrlist;
|
|
char def_string[MAX_FBUFF + 1];
|
|
boolean_t hasnode, hasdir, hasname, hasext, wilddir, wildname;
|
|
enum parse_state state;
|
|
struct sockaddr_storage query_sas;
|
|
struct sockaddr localhost_sa, *localhost_sa_ptr;
|
|
mval def_trans;
|
|
int errcode;
|
|
|
|
pblk->fnb = 0;
|
|
assert(((unsigned int)pblk->buff_size + 1) <= (MAX_FBUFF + 1));
|
|
/* All callers of parse_blk set buff_size to 1 less than the allocated buffer. This is because buff_size is a char
|
|
* type (for historical reasons) and so cannot go more than 255 whereas we support a max of 255 characters. So we
|
|
* allocate buffers that contain one more byte (for the terminating '\0') but dont set that in buff_size. Use
|
|
* that extra byte for the trans_log_name call.
|
|
*/
|
|
status = TRANS_LOG_NAME(file, &trans, pblk->buffer, pblk->buff_size + 1, dont_sendmsg_on_log2long);
|
|
if (SS_LOG2LONG == status)
|
|
return ERR_PARBUFSM;
|
|
assert(trans.addr == pblk->buffer);
|
|
|
|
memset(&def, 0, SIZEOF(def)); /* initial the defaults to zero */
|
|
if (pblk->def1_size > 0)
|
|
{ /* Parse default filespec if supplied */
|
|
def.fop = F_SYNTAXO;
|
|
def.buffer = def_string;
|
|
def.buff_size = MAX_FBUFF;
|
|
def.def1_size = pblk->def2_size;
|
|
def.def1_buf = pblk->def2_buf;
|
|
tmp.len = pblk->def1_size;
|
|
tmp.addr = pblk->def1_buf;
|
|
if ((status = parse_file(&tmp, &def)) != ERR_PARNORMAL)
|
|
return status;
|
|
|
|
assert(!def.b_node);
|
|
if (def.b_dir) def.fnb |= F_HAS_DIR;
|
|
if (def.b_name) def.fnb |= F_HAS_NAME;
|
|
if (def.b_ext) def.fnb |= F_HAS_EXT;
|
|
}
|
|
|
|
wildname = wilddir = hasnode = hasdir = hasname = hasext = FALSE;
|
|
node = base = ptr = trans.addr;
|
|
top = ptr + trans.len;
|
|
if (trans.len == 0 || *ptr != '/')
|
|
{ /* No file given, no full path given, or a nodename was specified */
|
|
setzdir(NULL, &def_trans); /* Default current directory if none given */
|
|
assert(0 == dollar_zdir.str.len || /* dollar_zdir not initialized yet, possible thru main() -> gtm_chk_dist() */
|
|
(def_trans.str.len == dollar_zdir.str.len && /* check if cwd and cached value are the same */
|
|
0 == memcmp(def_trans.str.addr, dollar_zdir.str.addr, def_trans.str.len)));
|
|
if (pblk->fop & F_PARNODE)
|
|
{ /* What we have could be a nodename */
|
|
assert(pblk->fop & F_SYNTAXO);
|
|
while (node < top)
|
|
{
|
|
ch = *node++;
|
|
if (':' == ch) /* We have nodeness */
|
|
break;
|
|
if ('/' == ch)
|
|
{ /* Not a node - bypass node checking */
|
|
node = top;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (node < top)
|
|
{
|
|
hasnode = TRUE;
|
|
ptr = base = node; /* Update pointers past node name */
|
|
|
|
/* See if the desired (query) node is the local node */
|
|
node_name_len = (int)(node - trans.addr); /* Scanned node including ':' */
|
|
query_node_len = node_name_len - 1; /* Pure name length, no ':' on end */
|
|
assert(MAX_HOST_NAME_LEN >= query_node_len);
|
|
assert(0 < query_node_len);
|
|
assert(':' == *(trans.addr + query_node_len));
|
|
memcpy(query_node_name, trans.addr, query_node_len);
|
|
query_node_name[query_node_len] = 0;
|
|
localhost_sa_ptr = NULL; /* null value needed if not find query node (remote default) */
|
|
CLIENT_HINTS(hints);
|
|
if (0 != (errcode = getaddrinfo(query_node_name, NULL, &hints, &ai_ptr)))
|
|
ai_ptr = NULL; /* skip additional lookups */
|
|
else
|
|
memcpy((sockaddr_ptr)&query_sas, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
|
|
CLIENT_HINTS(hints);
|
|
if (0 == (errcode = getaddrinfo(LOCALHOSTNAME, NULL, &hints, &localhost_ai_ptr)))
|
|
{
|
|
if (0 == memcmp(localhost_ai_ptr->ai_addr, (sockaddr_ptr)&query_sas,
|
|
localhost_ai_ptr->ai_addrlen))
|
|
localhost_sa_ptr = localhost_ai_ptr->ai_addr;
|
|
}
|
|
FREEADDRINFO(localhost_ai_ptr);
|
|
if (ai_ptr && !localhost_sa_ptr)
|
|
{ /* Have not yet established this is not a local node -- check further */
|
|
GETHOSTNAME(local_node_name, MAX_HOST_NAME_LEN, status);
|
|
if (-1 == status)
|
|
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5,
|
|
LEN_AND_LIT("gethostname"), CALLFROM, errno);
|
|
CLIENT_HINTS(hints);
|
|
if (0 != (errcode = getaddrinfo(local_node_name, NULL, &hints, &localhost_ai_ptr)))
|
|
localhost_ai_ptr = NULL; /* empty address list */
|
|
for (temp_ai_ptr = localhost_ai_ptr; temp_ai_ptr!= NULL;
|
|
temp_ai_ptr = temp_ai_ptr->ai_next)
|
|
{
|
|
if (0 == memcmp((sockaddr_ptr)&query_sas, temp_ai_ptr->ai_addr,
|
|
temp_ai_ptr->ai_addrlen))
|
|
{
|
|
localhost_sa_ptr = temp_ai_ptr->ai_addr;
|
|
break; /* Tiz truly a local node */
|
|
}
|
|
}
|
|
FREEADDRINFO(localhost_ai_ptr);
|
|
}
|
|
if (ai_ptr && !localhost_sa_ptr)
|
|
{
|
|
CLIENT_HINTS(hints);
|
|
if (0 != (errcode = getaddrinfo(LOCALHOSTNAME6, NULL, &hints, &localhost_ai_ptr)))
|
|
localhost_ai_ptr = NULL; /* empty address list */
|
|
for (temp_ai_ptr = localhost_ai_ptr; temp_ai_ptr!= NULL;
|
|
temp_ai_ptr = temp_ai_ptr->ai_next)
|
|
{
|
|
if (0 == memcmp((sockaddr_ptr)&query_sas, temp_ai_ptr->ai_addr,
|
|
temp_ai_ptr->ai_addrlen))
|
|
{
|
|
localhost_sa_ptr = temp_ai_ptr->ai_addr;
|
|
break; /* Tiz truly a local node */
|
|
}
|
|
}
|
|
FREEADDRINFO(localhost_ai_ptr);
|
|
}
|
|
if (!localhost_sa_ptr) /* Not local (or an unknown) host given */
|
|
{ /* Remote node specified -- don't apply any defaults */
|
|
FREEADDRINFO(ai_ptr);
|
|
pblk->l_node = trans.addr;
|
|
pblk->b_node = node_name_len;
|
|
pblk->l_dir = base;
|
|
pblk->b_dir = top - base;
|
|
pblk->l_name = pblk->l_ext = base + pblk->b_dir;
|
|
pblk->b_esl = pblk->b_node + pblk->b_dir;
|
|
pblk->b_name = pblk->b_ext = 0;
|
|
pblk->fnb |= (hasnode << V_HAS_NODE);
|
|
return ERR_PARNORMAL;
|
|
}
|
|
FREEADDRINFO(ai_ptr);
|
|
/* Remove local node name from filename buffer */
|
|
assert(0 < trans.len - node_name_len);
|
|
memmove(trans.addr, node, trans.len - node_name_len);
|
|
ptr = base = node -= node_name_len;
|
|
top -= node_name_len;
|
|
trans.len -= node_name_len;
|
|
if ('/' == *base) /* No default directory if full path given */
|
|
def_trans.str.len = 0;
|
|
} else
|
|
{ /* Supplied text was not a node -- reset pointer back to beginning for rescan */
|
|
node = trans.addr;
|
|
}
|
|
}
|
|
|
|
/* If parse buffer is not large enough, return error */
|
|
if (def_trans.str.len + trans.len > pblk->buff_size)
|
|
return ERR_PARBUFSM;
|
|
|
|
/* Construct full filename to parse prefixing given filename with default path prefix */
|
|
if (0 < def_trans.str.len)
|
|
{
|
|
memmove(ptr + def_trans.str.len, ptr, trans.len);
|
|
memcpy(ptr, def_trans.str.addr, def_trans.str.len);
|
|
assert('/' == ptr[def_trans.str.len - 1]);
|
|
ptr += def_trans.str.len;
|
|
top += def_trans.str.len;
|
|
}
|
|
}
|
|
|
|
name = ptr;
|
|
state = NOSTATE;
|
|
for (;ptr < top;)
|
|
{
|
|
ch = *ptr;
|
|
if ('.' == ch)
|
|
{ /* Could be /./ or /../ or name.name */
|
|
ptr++;
|
|
state = (DOT1 == state) ? ((DOT2 == state) ? NAME : DOT2) : DOT1;
|
|
} else if (ch == '/')
|
|
{ /* We must still be doing the path */
|
|
ptr++;
|
|
|
|
hasdir = TRUE;
|
|
hasname = FALSE;
|
|
hasext = FALSE;
|
|
wilddir |= wildname;
|
|
wildname = FALSE;
|
|
|
|
if (DOT1 != state && DOT2 != state && SLASH != state)
|
|
{ /* No dots seen recently so scan as if this is start of filename */
|
|
state = SLASH;
|
|
name = ptr;
|
|
continue;
|
|
}
|
|
if (DOT1 == state)
|
|
{ /* Just remove "./" chars from path */
|
|
del = ptr - 2;
|
|
} else if (DOT2 == state)
|
|
{ /* Have xx/../ construct. Remove /../ and previous level directory from path */
|
|
del = ptr - 4; /* /../ characters being removed */
|
|
assert ('/' == *del);
|
|
if (del > base)
|
|
{
|
|
del--;
|
|
while ('/' != *del)
|
|
del--;
|
|
}
|
|
assert(del >= base && '/' == *del);
|
|
del++;
|
|
} else if (SLASH == state)
|
|
{ /* Remove duplicate slash from path */
|
|
del = ptr - 1;
|
|
while (ptr < top && '/' == *ptr)
|
|
ptr++;
|
|
}
|
|
memmove(del, ptr, top - ptr);
|
|
diff = (int)(ptr - del);
|
|
ptr -= diff;
|
|
top -= diff;
|
|
state = SLASH;
|
|
name = ptr;
|
|
} else
|
|
{ /* Hopeful of filename */
|
|
hasname = TRUE;
|
|
while (ptr < top) /* Do small scan looking for filename end */
|
|
{
|
|
ch = *ptr;
|
|
if ('/' == ch)
|
|
break; /* Ooops, still doing path */
|
|
if ('.' == ch)
|
|
{/* Filename has an extension */
|
|
hasext = TRUE;
|
|
ext = ptr;
|
|
} else if ('?' == ch || '*' == ch)
|
|
wildname = TRUE;
|
|
ptr++;
|
|
}
|
|
state = NAME;
|
|
}
|
|
}
|
|
/* Handle scan end with non-normal state */
|
|
if (SLASH == state || DOT1 == state || DOT2 == state)
|
|
{
|
|
assert(!hasname && !hasext);
|
|
hasdir = TRUE;
|
|
if (state == DOT1)
|
|
{ /* Ignore ./ */
|
|
top--;
|
|
ptr--;
|
|
}
|
|
if (DOT2 == state)
|
|
{ /* ignore ../ plus last directory level specified */
|
|
del = ptr - 3; /* on the end */
|
|
assert ('/' == *del);
|
|
if (del > base)
|
|
{
|
|
del--;
|
|
while ('/' == *del)
|
|
del--;
|
|
}
|
|
assert(del >= base && '/' == *del);
|
|
del++;
|
|
ptr = top = del;
|
|
name = ptr;
|
|
}
|
|
}
|
|
|
|
if (!hasname)
|
|
{
|
|
assert(!hasext);
|
|
name = ptr;
|
|
if (def.fnb & F_HAS_NAME)
|
|
{ /* Use default filename if we didn't find one */
|
|
diff = (int)(name - node);
|
|
if (def.b_name + diff > pblk->buff_size)
|
|
return ERR_PARBUFSM;
|
|
memcpy(name, def.l_name, def.b_name);
|
|
ptr += def.b_name;
|
|
}
|
|
ext = ptr;
|
|
}
|
|
if (!hasext)
|
|
{
|
|
ext = ptr;
|
|
if (def.fnb & F_HAS_EXT)
|
|
{ /* Use default file extension if we didn't find one */
|
|
diff = (int)((ext - node));
|
|
if (def.b_ext + diff > pblk->buff_size)
|
|
return ERR_PARBUFSM;
|
|
memcpy(ext, def.l_ext, def.b_ext);
|
|
ptr += def.b_ext;
|
|
}
|
|
}
|
|
pblk->b_name = ext - name;
|
|
pblk->b_ext = ptr - ext;
|
|
|
|
if (!hasdir && (def.fnb & F_HAS_DIR))
|
|
{
|
|
diff = (int)(name - base);
|
|
diff = def.b_dir - diff;
|
|
if (def.b_dir + pblk->b_name + pblk->b_ext > pblk->buff_size)
|
|
return ERR_PARBUFSM;
|
|
if (diff > 0)
|
|
memmove(name + diff, name, pblk->b_name + pblk->b_ext);
|
|
else if (diff < 0)
|
|
memcpy(name + diff, name, pblk->b_name + pblk->b_ext);
|
|
memcpy(base, def.l_dir, def.b_dir);
|
|
ptr += diff;
|
|
name += diff;
|
|
}
|
|
pblk->b_dir = name - base;
|
|
pblk->b_esl = ptr - base;
|
|
pblk->l_dir = base;
|
|
pblk->l_name = base + pblk->b_dir;
|
|
pblk->l_ext = pblk->l_name + pblk->b_name;
|
|
|
|
pblk->fnb |= (hasdir << V_HAS_DIR);
|
|
pblk->fnb |= (hasname << V_HAS_NAME);
|
|
pblk->fnb |= (hasext << V_HAS_EXT);
|
|
pblk->fnb |= (wildname << V_WILD_NAME);
|
|
pblk->fnb |= (wilddir << V_WILD_DIR);
|
|
|
|
if (!(pblk->fop & F_SYNTAXO) && !wilddir)
|
|
{
|
|
assert('/' == pblk->l_dir[pblk->b_dir - 1]);
|
|
if (pblk->b_dir > 1)
|
|
{
|
|
pblk->l_dir[pblk->b_dir - 1] = 0;
|
|
STAT_FILE(pblk->l_dir, &statbuf, status);
|
|
pblk->l_dir[pblk->b_dir - 1] = '/';
|
|
if (-1 == status || !(statbuf.st_mode & S_IFDIR))
|
|
return ERR_FILENOTFND;
|
|
}
|
|
}
|
|
|
|
return ERR_PARNORMAL;
|
|
}
|