X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fnames.c;h=46581e0738f5228ed542a8af4ee29e983766f44e;hb=f364d50661977d5db6df03470e85999a88ec5981;hp=2b009cb8152eaad3485ce5c022c05ff054284b2b;hpb=4f07b065060ea347aafa8ffea1d20c4934e9c20a;p=chaz%2Ftar diff --git a/src/names.c b/src/names.c index 2b009cb..46581e0 100644 --- a/src/names.c +++ b/src/names.c @@ -1,7 +1,7 @@ /* Various processing of names. - Copyright 1988, 1992, 1994, 1996, 1997, 1998, 1999, 2000, 2001 Free - Software Foundation, Inc. + Copyright (C) 1988, 1992, 1994, 1996, 1997, 1998, 1999, 2000, 2001, + 2003 Free Software Foundation, Inc. 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 @@ -43,15 +43,15 @@ struct group *getgrgid (); This code should also be modified for non-UNIX systems to do something reasonable. */ -static char cached_uname[UNAME_FIELD_SIZE]; -static char cached_gname[GNAME_FIELD_SIZE]; +static char *cached_uname; +static char *cached_gname; static uid_t cached_uid; /* valid only if cached_uname is not empty */ static gid_t cached_gid; /* valid only if cached_gname is not empty */ /* These variables are valid only if nonempty. */ -static char cached_no_such_uname[UNAME_FIELD_SIZE]; -static char cached_no_such_gname[GNAME_FIELD_SIZE]; +static char *cached_no_such_uname; +static char *cached_no_such_gname; /* These variables are valid only if nonzero. It's not worth optimizing the case for weird systems where 0 is not a valid uid or gid. */ @@ -60,87 +60,87 @@ static gid_t cached_no_such_gid; /* Given UID, find the corresponding UNAME. */ void -uid_to_uname (uid_t uid, char uname[UNAME_FIELD_SIZE]) +uid_to_uname (uid_t uid, char **uname) { struct passwd *passwd; if (uid != 0 && uid == cached_no_such_uid) { - *uname = '\0'; + *uname = strdup (""); return; } - if (!cached_uname[0] || uid != cached_uid) + if (!cached_uname || uid != cached_uid) { passwd = getpwuid (uid); if (passwd) { cached_uid = uid; - strncpy (cached_uname, passwd->pw_name, UNAME_FIELD_SIZE); + assign_string (&cached_uname, passwd->pw_name); } else { cached_no_such_uid = uid; - *uname = '\0'; + *uname = strdup (""); return; } } - strncpy (uname, cached_uname, UNAME_FIELD_SIZE); + *uname = strdup (cached_uname); } /* Given GID, find the corresponding GNAME. */ void -gid_to_gname (gid_t gid, char gname[GNAME_FIELD_SIZE]) +gid_to_gname (gid_t gid, char **gname) { struct group *group; if (gid != 0 && gid == cached_no_such_gid) { - *gname = '\0'; + *gname = strdup (""); return; } - if (!cached_gname[0] || gid != cached_gid) + if (!cached_gname || gid != cached_gid) { group = getgrgid (gid); if (group) { cached_gid = gid; - strncpy (cached_gname, group->gr_name, GNAME_FIELD_SIZE); + assign_string (&cached_gname, group->gr_name); } else { cached_no_such_gid = gid; - *gname = '\0'; + *gname = strdup (""); return; } } - strncpy (gname, cached_gname, GNAME_FIELD_SIZE); + *gname = strdup (cached_gname); } /* Given UNAME, set the corresponding UID and return 1, or else, return 0. */ int -uname_to_uid (char uname[UNAME_FIELD_SIZE], uid_t *uidp) +uname_to_uid (char *uname, uid_t *uidp) { struct passwd *passwd; - if (cached_no_such_uname[0] - && strncmp (uname, cached_no_such_uname, UNAME_FIELD_SIZE) == 0) + if (cached_no_such_uname + && strcmp (uname, cached_no_such_uname) == 0) return 0; - if (!cached_uname[0] + if (!cached_uname || uname[0] != cached_uname[0] - || strncmp (uname, cached_uname, UNAME_FIELD_SIZE) != 0) + || strcmp (uname, cached_uname) != 0) { passwd = getpwnam (uname); if (passwd) { cached_uid = passwd->pw_uid; - strncpy (cached_uname, uname, UNAME_FIELD_SIZE); + assign_string (&cached_uname, passwd->pw_name); } else { - strncpy (cached_no_such_uname, uname, UNAME_FIELD_SIZE); + assign_string (&cached_no_such_uname, uname); return 0; } } @@ -150,33 +150,34 @@ uname_to_uid (char uname[UNAME_FIELD_SIZE], uid_t *uidp) /* Given GNAME, set the corresponding GID and return 1, or else, return 0. */ int -gname_to_gid (char gname[GNAME_FIELD_SIZE], gid_t *gidp) +gname_to_gid (char *gname, gid_t *gidp) { struct group *group; - if (cached_no_such_gname[0] - && strncmp (gname, cached_no_such_gname, GNAME_FIELD_SIZE) == 0) + if (cached_no_such_gname + && strcmp (gname, cached_no_such_gname) == 0) return 0; - if (!cached_gname[0] + if (!cached_gname || gname[0] != cached_gname[0] - || strncmp (gname, cached_gname, GNAME_FIELD_SIZE) != 0) + || strcmp (gname, cached_gname) != 0) { group = getgrnam (gname); if (group) { cached_gid = group->gr_gid; - strncpy (cached_gname, gname, GNAME_FIELD_SIZE); + assign_string (&cached_gname, gname); } else { - strncpy (cached_no_such_gname, gname, GNAME_FIELD_SIZE); + assign_string (&cached_no_such_gname, gname); return 0; } } *gidp = cached_gid; return 1; } + /* Names from the command call. */ @@ -270,7 +271,9 @@ read_name_from_file (void) { if (counter == name_buffer_length) { - name_buffer_length += NAME_FIELD_SIZE; + if (name_buffer_length * 2 < name_buffer_length) + xalloc_die (); + name_buffer_length *= 2; name_buffer = xrealloc (name_buffer, name_buffer_length + 2); } name_buffer[counter++] = character; @@ -281,7 +284,9 @@ read_name_from_file (void) if (counter == name_buffer_length) { - name_buffer_length += NAME_FIELD_SIZE; + if (name_buffer_length * 2 < name_buffer_length) + xalloc_die (); + name_buffer_length *= 2; name_buffer = xrealloc (name_buffer, name_buffer_length + 2); } name_buffer[counter] = '\0'; @@ -319,11 +324,20 @@ name_next (int change_dirs) } else { + size_t source_len; source = name_array[name_index++]; - if (strlen (source) > name_buffer_length) + source_len = strlen (source); + if (name_buffer_length < source_len) { + do + { + name_buffer_length *= 2; + if (! name_buffer_length) + xalloc_die (); + } + while (name_buffer_length < source_len); + free (name_buffer); - name_buffer_length = strlen (source); name_buffer = xmalloc (name_buffer_length + 2); } strcpy (name_buffer, source); @@ -382,7 +396,7 @@ name_gather (void) { /* Buffer able to hold a single name. */ static struct name *buffer; - static size_t allocated_length; + static size_t allocated_size; char const *name; @@ -390,12 +404,12 @@ name_gather (void) { static int change_dir; - if (allocated_length == 0) + if (allocated_size == 0) { - allocated_length = sizeof (struct name) + NAME_FIELD_SIZE; - buffer = xmalloc (allocated_length); + allocated_size = offsetof (struct name, name) + NAME_FIELD_SIZE + 1; + buffer = xmalloc (allocated_size); /* FIXME: This memset is overkill, and ugly... */ - memset (buffer, 0, allocated_length); + memset (buffer, 0, allocated_size); } while ((name = name_next (0)) && strcmp (name, "-C") == 0) @@ -408,14 +422,23 @@ name_gather (void) if (name) { + size_t needed_size; buffer->length = strlen (name); - if (sizeof (struct name) + buffer->length >= allocated_length) + needed_size = offsetof (struct name, name) + buffer->length + 1; + if (allocated_size < needed_size) { - allocated_length = sizeof (struct name) + buffer->length; - buffer = xrealloc (buffer, allocated_length); + do + { + allocated_size *= 2; + if (! allocated_size) + xalloc_die (); + } + while (allocated_size < needed_size); + + buffer = xrealloc (buffer, allocated_size); } buffer->change_dir = change_dir; - memcpy (buffer->name, name, buffer->length + 1); + strcpy (buffer->name, name); buffer->next = 0; buffer->found = 0; @@ -454,23 +477,26 @@ name_gather (void) struct name * addname (char const *string, int change_dir) { - struct name *name; - size_t length; - - length = string ? strlen (string) : 0; - name = xmalloc (sizeof (struct name) + length); - memset (name, 0, sizeof (struct name) + length); - name->next = 0; + size_t length = string ? strlen (string) : 0; + struct name *name = xmalloc (offsetof (struct name, name) + length + 1); if (string) { name->fake = 0; - name->length = length; - memcpy (name->name, string, length + 1); + strcpy (name->name, string); } else - name->fake = 1; + { + name->fake = 1; + + /* FIXME: This initialization (and the byte of memory that it + initializes) is probably not needed, but we are currently in + bug-fix mode so we'll leave it in for now. */ + name->name[0] = 0; + } + name->next = 0; + name->length = length; name->found = 0; name->regexp = 0; /* assume not a regular expression */ name->firstch = 1; /* assume first char is literal */ @@ -506,7 +532,8 @@ namelist_match (char const *path, size_t length) if (p->regexp ? fnmatch (p->name, path, recursion_option) == 0 : (p->length <= length - && (path[p->length] == '\0' || ISSLASH (path[p->length])) + && (path[p->length] == '\0' + || (ISSLASH (path[p->length]) && recursion_option)) && memcmp (path, p->name, p->length) == 0)) return p; } @@ -547,7 +574,7 @@ name_match (const char *path) nametail = &namelist; } chdir_do (cursor->change_dir); - + /* We got a match. */ return 1; } @@ -568,6 +595,17 @@ name_match (const char *path) } } +/* Returns true if all names from the namelist were processed */ +bool +names_done () +{ + struct name const *cursor; + for (cursor = namelist; cursor; cursor = cursor->next) + if (cursor->regexp || (!cursor->found && !cursor->fake)) + return false; + return true; +} + /* Print the names of things in the namelist that were not matched. */ void names_notfound (void) @@ -717,10 +755,16 @@ add_hierarchy_to_namelist (struct name *name, dev_t device) string_length = strlen (string); if (*string == 'D') { - if (name_length + string_length >= allocated_length) + if (allocated_length <= name_length + string_length) { - while (name_length + string_length >= allocated_length) - allocated_length += NAME_FIELD_SIZE; + do + { + allocated_length *= 2; + if (! allocated_length) + xalloc_die (); + } + while (allocated_length <= name_length + string_length); + name_buffer = xrealloc (name_buffer, allocated_length + 1); } strcpy (name_buffer + name_length, string + 1); @@ -865,46 +909,192 @@ new_name (const char *path, const char *name) return buffer; } -/* Return nonzero if file NAME is excluded. Exclude a name if its - prefix matches a pattern that contains slashes, or if one of its - components matches a pattern that contains no slashes. */ +/* Return nonzero if file NAME is excluded. */ bool excluded_name (char const *name) { return excluded_filename (excluded, name + FILESYSTEM_PREFIX_LEN (name)); } -/* Names to avoid dumping. */ -static Hash_table *avoided_name_table; +/* Hash tables of strings. */ -/* Calculate the hash of an avoided name. */ +/* Calculate the hash of a string. */ static unsigned -hash_avoided_name (void const *name, unsigned n_buckets) +hash_string_hasher (void const *name, unsigned n_buckets) { return hash_string (name, n_buckets); } -/* Compare two avoided names for equality. */ +/* Compare two strings for equality. */ static bool -compare_avoided_names (void const *name1, void const *name2) +hash_string_compare (void const *name1, void const *name2) { return strcmp (name1, name2) == 0; } +/* Return zero if TABLE contains a copy of STRING; otherwise, insert a + copy of STRING to TABLE and return 1. */ +static bool +hash_string_insert (Hash_table **table, char const *string) +{ + Hash_table *t = *table; + char *s = xstrdup (string); + char *e; + + if (! ((t + || (*table = t = hash_initialize (0, 0, hash_string_hasher, + hash_string_compare, 0))) + && (e = hash_insert (t, s)))) + xalloc_die (); + + if (e == s) + return 1; + else + { + free (s); + return 0; + } +} + +/* Return 1 if TABLE contains STRING. */ +static bool +hash_string_lookup (Hash_table const *table, char const *string) +{ + return table && hash_lookup (table, string); +} + +/* Names to avoid dumping. */ +static Hash_table *avoided_name_table; + /* Remember to not archive NAME. */ void add_avoided_name (char const *name) { - if (! ((avoided_name_table - || (avoided_name_table = hash_initialize (0, 0, hash_avoided_name, - compare_avoided_names, 0))) - && hash_insert (avoided_name_table, xstrdup (name)))) - xalloc_die (); + hash_string_insert (&avoided_name_table, name); } /* Should NAME be avoided when archiving? */ -int +bool is_avoided_name (char const *name) { - return avoided_name_table && hash_lookup (avoided_name_table, name); + return hash_string_lookup (avoided_name_table, name); +} + +/* Return a safer suffix of FILE_NAME, or "." if it has no safer + suffix. Check for fully specified file names and other atrocities. + Warn the user if we do not return NAME. If LINK_TARGET is 1, + FILE_NAME is the target of a hard link, not a member name. */ + +char * +safer_name_suffix (char const *file_name, bool link_target) +{ + char const *p; + + if (absolute_names_option) + p = file_name; + else + { + /* Skip file system prefixes, leading pathnames that contain + "..", and leading slashes. */ + + size_t prefix_len = FILESYSTEM_PREFIX_LEN (file_name); + + for (p = file_name + prefix_len; *p; ) + { + if (p[0] == '.' && p[1] == '.' && (ISSLASH (p[2]) || !p[2])) + prefix_len = p + 2 - file_name; + + do + { + char c = *p++; + if (ISSLASH (c)) + break; + } + while (*p); + } + + for (p = file_name + prefix_len; ISSLASH (*p); p++) + continue; + prefix_len = p - file_name; + + if (prefix_len) + { + static Hash_table *prefix_table[2]; + char *prefix = alloca (prefix_len + 1); + memcpy (prefix, file_name, prefix_len); + prefix[prefix_len] = '\0'; + + if (hash_string_insert (&prefix_table[link_target], prefix)) + { + static char const *const diagnostic[] = + { + N_("Removing leading `%s' from member names"), + N_("Removing leading `%s' from hard link targets") + }; + WARN ((0, 0, _(diagnostic[link_target]), prefix)); + } + } + } + + if (! *p) + { + if (p == file_name) + { + static char const *const diagnostic[] = + { + N_("Substituting `.' for empty member name"), + N_("Substituting `.' for empty hard link target") + }; + WARN ((0, 0, _(diagnostic[link_target]))); + } + + p = "."; + } + + return (char *) p; +} + +/* Return the size of the prefix of FILE_NAME that is removed after + stripping NUM leading path name components. NUM must be + positive. */ + +size_t +stripped_prefix_len (char const *file_name, size_t num) +{ + char const *p = file_name + FILESYSTEM_PREFIX_LEN (file_name); + while (ISSLASH (*p)) + p++; + while (*p) + { + bool slash = ISSLASH (*p); + p++; + if (slash) + { + if (--num == 0) + return p - file_name; + while (ISSLASH (*p)) + p++; + } + } + return -1; +} + +/* Return nonzero if NAME contains ".." as a path name component. */ +bool +contains_dot_dot (char const *name) +{ + char const *p = name + FILESYSTEM_PREFIX_LEN (name); + + for (;; p++) + { + if (p[0] == '.' && p[1] == '.' && (ISSLASH (p[2]) || !p[2])) + return 1; + + do + { + if (! *p++) + return 0; + } + while (! ISSLASH (*p)); + } }