1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278
|
/****************************************************************
* *
* Copyright (c) 2014-2022 Fidelity National Information *
* Services, Inc. and/or its subsidiaries. All rights reserved. *
* *
* 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 <errno.h>
#include <stdarg.h>
#include "gtm_limits.h"
#include "gtm_stdio.h"
#include "gtm_string.h"
#include "gtm_stat.h"
#include "gtm_stdlib.h"
#include "gtmio.h"
#include "io.h"
#include "iosp.h"
#include <rtnhdr.h>
#include "relinkctl.h"
#include "parse_file.h"
#include "eintr_wrappers.h"
#include "error.h"
#include "min_max.h"
#include "op.h"
#include "op_fnzsearch.h"
#include "interlock.h"
#include "toktyp.h"
#include "valid_mname.h"
#include "restrict.h"
#ifdef DEBUG
# include "toktyp.h" /* Needed for "valid_mname.h" */
#endif
#define DOTOBJEXT ".o"
#define OBJEXT 'o'
#define ASTERISK '*'
#define QUESTION '?'
LITREF mval literal_null;
error_def(ERR_FILEPARSE);
error_def(ERR_PARNORMAL);
error_def(ERR_RESTRICTEDOP);
error_def(ERR_TEXT);
#ifndef AUTORELINK_SUPPORTED
/* Stub routine for unsupported platforms */
void op_zrupdate(int argcnt, ...)
{
return;
}
#else
/* The ZRUPDATE command drives this routine once through for each argument (object file path and object file - potentially
* containing wildcards). Each file specified, or found in a wildcard search, is separated into its path and its routine name;
* the path is then looked up and the appropriate relinkctl file opened, where we find the routine name and bump its cycle.
*
* Although this routine is set up to handle a variable argument list, more than 1 argument is not currently supported. The
* ZRUPDATE command itself does support a commented list of filespecs, but the compiler turns each argument into a separate
* call to this routine. The purpose of the variable argument list is to support a future proposed enhancement, which would
* allow a ZRUPDATE argument to be a parenthesized list of filespecs with the intention that all of them be simultaneously
* updated. Such a list, when supported, would be passed as a list of files to this routine - hence the multi-arg support.
*
* Parameters:
* argcnt - currently always 1 (see note above).
* objfilespec - mval address holding string containing filespec to process.
*
* No return value.
*/
void op_zrupdate(int argcnt, ...)
{
boolean_t wildcarded, noresult, seenfext, invalid;
char pblkbuf[MAX_FN_LEN + 1], statbuf[MAX_FN_LEN + 1], namebuf[MAX_FN_LEN + 1];
char *chptr, chr;
int status, fextlen, fnamlen, object_count;
mstr objdir, rtnname;
mval *objfilespec, objpath;
open_relinkctl_sgm *linkctl;
parse_blk pblk;
plength plen;
relinkrec_t *rec;
struct stat outbuf;
uint4 hash, prev_hash_index;
va_list var;
if (RESTRICTED(zrupdate_op))
RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_RESTRICTEDOP, 1, "ZRUPDATE");
/* Currently only expecting one value per invocation right now. That will change in phase 2, hence the stdarg setup. */
va_start(var, argcnt);
assert(1 == argcnt);
objfilespec = va_arg(var, mval *);
va_end(var);
MV_FORCE_STR(objfilespec);
/* Initialize pblk with information about the pattern in the argument to ZRUPDATE. */
memset(&pblk, 0, SIZEOF(pblk));
pblk.buffer = pblkbuf;
pblk.buff_size = (unsigned char)(MAX_FN_LEN); /* Pass size of buffer - 1 (standard protocol for parse_file). */
pblk.def1_buf = DOTOBJEXT; /* Default .o file type if not specified. */
pblk.def1_size = SIZEOF(DOTOBJEXT) - 1;
pblk.fop = F_SYNTAXO; /* Syntax check only - bypass directory / file existence check. */
status = parse_file(&objfilespec->str, &pblk);
if (ERR_PARNORMAL != status)
RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_FILEPARSE, 2, objfilespec->str.len, objfilespec->str.addr, status);
wildcarded = (pblk.fnb & F_WILD); /* Our error logic is different depending on the presence of wildcards. */
invalid = FALSE;
if (0 != pblk.b_name)
{ /* A file name was specified (if not, it is probably hard to find the file name, but that can be dealt with later).
* Like above, the string must be comprised of valid chars for routine names.
*/
for (chptr = pblk.l_name, fnamlen = pblk.b_name; 0 < fnamlen; chptr++, fnamlen--)
{
if ((ASTERISK != *chptr) && (QUESTION != *chptr))
{ /* Substitute '%' for '_'. While this substitution is valid just for the first char, only the first
* char can be '%', so a check of the second or later char would fail the '%' substitution anyway.
*/
chr = ('_' == *chptr) ? '%' : *chptr;
/* We see a char that isn't a wildcard character. If this is the first character, it can be
* alpha or percent. If the second or later character, it can be alphanumeric.
*/
if (((fnamlen == pblk.b_name) && (!VALID_MNAME_FCHAR(chr) || ('%' == *chptr))) /* If 1st char */
|| ((fnamlen != pblk.b_name) && !VALID_MNAME_NFCHAR(chr))) /* If 2nd+ char */
{
invalid = TRUE;
break;
}
}
}
} else if (!wildcarded)
invalid = TRUE;
if (invalid)
RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_FILEPARSE, 2, objfilespec->str.len, objfilespec->str.addr,
ERR_TEXT, 2, RTS_ERROR_TEXT("Filename is not a valid routine name"));
/* Do a simlar check for the file type */
seenfext = FALSE;
if (0 != pblk.b_ext)
{ /* If a file extension was specified - get the extension sans any potential wildcard character. */
for (chptr = pblk.l_ext + 1, fextlen = pblk.b_ext - 1; 0 < fextlen; chptr++, fextlen--)
{ /* Check each character in the extension except the first, which is the dot if extension exists at all. */
if (ASTERISK != *chptr)
{ /* We see a char that is not a '*' wildcard character. If we have already seen our "o" file
* extension or a '?' wildcard character (which we assume is "o"), this char makes our requirement
* filetype impossible, so raise an error.
*/
if (seenfext || ((OBJEXT != *chptr) && (QUESTION != *chptr)))
{
invalid = TRUE;
break;
}
seenfext = TRUE;
}
}
} else if (!wildcarded)
invalid = TRUE;
if (invalid)
RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_FILEPARSE, 2, objfilespec->str.len, objfilespec->str.addr,
ERR_TEXT, 2, RTS_ERROR_TEXT("Unsupported filetype specified"));
zsrch_clr(STRM_ZRUPDATE); /* Clear any existing search cache */
object_count = 0;
do
{ /* The DO-WHILE form is to do one iteration even if not wildcarded. */
plen.p.pint = op_fnzsearch(objfilespec, STRM_ZRUPDATE, 0, &objpath);
if (TRUE == (noresult = (0 == objpath.str.len))) /* Note: assignment! */
{ /* No (more) matches. In wildcarded case we are simply done with this loop. */
if (wildcarded)
break;
else
{ /* In a non-wildcarded case we want to verify whether the user is referring to a previously existent
* file that got removed or the one that op_fnzsearch() silently skipped due to access issues. So,
* set the objpath to the user-provided string after processing by parse_file() and adjust the
* length fields accordingly.
*/
objpath.str.addr = pblk.buffer;
objpath.str.len = pblk.b_esl;
SET_LENGTHS(&plen, objpath.str.addr, objpath.str.len, FALSE);
}
}
/* Verify the extension and filename on wildcarded patterns; the non-wildcarded ones have been checked earlier.
* Start with the extension.
*/
if (wildcarded && ((SIZEOF(DOTOBJEXT) - 1 != plen.p.pblk.b_ext)
|| (OBJEXT != objpath.str.addr[plen.p.pblk.b_dir + plen.p.pblk.b_name + 1])))
continue;
/* Before opening the relinkctl file, verify that a valid routine name can be derived, thus almost definitely
* telling us that the object name is also correct. The only exception is when the object name starts with a '%',
* so we want to note down that fact. Note that we cannot operate on the objpath memory because we would be
* affecting the object name, so we have to make a copy first.
*/
if (wildcarded && ((0 == plen.p.pblk.b_name) || ('%' == objpath.str.addr[plen.p.pblk.b_dir])))
continue;
memcpy(namebuf, objpath.str.addr + plen.p.pblk.b_dir, plen.p.pblk.b_name);
rtnname.len = plen.p.pblk.b_name;
rtnname.addr = namebuf;
CONVERT_FILENAME_TO_RTNNAME(rtnname); /* Get rtnname before searching in relinkctl file */
if (wildcarded && !valid_mname(&rtnname))
continue;
assert(!noresult || !wildcarded); /* We should have left the loop early on no results with a wildcard. */
/* The reasons for doing the below STAT depend on the situation. If we do have at least one result, we need to make
* sure it is legitimate. If we have no results, we do the STAT because op_fnzsearch() on non-wildcarded requests
* may cleanly return an empty list even in the face of access errors, whereas we want to notify the user about
* potential access issues on a single file.
*/
memcpy(statbuf, objpath.str.addr, objpath.str.len);
statbuf[objpath.str.len] = '\0';
LSTAT_FILE(statbuf, &outbuf, status); /* We use lstat to detect and eliminate soft links. */
if (-1 == status)
{ /* In the wildcarded case we just skip missing files. Any access error (but not the case of a missing file)
* gets reported on non-wildcarded patterns. If we did not find this file initially, it does not matter if
* exists now. We simply want to determine whether the reason that we could not find it had to do with
* access errors.
*/
if (wildcarded)
continue;
else if (ENOENT != errno)
RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_FILEPARSE, 2,
objfilespec->str.len, objfilespec->str.addr, errno);
} else if (!S_ISREG(outbuf.st_mode))
{ /* We are only interested in regular files. */
continue;
}
/* Extraction of object directory is different depending on whether a match was found or not. If we have a match,
* then objpath already contains the full path to the object, including the directory. If not, then we cannot use
* objpath because it was populated with user's argument to ZRUPDATE, which might not have a directory name in it.
* So, in that case we derive the directory name from the original parsing results populated by parse_file().
*/
if (noresult)
{
objdir.addr = pblk.l_dir;
objdir.len = pblk.b_dir;
} else
{
objdir.addr = objpath.str.addr;
objdir.len = plen.p.pblk.b_dir;
}
linkctl = relinkctl_attach(&objdir, &objpath.str, 0); /* Create/attach/open relinkctl file. */
if (NULL == linkctl)
{
if (wildcarded)
continue;
else
{ /* Note that the below errno value should come from the realpath() call in relinkctl_attach()
* invoked above, so we need to make sure nothing gets called in between.
*/
RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_FILEPARSE, 2,
objfilespec->str.len, objfilespec->str.addr, errno);
}
}
if (!wildcarded)
{ /* In the non-wildcarded case we decide whether to proceed with the cycle bump thusly:
* 1. If the specified file exists, it may or may not be accounted for in the relinkctl file, meaning that
* we need to either add it there or simply update its cycle.
* 2. If the file does not exist on disk, but the routine is found in the relinkctl file, update its cycle.
* 3. If there is no file and no entry for it in the relinkctl file, do nothing (info error removed by
* request).
*/
COMPUTE_RELINKCTL_HASH(&rtnname, hash, linkctl->hdr->relinkctl_hash_buckets);
rec = relinkctl_find_record(linkctl, &rtnname, hash, &prev_hash_index);
if ((NULL == rec) && noresult)
return;
}
rec = relinkctl_insert_record(linkctl, &rtnname);
RELINKCTL_CYCLE_INCR(rec, linkctl); /* Increment cycle indicating change to world */
object_count++; /* Update the count of valid objects encountered. */
} while (wildcarded);
/* For a wildcarded request that did not return any suitable object files give a no objects found error as a "soft" INFO
* level message, which gets supressed in except in direct mode.
*/
if (wildcarded && (0 == object_count))
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(8) MAKE_MSG_INFO(ERR_FILEPARSE), 2, objfilespec->str.len,
objfilespec->str.addr, ERR_TEXT, 2, RTS_ERROR_TEXT("No object files found"));
}
#endif /* AUTORELINK_SUPPORTED */
|