/* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * BSD 3 Clause License * * Copyright (c) 2007, The Storage Networking Industry Association. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * - Neither the name of The Storage Networking Industry Association (SNIA) * nor the names of its contributors may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include "tlm_proto.h" /* * Implementation of a list based stack class. The stack only holds * pointers/references to application objects. The objects are not * copied and the stack never attempts to dereference or access the * data objects. Applications should treat cstack_t references as * opaque handles. */ /* * cstack_new * * Allocate and initialize a new stack, which is just an empty cstack_t. * A pointer to the new stack is returned. This should be treated as an * opaque handle by the caller. */ cstack_t * cstack_new(void) { cstack_t *stk; if ((stk = ndmp_malloc(sizeof (cstack_t))) == NULL) return (NULL); return (stk); } /* * cstack_delete * * Deallocate the stack. This goes through the list freeing all of the * cstack nodes but not the data because we don't know how the data was * allocated. A stack really should be empty before it is deleted. */ void cstack_delete(cstack_t *stk) { cstack_t *tmp; if (stk == NULL) { NDMP_LOG(LOG_DEBUG, "cstack_delete: invalid stack"); return; } while ((tmp = stk->next) != NULL) { stk->next = tmp->next; NDMP_LOG(LOG_DEBUG, "cstack_delete(element): 0x%p", tmp); free(tmp); } NDMP_LOG(LOG_DEBUG, "cstack_delete: 0x%p", stk); free(stk); } /* * cstack_push * * Push an element onto the stack. Allocate a new node and assign the * data and len values. We don't care what about the real values of * data or len and we never try to access them. The stack head will * point to the new node. * * Returns 0 on success. Otherwise returns -1 to indicate overflow. */ int cstack_push(cstack_t *stk, void *data, int len) { cstack_t *stk_node; if (stk == NULL) { NDMP_LOG(LOG_DEBUG, "cstack_push: invalid stack"); return (-1); } if ((stk_node = ndmp_malloc(sizeof (cstack_t))) == NULL) return (-1); stk_node->data = data; stk_node->len = len; stk_node->next = stk->next; stk->next = stk_node; NDMP_LOG(LOG_DEBUG, "cstack_push(0x%p): 0x%p", stk, stk_node); return (0); } /* * cstack_pop * * Pop an element off the stack. Set up the data and len references for * the caller, advance the stack head and free the popped stack node. * * Returns 0 on success. Otherwise returns -1 to indicate underflow. */ int cstack_pop(cstack_t *stk, void **data, int *len) { cstack_t *stk_node; if (stk == NULL) { NDMP_LOG(LOG_DEBUG, "cstack_pop: invalid stack"); return (-1); } if ((stk_node = stk->next) == NULL) { NDMP_LOG(LOG_DEBUG, "cstack_pop: underflow"); return (-1); } if (data) *data = stk_node->data; if (len) *len = stk_node->len; stk->next = stk_node->next; NDMP_LOG(LOG_DEBUG, "cstack_pop(0x%p): 0x%p", stk, stk_node); free(stk_node); return (0); } /* * cstack_top * * Returns the top data element on the stack without removing it. * * Returns 0 on success. Otherwise returns -1 to indicate underflow. */ int cstack_top(cstack_t *stk, void **data, int *len) { if (stk == NULL) { NDMP_LOG(LOG_DEBUG, "cstack_pop: invalid stack"); return (-1); } if (stk->next == NULL) { NDMP_LOG(LOG_DEBUG, "cstack_pop: underflow"); return (-1); } if (data) *data = stk->next->data; if (len) *len = stk->next->len; return (0); } /* * match * * Matching rules: * c Any non-special character matches itslef * ? Match any character * ab character 'a' followed by character 'b' * S Any string of non-special characters * AB String 'A' followed by string 'B' * * Any String, including the empty string */ boolean_t match(char *patn, char *str) { for (; ; ) { switch (*patn) { case 0: return (*str == 0); case '?': if (*str != 0) { str++; patn++; continue; } else { return (FALSE); } break; case '*': patn++; if (*patn == 0) return (TRUE); while (*str) { if (match(patn, str)) return (TRUE); str++; } return (FALSE); default: if (*str != *patn) return (FALSE); str++; patn++; continue; } } } /* * Match recursive call */ int match_ci(char *patn, char *str) { /* * "<" is a special pattern that matches only those names * that do NOT have an extension. "." and ".." are ok. */ if (strcmp(patn, "<") == 0) { if ((strcmp(str, ".") == 0) || (strcmp(str, "..") == 0)) return (TRUE); if (strchr(str, '.') == 0) return (TRUE); return (FALSE); } for (; ; ) { switch (*patn) { case 0: return (*str == 0); case '?': if (*str != 0) { str++; patn++; continue; } else { return (FALSE); } break; case '*': patn++; if (*patn == 0) return (TRUE); while (*str) { if (match_ci(patn, str)) return (TRUE); str++; } return (FALSE); default: if (*str != *patn) { int c1 = *str; int c2 = *patn; c1 = tolower(c1); c2 = tolower(c2); if (c1 != c2) return (FALSE); } str++; patn++; continue; } } /* NOT REACHED */ } /* * Linear matching against a list utility function */ static boolean_t parse_match(char line, char *seps) { char *sep = seps; while (*sep != 0) { /* compare this char with the seperator list */ if (*sep == line) return (TRUE); sep++; } return (FALSE); } /* * Returns the next entry of the list after * each separator */ char * parse(char **line, char *seps) { char *start = *line; while (**line != 0) { *line = *line + 1; if (parse_match(**line, seps)) { /* hit a terminator, skip trailing terminators */ while (parse_match(**line, seps)) { **line = 0; *line = *line + 1; } break; } } return (start); } /* * oct_atoi * * Convert an octal string to integer */ int oct_atoi(char *p) { int v = 0; int c; while (*p == ' ') p++; while ('0' <= (c = *p++) && c <= '7') { v <<= 3; v += c - '0'; } return (v); } /* * strupr * * Convert a string to uppercase using the appropriate codepage. The * string is converted in place. A pointer to the string is returned. * There is an assumption here that uppercase and lowercase values * always result encode to the same length. */ char * strupr(char *s) { char c; unsigned char *p = (unsigned char *)s; while (*p) { c = toupper(*p); *p++ = c; } return (s); } /* * trim_whitespace * * Trim leading and trailing whitespace chars(as defined by isspace) * from a buffer. Example; if the input buffer contained " text ", * it will contain "text", when we return. We assume that the buffer * contains a null terminated string. A pointer to the buffer is * returned. */ char * trim_whitespace(char *buf) { char *p = buf; char *q = buf; if (buf == 0) return (0); while (*p && isspace(*p)) ++p; while ((*q = *p++) != 0) ++q; if (q != buf) { while ((--q, isspace(*q)) != 0) *q = '\0'; } return (buf); } /* * trim_name * * Trims the slash and dot slash from the beginning of the * path name. */ char * trim_name(char *nm) { while (*nm) { if (*nm == '/') { nm++; continue; } if (*nm == '.' && nm[1] == '/' && nm[2]) { nm += 2; continue; } break; } return (nm); } /* * get_volname * * Extract the volume name from the path */ char * get_volname(char *path) { char *cp, *save; int sp; if (!path) return (NULL); if (!(save = strdup(path))) return (NULL); sp = strspn(path, "/"); if (*(path + sp) == '\0') { free(save); return (NULL); } if ((cp = strchr(save + sp, '/'))) *cp = '\0'; return (save); } /* * fs_volexist * * Check if the volume exists */ boolean_t fs_volexist(char *path) { struct stat64 st; char *p; if ((p = get_volname(path)) == NULL) return (FALSE); if (stat64(p, &st) != 0) { free(p); return (FALSE); } free(p); return (TRUE); } /* * tlm_tarhdr_size * * Returns the size of the TLM_TAR_HDR structure. */ int tlm_tarhdr_size(void) { return (sizeof (tlm_tar_hdr_t)); } /* * dup_dir_info * * Make and return a copy of the directory info. */ struct full_dir_info * dup_dir_info(struct full_dir_info *old_dir_info) { struct full_dir_info *new_dir_info; new_dir_info = ndmp_malloc(sizeof (struct full_dir_info)); if (new_dir_info) { bcopy(old_dir_info, new_dir_info, sizeof (struct full_dir_info)); } return (new_dir_info); } /* * tlm_new_dir_info * * Create a new structure, set fh field to what is specified and the path * to the concatenation of directory and the component */ struct full_dir_info * tlm_new_dir_info(struct fs_fhandle *fhp, char *dir, char *nm) { struct full_dir_info *fdip; if (!(fdip = ndmp_malloc(sizeof (struct full_dir_info)))) return (NULL); (void) memcpy(&fdip->fd_dir_fh, fhp, sizeof (fs_fhandle_t)); if (!tlm_cat_path(fdip->fd_dir_name, dir, nm)) { free(fdip); NDMP_LOG(LOG_DEBUG, "TAPE BACKUP Find> path too long [%s][%s]", dir, nm); return (NULL); } return (fdip); }