/** * Write a .spl text file to a file in preparation for pushing it * to the device. * * @param fd file descriptor to write to * @param p the text to output one line per string in the linked list * @see playlist_t_to_spl() */ static void write_from_spl_text_t(LIBMTP_mtpdevice_t *device, const int fd, text_t* p) { ssize_t ret; // write out BOM for utf16/ucs2 (byte order mark) ret = write(fd,"\xff\xfe",2); while(p != NULL) { char *const t = (char*) utf8_to_utf16(device, p->text); // note: 2 bytes per ucs2 character const size_t len = ucs2_strlen((uint16_t*)t)*sizeof(uint16_t); int i; IF_DEBUG() { printf("\nutf8=%s ",p->text); for(i=0;i<strlen(p->text);i++) printf("%02x ", p->text[i] & 0xff); printf("\n"); printf("ucs2="); for(i=0;i<ucs2_strlen((uint16_t*)t)*sizeof(uint16_t);i++) printf("%02x ", t[i] & 0xff); printf("\n"); } // write: utf8 -> utf16 ret += write(fd, t, len); // release the converted string free(t); // check for failures if(ret < 0) printf("write spl file failed: %s\n", strerror(errno)); else if(ret != len +2) printf("write spl file wrong number of bytes ret=%d len=%d '%s'\n", (int)ret, (int)len, p->text); // write carriage return, line feed in ucs2 ret = write(fd, "\r\0\n\0", 4); if(ret < 0) printf("write spl file failed: %s\n", strerror(errno)); else if(ret != 4) printf("failed to write the correct number of bytes '\\n'!\n"); // fake out count (first time through has two extra bytes from BOM) ret = 2; // advance to the next line p = p->next; } }
uint16_t *ucs2_strdup(const uint16_t *s) { size_t len = ucs2_strlen(s); uint16_t *ret = calloc(len, sizeof (*ret)); if (!ret) return NULL; memcpy(ret, s, len * sizeof (*ret)); return ret; }
/** * efivar_create_sysfs_entry - create a new entry in sysfs * @new_var: efivar entry to create * * Returns 0 on success, negative error code on failure */ static int efivar_create_sysfs_entry(struct efivar_entry *new_var) { int i, short_name_size; char *short_name; unsigned long variable_name_size; efi_char16_t *variable_name; int ret; variable_name = new_var->var.VariableName; variable_name_size = ucs2_strlen(variable_name) * sizeof(efi_char16_t); /* * Length of the variable bytes in ASCII, plus the '-' separator, * plus the GUID, plus trailing NUL */ short_name_size = variable_name_size / sizeof(efi_char16_t) + 1 + EFI_VARIABLE_GUID_LEN + 1; short_name = kzalloc(short_name_size, GFP_KERNEL); if (!short_name) return -ENOMEM; /* Convert Unicode to normal chars (assume top bits are 0), ala UTF-8 */ for (i=0; i < (int)(variable_name_size / sizeof(efi_char16_t)); i++) { short_name[i] = variable_name[i] & 0xFF; } /* This is ugly, but necessary to separate one vendor's private variables from another's. */ *(short_name + strlen(short_name)) = '-'; efi_guid_to_str(&new_var->var.VendorGuid, short_name + strlen(short_name)); new_var->kobj.kset = efivars_kset; ret = kobject_init_and_add(&new_var->kobj, &efivar_ktype, NULL, "%s", short_name); kfree(short_name); if (ret) return ret; kobject_uevent(&new_var->kobj, KOBJ_ADD); efivar_entry_add(new_var, &efivar_sysfs_list); return 0; }
/* * Clean up an entry with the same name */ static int efi_pstore_erase_func(struct efivar_entry *entry, void *data) { struct pstore_erase_data *ed = data; efi_guid_t vendor = LINUX_EFI_CRASH_GUID; efi_char16_t efi_name_old[DUMP_NAME_LEN]; efi_char16_t *efi_name = ed->name; unsigned long ucs2_len = ucs2_strlen(ed->name); char name_old[DUMP_NAME_LEN]; int i; if (efi_guidcmp(entry->var.VendorGuid, vendor)) return 0; if (ucs2_strncmp(entry->var.VariableName, efi_name, (size_t)ucs2_len)) { /* * Check if an old format, which doesn't support * holding multiple logs, remains. */ sprintf(name_old, "dump-type%u-%u-%lu", ed->type, (unsigned int)ed->id, ed->time.tv_sec); for (i = 0; i < DUMP_NAME_LEN; i++) efi_name_old[i] = name_old[i]; if (ucs2_strncmp(entry->var.VariableName, efi_name_old, ucs2_strlen(efi_name_old))) return 0; } /* found */ __efivar_entry_delete(entry); list_del(&entry->list); return 1; }
/** * Load a file descriptor into a string. * * @param device a pointer to the current device. * (needed for ucs2->utf8 charset conversion) * @param fd the file descriptor to load * @return text_t* a linked list of lines of text, id is left blank, NULL if nothing read in */ static text_t* read_into_spl_text_t(LIBMTP_mtpdevice_t *device, const int fd) { // set MAXREAD to match STRING_BUFFER_LENGTH in unicode.h conversion function const size_t MAXREAD = 1024*2; char t[MAXREAD]; // upto 3 bytes per utf8 character, 2 bytes per ucs2 character, // +1 for '\0' at end of string const size_t WSIZE = MAXREAD/2*3+1; char w[WSIZE]; char* it = t; // iterator on t char* iw = w; ssize_t rdcnt; off_t offcnt; text_t* head = NULL; text_t* tail = NULL; int eof = 0; // reset file descriptor (fd) to start of file offcnt = lseek(fd, 0, SEEK_SET); while(!eof) { // find the current offset in the file // to allow us to determine how many bytes we read if we hit the EOF // where returned rdcnt=0 from read() offcnt = lseek(fd, 0, SEEK_CUR); // read to refill buffer // (there might be data left from an incomplete last string in t, // hence start filling at it) it = t; // set ptr to start of buffer rdcnt = read(fd, it, sizeof(char)*MAXREAD); if(rdcnt < 0) LIBMTP_INFO("load_spl_fd read err %s\n", strerror(errno)); else if(rdcnt == 0) { // for EOF, fix rdcnt if(it-t == MAXREAD) LIBMTP_ERROR("error -- buffer too small to read in .spl playlist entry\n"); rdcnt = lseek(fd, 0, SEEK_CUR) - offcnt; eof = 1; } LIBMTP_PLST_DEBUG("read buff= {%dB new, %dB old/left-over}%s\n",(int)rdcnt, (int)(iw-w), eof?", EOF":""); // while more input bytes char* it_end = t + rdcnt; while(it < it_end) { // copy byte, unless EOL (then replace with end-of-string \0) if(*it == '\r' || *it == '\n') *iw = '\0'; else *iw = *it; it++; iw++; // EOL -- store it if( (iw-w) >= 2 && // we must have at least two bytes *(iw-1) == '\0' && *(iw-2) == '\0' && // 0x0000 is end-of-string // but it must be aligned such that we have an {odd,even} set of // bytes since we are expecting to consume bytes two-at-a-time !((iw-w)%2) ) { // drop empty lines // ... cast as a string of 2 byte characters if(ucs2_strlen((uint16_t*)w) == 0) { iw = w; continue; } // create a new node in the list if(head == NULL) { head = malloc(sizeof(text_t)); tail = head; } else { tail->next = malloc(sizeof(text_t)); tail = tail->next; } // fill in the data for the node // ... cast as a string of 2 byte characters tail->text = utf16_to_utf8(device, (uint16_t*) w); iw = w; // start again LIBMTP_PLST_DEBUG("line: %s\n", tail->text); } // prevent buffer overflow if(iw >= w + WSIZE) { // if we ever see this error its BAD: // we are dropping all the processed bytes for this line and // proceeding on as if everything is okay, probably losing a track // from the playlist LIBMTP_ERROR("ERROR %s:%u:%s(): buffer overflow! .spl line too long @ %zuB\n", __FILE__, __LINE__, __func__, WSIZE); iw = w; // reset buffer } } // if the last thing we did was save our line, then we finished working // on the input buffer and we can start fresh // otherwise we need to save our partial work, if we're not quiting (eof). // there is nothing special we need to do, to achieve this since the // partially completed string will sit in 'w' until we return to complete // the line } // set the next pointer at the end // if there is any list if(head != NULL) tail->next = NULL; // return the head of the list (NULL if no list) return head; }
static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor, unsigned long name_size, void *data) { struct super_block *sb = (struct super_block *)data; struct efivar_entry *entry; struct inode *inode = NULL; struct dentry *dentry, *root = sb->s_root; unsigned long size = 0; char *name; int len, i; int err = -ENOMEM; entry = kmalloc(sizeof(*entry), GFP_KERNEL); if (!entry) return err; memcpy(entry->var.VariableName, name16, name_size); memcpy(&(entry->var.VendorGuid), &vendor, sizeof(efi_guid_t)); len = ucs2_strlen(entry->var.VariableName); /* name, plus '-', plus GUID, plus NUL*/ name = kmalloc(len + 1 + EFI_VARIABLE_GUID_LEN + 1, GFP_KERNEL); if (!name) goto fail; for (i = 0; i < len; i++) name[i] = entry->var.VariableName[i] & 0xFF; name[len] = '-'; efi_guid_unparse(&entry->var.VendorGuid, name + len + 1); name[len + EFI_VARIABLE_GUID_LEN+1] = '\0'; inode = efivarfs_get_inode(sb, root->d_inode, S_IFREG | 0644, 0); if (!inode) goto fail_name; dentry = efivarfs_alloc_dentry(root, name); if (IS_ERR(dentry)) { err = PTR_ERR(dentry); goto fail_inode; } /* copied by the above to local storage in the dentry. */ kfree(name); efivar_entry_size(entry, &size); efivar_entry_add(entry, &efivarfs_list); mutex_lock(&inode->i_mutex); inode->i_private = entry; i_size_write(inode, size + sizeof(entry->var.Attributes)); mutex_unlock(&inode->i_mutex); d_add(dentry, inode); return 0; fail_inode: iput(inode); fail_name: kfree(name); fail: kfree(entry); return err; }