/* * zipl - zSeries Initial Program Loader tool * * Miscellaneous helper functions * * Copyright IBM Corp. 2001, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include #include "error.h" #include "misc.h" /* Allocate SIZE bytes of memory. Upon success, return pointer to memory. * Return NULL otherwise. */ void * misc_malloc(size_t size) { void* result; result = malloc(size); if (result == NULL) { error_reason("Could not allocate %lld bytes of memory", (unsigned long long) size); } return result; } /* asprintf with misc error checking */ int misc_asprintf(char** out, const char* fmt, ...) { va_list ap; int rc; va_start(ap, fmt); rc = vasprintf(out, fmt, ap); va_end(ap); if (rc == -1) error_reason("Could not allocate space for new string"); return rc; } /* Allocate N * SIZE bytes of memory. Upon success, return pointer to memory. * Return NULL otherwise. */ void * misc_calloc(size_t n, size_t size) { void* result; result = calloc(n, size); if (result == NULL) { error_reason("Could not allocate %lld bytes of memory", (unsigned long long) n * (unsigned long long) size); } return result; } /* Duplicate the given string S. Upon success, return pointer to new string. * Return NULL otherwise. */ char * misc_strdup(const char* s) { char* result; result = strdup(s); if (result == NULL) { error_reason("Could not allocate %lld bytes of memory", (unsigned long long) strlen(s) + 1); } return result; } /* Open file exclusive */ int misc_open_exclusive(const char* filename) { int fd; fd = open(filename, O_RDWR | O_EXCL); if (fd == -1 && errno == EBUSY) error_reason("Device is in use (probably mounted)"); else if (fd == -1) error_reason(strerror(errno)); return fd; } /* Read COUNT bytes of data from file identified by file descriptor FD to * memory at location BUFFER. Return 0 when all bytes were successfully read, * non-zero otherwise. */ int misc_read(int fd, void* buffer, size_t count) { size_t done; ssize_t rc; for (done=0; done < count; done += rc) { rc = read(fd, VOID_ADD(buffer, done), count - done); if (rc == -1) { error_reason(strerror(errno)); return -1; } if(rc == 0) { error_reason("Reached unexpected end of file"); return -1; } } return 0; } /* Read all of file FILENAME to memory. Upon success, return 0 and set BUFFER * to point to the data and SIZE (if non-NULL) to contain the file size. * If NIL_TERMINATE is non-zero, a nil-char will be added to the buffer string * Return non-zero otherwise. */ int misc_read_file(const char* filename, char** buffer, size_t* size, int nil_terminate) { struct stat stats; void* data; int fd; int rc; if (stat(filename, &stats)) { error_reason(strerror(errno)); return -1; } if (!S_ISREG(stats.st_mode)) { error_reason("Not a regular file"); return -1; } data = misc_malloc(stats.st_size + (nil_terminate ? 1 : 0)); if (data == NULL) return -1; fd = open(filename, O_RDONLY); if (fd == -1) { error_reason(strerror(errno)); free(data); return -1; } rc = misc_read(fd, data, stats.st_size); close(fd); if (rc) { free(data); return rc; } *buffer = data; if (size != NULL) *size = stats.st_size; if (nil_terminate) { if (size != NULL) (*size)++; (*buffer)[stats.st_size] = 0; } return 0; } #define INITIAL_FILE_BUFFER_SIZE 1024 /* Read file into buffer without querying its size (necessary for reading files * from /proc or /sys). Upon success, return zero and set BUFFER to point to * the file buffer and SIZE (if non-null) to contain the file size. Return * non-zero otherwise. Add a null-byte at the end of the buffer if * NIL_TERMINATE is non-zero. */ int misc_read_special_file(const char* filename, char** buffer, size_t* size, int nil_terminate) { FILE* file; char* data; char* new_data; size_t count; size_t current_size; int current; file = fopen(filename, "r"); if (file == NULL) { error_reason(strerror(errno)); return -1; } current_size = INITIAL_FILE_BUFFER_SIZE; count = 0; data = (char *) misc_malloc(current_size); if (data == NULL) { fclose(file); return -1; } current = fgetc(file); while (current != EOF || nil_terminate) { if (current == EOF) { current = 0; nil_terminate = 0; } data[count++] = (char) current; if (count >= current_size) { new_data = (char *) misc_malloc(current_size * 2); if (new_data == NULL) { free(data); fclose(file); return -1; } memcpy(new_data, data, current_size); free(data); data = new_data; current_size *= 2; } current = fgetc(file); } fclose(file); *buffer = data; if (size) *size = count; return 0; } /* Get contents of file identified by FILENAME and fill in the respective * fields of FILE. Return 0 on success, non-zero otherwise. */ int misc_get_file_buffer(const char* filename, struct misc_file_buffer* file) { int rc; rc = misc_read_file(filename, &file->buffer, &file->length, 0); file->pos = 0; return rc; } /* Free resources allocated for file buffer FILE. */ void misc_free_file_buffer(struct misc_file_buffer* file) { if (file->buffer != NULL) { free(file->buffer); file->buffer = NULL; file->pos = 0; file->length = 0; } } /* Return character at current FILE buffer position plus READAHEAD or EOF if * at end of file. */ int misc_get_char(struct misc_file_buffer* file, off_t readahead) { if (file->buffer != NULL) if ((size_t) (file->pos + readahead) < file->length) return file->buffer[file->pos + readahead]; return EOF; } char* misc_make_path(char* dirname, char* filename) { char* result; size_t len; len = strlen(dirname) + strlen(filename) + 2; result = (char *) misc_malloc(len); if (result == NULL) return NULL; sprintf(result, "%s/%s", dirname, filename); return result; } #define TEMP_DEV_MAX_RETRIES 1000 int misc_temp_dev(dev_t dev, int blockdev, char** devno) { char* result; char* pathname[] = { "/dev", getenv("TMPDIR"), "/tmp", getenv("HOME"), "." , "/"}; char filename[] = "zipl0000"; mode_t mode; unsigned int path; unsigned int retry; int rc; int fd; if (blockdev) mode = S_IFBLK | S_IRWXU; else mode = S_IFCHR | S_IRWXU; /* Try several locations as directory for the temporary device * node. */ for (path=0; path < ARRAY_SIZE(pathname); path++) { if (pathname[path] == NULL) continue; for (retry=0; retry < TEMP_DEV_MAX_RETRIES; retry++) { assert(retry < 10000); sprintf(filename, "zipl%04u", retry); result = misc_make_path(pathname[path], filename); if (result == NULL) return -1; rc = mknod(result, mode, dev); if (rc == 0) { /* Need this test to cover 'nodev'-mounted * filesystems. */ fd = open(result, O_RDWR); if (fd != -1) { close(fd); *devno = result; return 0; } remove(result); retry = TEMP_DEV_MAX_RETRIES; } else if (errno != EEXIST) retry = TEMP_DEV_MAX_RETRIES; free(result); } } error_text("Unable to create temporary device node"); error_reason(strerror(errno)); return -1; } /* Create a temporary device node for the device containing FILE. Upon * success, return zero and store a pointer to the name of the device node * file into DEVNO. Return non-zero otherwise. */ int misc_temp_dev_from_file(char* file, char** devno) { struct stat stats; if (stat(file, &stats)) { error_reason(strerror(errno)); return -1; } return misc_temp_dev(stats.st_dev, 1, devno); } /* Delete temporary device node DEVICE and free memory allocated for device * name. */ void misc_free_temp_dev(char* device) { if (remove(device)) { fprintf(stderr, "Warning: Could not remove " "temporary file %s: %s", device, strerror(errno)); } free(device); } /* Delete temporary bootmap file */ void misc_free_temp_file(char *filename) { if (remove(filename)) { fprintf(stderr, "Warning: Could not remove temporary file %s: %s", filename, strerror(errno)); } } /* Write COUNT bytes from memory at location DATA to the file identified by * file descriptor FD. Return 0 when all bytes were successfully written, * non-zero otherwise. */ int misc_write(int fd, const void* data, size_t count) { size_t written; ssize_t rc; for (written=0; written < count; written += rc) { rc = write(fd, VOID_ADD(data, written), count - written); if (rc == -1) { error_reason(strerror(errno)); error_text("Could not write to device"); return -1; } if (rc == 0) { error_reason("Write failed"); error_text("Could not write to device"); return -1; } } return 0; } int misc_seek(int fd, off_t off) { if (lseek(fd, off, SEEK_SET) == off) return 0; error_reason(strerror(errno)); error_text("Could not seek on device"); return -1; } int misc_pwrite(int fd, void *buf, size_t size, off_t off) { if (misc_seek(fd, off)) return -1; return misc_write(fd, buf, size); } int misc_check_writable_directory(const char* directory) { struct stat stats; if (stat(directory, &stats)) { error_reason(strerror(errno)); return -1; } if (!S_ISDIR(stats.st_mode)) { error_reason("Not a directory"); return -1; } if (access(directory, W_OK)) { error_reason(strerror(errno)); return -1; } return 0; } int misc_check_readable_file(const char* filename) { struct stat stats; if (stat(filename, &stats)) { error_reason(strerror(errno)); return -1; } if (!S_ISREG(stats.st_mode)) { error_reason("Not a regular file"); return -1; } if (access(filename, R_OK)) { error_reason(strerror(errno)); return -1; } return 0; } int misc_check_writable_device(const char* devno, int blockdev, int chardev) { struct stat stats; if (stat(devno, &stats)) { error_reason(strerror(errno)); return -1; } if (blockdev && chardev) { if (!(S_ISCHR(stats.st_mode) || S_ISBLK(stats.st_mode))) { error_reason("Not a device"); return -1; } } else if (blockdev) { if (!S_ISBLK(stats.st_mode)) { error_reason("Not a block device"); return -1; } } else if (chardev) { if (!S_ISCHR(stats.st_mode)) { error_reason("Not a character device"); return -1; } } if (access(devno, W_OK)) { error_reason(strerror(errno)); return -1; } return 0; } /* ASCII to EBCDIC conversion table. */ static unsigned char ascebc[256] = { 0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F, 0x16, 0x05, 0x15, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26, 0x18, 0x19, 0x3F, 0x27, 0x22, 0x1D, 0x1E, 0x1F, 0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D, 0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F, 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xBA, 0xE0, 0xBB, 0xB0, 0x6D, 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xC0, 0x4F, 0xD0, 0xA1, 0x07, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x59, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x90, 0x3F, 0x3F, 0x3F, 0x3F, 0xEA, 0x3F, 0xFF }; /* EBCDIC to ASCII conversion table. */ static unsigned char ebcasc[256] = { /* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */ 0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F, /* 0x08 -GE -SPS -RPT VT FF CR SO SI */ 0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, /* 0x10 DLE DC1 DC2 DC3 -RES -NL BS -POC */ 0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07, /* 0x18 CAN EM -UBS -CU1 -IFS -IGS -IRS -ITB */ 0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, /* 0x20 -DS -SOS FS -WUS -BYP LF ETB ESC */ 0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B, /* 0x28 -SA -SFE -SM -CSP -MFA ENQ ACK BEL */ 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07, /* 0x30 ---- ---- SYN -IR -PP -TRN -NBS EOT */ 0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04, /* 0x38 -SBS -IT -RFF -CU3 DC4 NAK ---- SUB */ 0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A, /* 0x40 SP RSP ? ---- */ 0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86, /* 0x48 . < ( + | */ 0x87, 0xA4, 0x9B, 0x2E, 0x3C, 0x28, 0x2B, 0x7C, /* 0x50 & ---- */ 0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07, /* 0x58 ? ! $ * ) ; */ 0x8D, 0xE1, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0xAA, /* 0x60 - / ---- ? ---- ---- ---- */ 0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F, /* 0x68 ---- , % _ > ? */ 0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, /* 0x70 --- ---- ---- ---- ---- ---- ---- */ 0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, /* 0x78 * ` : # @ ' = " */ 0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, /* 0x80 * a b c d e f g */ 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x88 h i ---- ---- ---- */ 0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1, /* 0x90 ? j k l m n o p */ 0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, /* 0x98 q r ---- ---- */ 0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07, /* 0xA0 ~ s t u v w x */ 0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, /* 0xA8 y z ---- ---- ---- ---- */ 0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x07, 0x07, 0x07, /* 0xB0 ^ ---- ? ---- */ 0x5E, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC, /* 0xB8 ---- [ ] ---- ---- ---- ---- */ 0xAB, 0x07, 0x5B, 0x5D, 0x07, 0x07, 0x07, 0x07, /* 0xC0 { A B C D E F G */ 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0xC8 H I ---- ? ---- */ 0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07, /* 0xD0 } J K L M N O P */ 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, /* 0xD8 Q R ---- ? */ 0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98, /* 0xE0 \ S T U V W X */ 0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, /* 0xE8 Y Z ---- ? ---- ---- ---- */ 0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07, /* 0xF0 0 1 2 3 4 5 6 7 */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0xF8 8 9 ---- ---- ? ---- ---- ---- */ 0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07 }; void misc_ebcdic_to_ascii(unsigned char *from, unsigned char *to) { for (; from != to; from++) *from = ebcasc[*from]; } void misc_ascii_to_ebcdic(unsigned char *from, unsigned char *to) { for (; from != to; from++) *from = ascebc[*from]; }